Ruby のグローバル変数をそれぞれ調べてみた


Ruby のクリプティックなグローバル変数をそれぞれ調べてみました.

Ruby のグローバル変数って一番広いスコープで使えるので, どこでも使えて便利ですが, 変数名が少々難解なので, どの変数名がどういう意味だったかみたいなことを忘れがちです.

なので自分への備忘録という意味も含めて, それぞれのグローバル変数を実際のコードで試してみました.

グローバル変数には使う人が覚えやすいように, エイリアスというものが名付けられたものもあるので, それも一緒に紹介したいと思います.

$!

Ruby の raise によって引き起こされた例外情報のメッセージです.

begin
  puts foo
rescue NameError => e
  puts $!        # undefined local variable or method `foo' for main:Object
  puts $!.class  # NameError
  puts $! == e   # true
end

$!rescue NameError => ee と同じオブジェクトになります.

$@

投げられた最後の例外のバックトレースの配列です.

example.rb

begin
  puts foo
rescue NameError => e
  puts $@                  # example.rb:2:in `<main>'
  puts $@ == $!.backtrace  # true
end

$@$!.backtrace と同じオブジェクトになります.

$&

正規表現で最後にマッチした文字列です.

'Hello World' =~ /\w+ \w+/
$&        # => "Hello World"
$&.class  # => String

$`

正規表現で最後にマッチした左側の文字列です.

'Hello World' =~ / /
$`  # => "Hello"

$'

正規表現で最後にマッチして右側の文字列です.

'Hello World' =~ / /
$'  # => "World"

$+

正規表現で最後にマッチした一番最後のグループです.

'Hello World' =~ /((\w+) (\w+))/
$+  # => "World"

$1

正規表現で最後にマッチした N 番目のグループです.

'Hello World' =~ /(\w+) (\w+)/
$1  # => "Hello"
$2  # => "World"

$~

正規表現で最後にマッチした MatchData です.

'Hello World' =~ /(\w+) (\w+)/
$~        # => "Hello World"
$~[1]     # => "Hello"
$~[2]     # => "World"
$~.class  # => MatchData

$/

ファイルを読み込む時の区切り文字です.

デフォルトは "\n" です.

$/  # => "\n"

example1.txt

This is the first line.
This is the second line.

example2.txt

This    sentence    is  separated   by  a   tab.
def puts_lines(file)
  File.open(file) do |f|
    f.readlines do |line|
      puts line
    end
  end
end

puts_lines('example1.txt')
# This is the first line.
# This is the second line.

$/ = "\t"
puts_lines('example2.txt')
# This
# sentence
# is
# separated
# by
# a
# tab.

example1.txtexample2.txt という二つのテキストファイルを用意して, 第一引数に指定されたファイルパスのファイルの内容を出力する関数 puts_lines を定義し, それぞれのファイルで呼び出しています.

最初の example1.txt$/ がデフォルトの "\n" のままなので, 一行目, 二行目と通常通りに表示されました.

一方の example2.txt$/ の値を "\t" に割り当てたので, 一行にも関わらず, その一行内のタブで区切られた 7 つの単語がそれぞれ表示されました.

$\

print 関数などで出力する時の行末に付けられる文字です.

デフォルトは nil なので, つまりは何も付けられないということです:

$\  # => nil

print 'Hello'
print 'World'
# HelloWorld

もし $\"\n" とすると puts と同じように print でも行末に "\n" が付けられます:

$\ = "\n"

print 'Hello'
print 'World'
# Hello
# World

$,

printjoin でのオブジェクト間の文字として使われます.

デフォルトは nil です.

$,                       # => nil

print 'Hello', 'World'   # HelloWorld

['Hello', 'World'].join  # => "HelloWorld"

$, = ' '

print 'Hello', 'World'   # Hello World

['Hello', 'World'].join  # => "Hello World"

$;

split で文字列を分割する際のパターンに使用されます.

デフォルトは nil です.

もし $;nil の場合, split" " で分割します.

$;                   # => nil

'Hello World'.split  # => ["Hello", "World"]

$; = "\n"
"Hello\nWorld".split # => ["Hello", "World"]

$.

最後に読み込まれたファイルの現在の行数です.

次のファイルを用意して:

example.txt

This is the first line.
This is the second line.

次のコードを実行すると:

def puts_line_with_line_number(f)
  line = f.readline
  puts "#{$.}: #{line}"
end

File.open('example.txt') do |f|
  puts_line_with_line_number(f)
  puts_line_with_line_number(f)
end

このように出力されます:

1: This is the first line.
2: This is the second line.

$<

コマンドラインで与えられたファイルの仮想連結ファイルです.

$< のエイリアスとして, グローバル定数 ARGF を代わりに使うこともできます.

また $<.filename のエイリアスとして, グローバル変数 $FILENAME を代わりに使うこともできます.

次のファイルを用意して:

example.txt

This is the first line.
This is the second line.

次の Ruby スクリプトを用意して:

example.rb

puts $<.filename
puts $<.readline
puts $<.readline

次のコマンドを入力すると:

ruby example.rb < example.txt

次のように表示されます:

exmaple.rb
This is the first line.
This is the second line.

$>

printprintf がデフォルトで出力する場所です.

デフォルトで $stdout が設定されています.

なので次の空ファイルを用意して:

example.txt

次の Ruby スクリプトを用意して:

example.rb

File.open('example.txt', 'w') do |f|
  $> = f
  puts 'Hello World'
end

次のコマンドを実行すると:

ruby example.rb

example.txt は次のように書き換えられます:

Hello World

$_

getsreadline での最後の行です.

