これはRuby Advent Calendar jp: 2011への参加エントリ、12月6日分です。5日は@zonu_exeさんのRuby vs Python! ~def vs def~でした。7日は@nari3さんのChipでWebページ上のコードを簡単に扱おうです。

さて、Ruby(MRI)にどのようなコマンドラインオプションがあるか、皆さんは把握しているでしょうか?

-e とか -r はポピュラーですね。今日は、確認(おさらい)の意味で、 それらも含めたMRIのコマンドラインオプションの中からいくつかピックアップして解説したいと思います。

なお、文中に実行例がある場合は、

ruby 2.0.0dev (2011-12-04 trunk 33940) [x86_64-darwin11.2.0]

を使用しています。

まずは知名度の高そうなものから:

-v / -w

いずれも、冗長モードに関する設定を行います。

グローバル変数 $VERBOSEtrue になり、いくつかのメソッドが冗長な出力を行うようになります。 自作メソッドでも $VERBOSE の値を参照することで、このメカニズムを利用することが出来ます。

-v だけの特徴として、

  • 起動後にバージョンナンバーを表示。(--version オプションの機能を内包)
  • 他に何も指定しなければインタプリタは標準入力を待たずに終了。

の2つがあります。

-w にはレベルの指定が可能な -W もあります。レベルには 0 / 1 / 2 を指定することが出来ます。 $VERBOSE の値はそれぞれ、 nilfalsetrue になります。

-w-W2 相当ですので、このオプションを使う機会はあまりなさそうです。 環境変数 RUBYOPT はコマンドラインオプションより 後で 評価されるので、RUBYOPT=-W0 を指定しておくと、 #!行に ruby -w と書かれていても黙って実行してくれる、みたいな使い方になるのでしょうか。

-r ライブラリ名

指定したライブラリを require します。ワンライナーではお世話になりますね。

標準ライブラリに un.rbubygems.rb という妙な名前のライブラリが存在するのをご存知でしょうか。 これらは -r の直後に書いて利用することを想定してこのような名前になっています。

un.rbfileutilsrequire し、いくつかのファイル操作コマンド(cp mv touch など)に相当するメソッドを定義しています。主として、mkmf ライブラリが生成する Makefile

ruby -run -e mv src dst

のようにしてファイル操作を行うために利用されます。オプションの r とライブラリ名の un がつながることで run(実行せよ)という命令のように見えますね。

ubygems.rb-rubygems という形で使われると、いかにも gem を使うぞ、というのがわかると思います。

-e RUBYのスクリプト

RUBYに実行させたいスクリプトを直接コマンドラインに指定します。 このオプションがある場合はコマンドラインからスクリプトとして実行するファイルを探すことはしません。

複数の -e オプションを与えると改行でつながったものと見なされるので、以下のようなことも出来ます。

ruby -e 'def now' -e 'puts Time.now' -e end -e now
2011-12-06 21:22:12 +0900

-n-p

スクリプトを暗黙のループで囲むオプションです。 sed や awk でよく見られる「1行読んで処理」タイプのワンライナーを書くときに便利です。

-n は1行読み込みのループを行います。この際、各行の内容は変数 $_ に格納されます。

-p-n の動作に加えて、ループの終了直前に $_ の内容を print します。

次あたりから若干マイナー度アップ気味で進行します。

-l

小文字のエルです。

行末処理を行います。

-n-p オプションが指定されている場合、入力行を自動的に chop! します。(chomp! じゃないのね…) スクリプトで $_ を処理する際は、改行が取り除かれた文字列に対して操作を行えばよいことになります。

また、 print が出力末尾に付ける文字列 $\$/ と同じにするので、入力時に chop! された改行文字は -p 時には再び補われることになります。

-a

-n-p と共に用いたときのみ機能し、入力行の自動分割を行うオートスプリットモードを有効にします。具体的には、

$F = $_.split

をループの先頭で実行します。

これはPerlの同名オプションに由来するオプションで、本来の用途は、実はAWKのシミュレートです。 PerlにはAWKスクリプトをPerlスクリプトに変換するa2pという名前のツールが付属して(sedスクリプト変換用のs2pも)おり、それが生成するスクリプトで利用されていたそうです。(ラクダ本初版) 現在のa2pは利用していないようですね。

とは言え、内容にもよりますが、AWKで用が済むならAWKを使ったほうがよいんじゃないでしょうか?

$ ls -l | awk '{sum+=$5} END{print sum}'                     
5826560

$ ls -l | perl -aln -e '$sum+=$F[4];' -e 'END{print $sum}'              
5826560

$ ls -l | ruby -aln -e 'BEGIN{$sum=0}' -e '$sum+=$F[4].to_i' -e'END{print $sum}' 
5826560

split は引数なしなので、 $; が区切りに使われます。この値は -F オプションに正規表現を指定して変更することが出来ます。

-i 拡張子

インプレイス編集モードを有効にします。

基本的には -p (まれに -n )オプション と併用して、入力を処理した内容で元ファイルを置き換えたいときに使用します。

編集結果を置き換えようと思って

$ ruby -pe 加工処理 file > file

などとしてしまうと、シェルがrubyプロセスの標準出力として file を開いてしまうので、まず file の内容が空になってしまいます。

