■ 引数を自由に

スクリプトをつくるとき、引数を渡しますよね。 オプションを指定して、動作にバリエーションを持たせたり、ファイル名を渡したり。 引数を自由に扱えれば、やりたいことに一歩近づきます。

■ 引数の参照

お約束です。 引数は変数 ARGV に「文字列」として納められています。 数値などの文字列以外で使用する場合には、それなりに変換するためのメソッドを呼びだすことが必要です。

number = ARGV.shift.to_i

● UNIX コマンド echo

UNIX 系のコマンド(シェルに内蔵されている場合もあります)の echo をつくってみます。 echo は、コマンドラインから入力した文字列をそのまま表示します。 また、オプションを指定することで出力の制御を行います。 ここで、引数をそのまま表示する場合や、コマンドへのオプションを渡す場合をみてみます。

コマンド echo の動きをみてみましょう。

$ echo 1 2 3 4 5 6 7
1 2 3 4 5 6 7
$ echo abc def ghi
abc def ghi
コマンドライン上で入力した文字列をそのまま出力しているのが分かります。

● Ruby で表示

Ruby の引数は ARGV という変数に配列(要素は文字列オブジェクト)として格納されています。 次のスクリプトは、その配列の内容を表示するものです。 配列のアクセスにはいろいろな方法があります。

for を使用した場合

#! /usr/local/bin/ruby

for arg in ARGV
  print arg, "\n"
end

exit
for を使用した方法は、配列の内容を一つ一つ参照のため(正確には違うのですが)にとりだしています。

shift を使用した場合

#! /usr/local/bin/ruby

while arg = ARGV.shift
  print arg, "\n"
end

exit
shift メソッドを使用した場合は、配列の「先頭の要素」をとりだし、とりだしたあとは 2 番目の要素以降が文字通り shift します。

join を使用した場合

#! /usr/local/bin/ruby

print ARGV.join("\n"), "\n"

exit
join は、配列の要素を連結(join)します。 これは、配列の要素を一つ一つ処理するというよりは、そのまま表示する際などによく利用する方法です。

イテレータ(繰り返し)を使用した場合

#! /usr/local/bin/ruby

ARGV.each {|arg|
  print arg, "\n"
}

exit
イテレータを使用した方法は、配列の内容を一つ一つ参照のため(正確には違うのですが)にとりだしています。

配列の内容を表示するだけの場合には、このようにいろいろな方法があります。 配列の一つ一つの要素を扱う場合に for やイテレータ(繰り返し)の場合は、とても便利です。 一般のプログラミング言語やスクリプト言語では、配列の要素がいくつあるかということを気にして処理しています。 配列の開始と終了を数値で示すことを行います。 Ruby の場合にはそのようなことが必要ありません。 for やイテレータを使うことで、その配列が持っている要素を間違いなく処理することができるのです。 これも Ruby の問題が起こりにくいいいところの一つです。

実行結果をみてみましょう。 結果はどれも同じになりますが次のようになります。

$ ruby echo.rb 1 2 3 4 5 abc def
1
2
3
4
5
abc
def

■ オプションの指定

一般的にコマンドなどを作成する場合、コマンドライン上での引数には、「オプション」や「ファイル名」を指定します。 「オプション」はそのコマンドの実行に対して指示を与えますし、「ファイル名」は処理対象のファイル名になります。 ここでは、引数として渡されたもののうち、オプションとして解釈する部分を扱います。 オプションの指定は「-英数字」という形式とします。

スクリプトとしては、次のように動作するものを作ります。

$ ruby echo5.rb -echo a b c
a b c 
$ ruby echo5.rb a b c      
a
b
c
オプション「-echo」を指定したときには、コマンド echo を指定したときのように一行に出力します。

引数処理を行う場合には shift を使用します。 これは、引数のうち「オプション」として解釈するものは、配列 ARGV から取り除きたいからです。 ただいきなり shift してしまうことはできないので、確認を行ってからにします。 このような処理はよく使用しますので、今後も使えるような書き方をします。 ちょっと分かりにくい書き方をしていますが、一度決まりきった方法で使えるようになれば、自分で応用するときに便利です。 こういう意味で、あまりスクリプトのコードとして入門者向けの優しいコード? を例とすることはありません。

#! /usr/local/bin/ruby

def usage
  STDERR.print "usage: #{$0} [-echo|-help] arg...\n"
end

separator = "\n"

while ARGV[0] =~ /^-/
  $_ = ARGV.shift
  if /^-echo/
    separator = " "
  elsif /^-help/
    usage
    exit
  else
    usage
    exit 1
  end
end

print ARGV.join(separator), "\n"

exit

ここでは新しくでてきたものが多いので、少し説明が長くなります。 最初に使い方として usage を定義しています。 これは、引数でオプションの指定が間違っているときに表示するためです。 または、オプションとして -help が使用された場合のためです。

次に表示の際の区切り文字を変数 separator に設定します。 ここで指定している「"\n"」は、デフォルトのときに使用される「改行」です。

while ... end では、オプションの処理を行います。 最初に while の条件として引数が入っている変数 ARGV の最初の先頭の文字が「-」か確認します。 「-」の場合にはオプションのためにオプションの処理を行います。 違う場合には、オプションはないものとみなして while 内の処理は行いません。

while 内では、次のようなことが実施されます。 引数の先頭の文字が「-」のため、オプションとして解釈します。 変数 $_ に変数 ARGV の最初の要素を shift でとりだし設定します。 ARGV の中から、「-」ではじまったオプションの要素は取り除かれます。 そして、文字列の比較などでデフォルトで使用される変数 $_ に値(オブジェクト)が入ります。 -echo が指定された場合には、変数 separator に「" "」(「スペース」)を設定します。 -help が指定された場合には、usage を呼びだし exit で終了します。 それ以外の場合には、usage を呼びだし exit 1 で終了します。 コマンドの終了ステータスとして、オプションエラーの意味で「exit 1」を実施しています。

最後の print では、残った引数 ARGV の内容を、区切り文字を変数 separator として join を行っています。 そして、大切なのが正常終了したという意味の exit です。 まあ、忘れても大丈夫なのですが。

最後に、こちらの方の処理がすっきりしているのですが、case ... end を使用した例です。 内容は同じものです。

#! /usr/local/bin/ruby

def usage
  STDERR.print "usage: #{$0} [-echo|-help] arg...\n"
end

separator = "\n"

while ARGV[0] =~ /^-/
  case ARGV.shift
  when '-echo'
    separator = " "
  when '-help'
    usage
    exit
  else
    usage
    exit 1
  end
end

print ARGV.join(separator), "\n"

exit


渡辺哲也(WATANABE Tetsuya): Tetsuya.WATANABE atmark nifty.com