次の Ruby スクスプトファイルを用意して:

example.rb

print 'Please input something: '
gets
puts "Your input: #{$_}"

その example.rb を実行すると, 次のようになります:

$ ruby example.rb
Please input something: Hello World
Your input: Hello World

getsHello World と入力した内容が $_ に入っているのがわかります.

$0

現在実行しているスクリプト名です.

次の Ruby スクリプトファイルを用意して:

example.rb

puts $0

その example.rb を実行すると, 次のようになります:

$ ruby example.rb
example.rb

$*

スクリプトのパスを除いたコマンドライン引数です.

$* のエイリアスとして, グローバル定数 ARGV を代わりに使うこともできます.

次の Ruby スクリプトファイルを用意して:

example.rb

puts $*.inspect

次のコマンドを実行すると, このようになります:

$ ruby example.rb a b c
["a", "b", "c"]

example.rb というスクリプトのパスが除かれた a, b, c という 3 つの引数のみ $* に入っているのがわかります.

$$

スクリプトを実行している Ruby のプロセス番号です.

次の Ruby スクリプトファイルを用意して:

example.rb

puts $$

次のコマンドを実行すると, このようになります:

$ ruby example.rb
34890

34890 は僕が実行した時の Ruby のプロセス番号です.

実際の番号はランダムになります.

$?

最後に実行した子プロセスの状態です.

次の Ruby スクリプトファイルを用意して:

example.rb

puts 'Correct command:'
puts `echo 'Hello World' | sed 's/ /+/'`
puts $?
puts

puts 'Wrong command:'
puts `echo 'Hello World' | sed --wrong-option 's/ /+/' 2> /dev/null`
puts $?

次のように実行すると, このようになります:

$ ruby example.rb
Correct command:
Hello+World
pid 42661 exit 0

Wrong command:

pid 42664 exit 1

最初の echo 'Hello World' | sed 's/ /+/' は正しいコマンドなので exit 0 となりましたが, その次の echo 'Hello World' | sed --wrong-option 's/ /+/' 2> /dev/null は誤ったオプション --wrong-option を指定しているのでエラーとなり exit 1 となりました.

ちなみに 2> /dev/null というのは, stderr に出力されるエラーメッセージを表示しないようにしています.

$:

loadrequire によって探索されるパスの配列です.

$: のエイリアスとして, グローバル変数 $LOAD_PATH を代わりに使うこともできます.

puts $:
# /usr/local/Cellar/rbenv/1.1.1/rbenv.d/exec/gem-rehash
# /usr/local/Cellar/rbenv/1.1.1/rbenv.d/exec/gem-rehash
# /usr/local/Cellar/rbenv/1.1.1/rbenv.d/exec/gem-rehash
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/awesome_print-1.8.0/lib
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/site_ruby/2.5.0
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/site_ruby/2.5.0/x86_64-darwin17
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/site_ruby
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/vendor_ruby/2.5.0
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/vendor_ruby/2.5.0/x86_64-darwin17
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/vendor_ruby
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/x86_64-darwin17

僕は rbenv を使用して Ruby を使っているので, このようなロードパスが表示されるのでしょうね.

$"

require によって読み込まれたモジュール名を含んだ配列です.

$" のエイリアスとして, グローバル変数 $LOADED_FEATURES を代わりに使うこともできます.

puts $"
# enumerator.so
# thread.rb
# rational.so
# complex.so
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/x86_64-darwin17/enc/encdb.bundle
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/x86_64-darwin17/enc/trans/transdb.bundle
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/x86_64-darwin17/rbconfig.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/compatibility.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/defaults.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/deprecate.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/errors.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/version.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/requirement.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/platform.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/basic_specification.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/stub_specification.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/util/list.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/x86_64-darwin17/stringio.bundle
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/uri/rfc2396_parser.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/uri/rfc3986_parser.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/uri/common.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/uri/generic.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/uri/ftp.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/uri/http.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/uri/https.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/uri/ldap.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/uri/ldaps.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/uri/mailto.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/uri.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/specification.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/exceptions.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/dependency.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/core_ext/kernel_gem.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/monitor.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/path_support.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/version.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/core_ext/name_error.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/levenshtein.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/jaro_winkler.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checker.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/2.5.0/delegate.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/name_error_checkers.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/method_name_checker.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/key_error_checker.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/null_checker.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/formatters/plain_formatter.rb
# /Users/yu8mada/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean.rb

こうしてみると随分デフォルトで色々なモジュールが読み込まれているというのがわかります.

まとめ

Ruby のグローバル変数って, 変数名が難解ではじめ見たときは “???” という感じだったのですが, あくまで変数名が難解というだけですので, 一度それらの変数名と意味を覚えてしまったら二文字で入力できるので, 便利この上ありません.

僕はこの難解さが, いかにもプログラミングをしているという気持ちにさせてくれますので, 気に入っておりますw.

ただ $: などのエイリアスがあるものは $LOAD_PATH などのエイリアスを使ったほうがよりわかりやすくて良いという意見もあるので, そうなのかもしれません.

何かのプロジェクトでチームで開発している場合は, そのチームのどのようにコードを書くかというルールに従うべきなのでしょう.

でも一人だけで開発している分には, そのような難解なグローバル変数名をそのまま使っても全然良いのではと個人的には思っております.

それが Ruby らしさの一つでもあるような気がしますので.

あくまで今回紹介させていただきました Ruby のグローバル変数は全てというわけではありません. 網羅的な情報をお知りになりたい方は次の URL より参照していただければと思います.

https://ruby-doc.org/core-2.5.1/doc/globals_rdoc.html