これを避けるためには、たとえば

$ ruby -pe 加工処理 file > file.tmp && mv file.tmp file

のように一時ファイルに出力してからリネームして元ファイルを置き換える必要がありますが、 -i を使うと、その代わりに

$ ruby -i.bak -pe 加工処理 file

と書くことが出来ます。

拡張子を指定すると、元ファイルがその拡張子を付けた名前でバックアップされます。指定がない場合はバックアップされません。 実行してもエラーがなく期待通りの結果になると確信している場合以外は指定したほうがよいでしょう。

便利な機能なので他のコマンドでもサポートして欲しいところですが、対応していない(ほとんどの)場合は、 @knuさん作の inplaceコマンドが利用できるかもしれません。

あるいは、シェルの機能を駆使して

$ ( /bin/rm file && 加工処理 > file ) < file

というテクニックもあります。何をやっているか分かりますか?

  1. (...) < file 入力のリダイレクトを伴ったサブシェル起動です。 fileを読み込みモードでオープンしたファイルディスクリプタが、括弧内のコマンドを実行するサブシェルの標準入力として利用されます。
  2. /bin/rm file fileを削除してしまいます。 しかし、UNIXのファイルシステムにおけるファイル名は、あるデータ(i-node)に(ハード)リンクするものとしてディレクトリ(=名簿)に登録された名前にすぎません。 削除とは、ディレクトリ上のファイル名エントリを抹消し、データとの結びつきをなくす(unlink)ことを意味します。 オープン済みのファイルディスクリプタはファイル名とは関係がなくなっているので、ファイル名がなくなっても、データ読み込みは問題なく実行できます。 エイリアスで-iオプションを付けていると標準入力(=fileの内容)から読み込みを行ってしまうので、要注意。
  3. && ファイルの削除が成功したときだけ後続コマンドを実行します。
  4. 加工処理 > file 標準入力を加工して、元と同名のファイル名を使って書き込みます。 新規オープンなのでサブシェルが使っている標準入力の元ファイルとは無関係です。

このオプションもPerlからRubyに継承されたものですが、GNU sedにも採用されているのを知ってびっくりしました。

-s

小文字のsです。

簡単なオプション処理を実行します。

このオプションが指定されていると、スクリプトファイル名以後の引数に対応する名前のグローバル変数の値が true または直後に空白を置かずに=に続けて書いた値、に設定されます。

グローバル変数を使用することに目をつぶれば、これで十分な場合もあるかもしれません。

-x[ディレクトリ]

これはネットニュースやメールの本文に埋め込んだスクリプトを記事ごと実行可能にするオプションなのですが、 昨今は、スクリプトが必要な場合は、添付ファイルにしてしまうか、(gistなど)HTTPで収得できる場所に置く、 などとすることが多くて、使う機会は激減しているのではないでしょうか。

Rubyスクリプトを含んだ記事を ruby -x に続けて実行すると、

  • #! で始まり ruby を含む行までの部分を取り除く。
  • もしあれば、トークン __END__ の行とそれ以降を取り除く。

という処理を行った結果をスクリプトと見なして評価します。

ディレクトリが指定されている場合は、評価前に指定のディレクトリに実行します。

-S

大文字のSです。

-S を指定すると、環境変数 PATH からスクリプトを探します。 これも最近は見なくなったオプションだと思います。

(そこに至るパスを含まない)Rubyスクリプト名だけをコマンドラインから指定して実行するには、

  • 環境変数 PATH に含まれるディレクトリのいずれかに存在する。
  • sh-bang行にRubyインタプリタが指定されており、カーネルなどが解釈してRubyインタプリタにスクリプトを渡してくれる。
  • 実行ビットが立っている。

といった前提条件が必要ですが、そのうちとくに sh-bang 行のインタプリタ指定を解釈してくれない環境が世の中には存在するそうです。

そのような場合も PATH の通った個所においてありさえすれば、ruby -S に続けて指定することで実行が可能です。 また、この場合、sh-bang 行に書かれたインタプリタのパスは問題になりませんので、 sh-bang 行の書き換えは不要です。

さらに、スクリプトの先頭を

#!/bin/sh
exec ruby -x -S $0 "$@"
#! ruby
スクリプトの本体

のようにしておくと、

  1. スクリプトが /bin/sh に渡される。
  2. exec でこのスクリプト($0)自身(絶対パスとは限らない)がRubyインタプリタに引数とともに起動される。
  3. -s オプションがあるので、スクリプトが PATH から検索され、Rubyインタプリタに渡される。
  4. -x オプションがあるので、#! で始まり ruby を含む行までが読み飛ばされ、残りが最終的に評価される。

という形でRubyスクリプトをコマンドとして実行できます。

おわりに

以上、今回はRubyのコマンドラインオプションからいくつかを紹介してみました。

改めて拾い出してみると、コマンドラインからスクリプトを起動するときに便利そうな機能がいろいろありますね。一方、歴史的事情で存在していて、今となっては影が薄れているオプションや、似ているようで微妙に異なるオプション(-v, -w, -W および、今回紹介しなかった長いオプション —version, —verbose)もあり、少し整理の余地もありそうです。

  1. joscelin-peck reblogged this from sakuro
  2. sakuro posted this