Rubyワンライナー入門

先日仕事中、一瞬の隙も見逃さずに情報収集できるRubyワンライナーとスクリプトというネタエントリを書いたのだが、その際Rubyワンライナーをもう少しまともに理解したいと思ったので入門してみた。手元のRubyは1.9.2

はじめに

Rubyワンライナーは、rubyコマンドに色々なオプション(-eや-nなど)をつけて実行する。各オプションの網羅的な解説が見たければ、コマンドラインでは$ man rubyで閲覧できるし、webではるりまサーチで参照できる。ただ、これらは網羅的すぎるので、以下よく使うオプションとその周辺情報についてまとめた。

-e

一番基本的なオプション。スクリプトを実行する。(ちなみに他のオプションと組み合わせる時は必ず最後に記載する)

例: hogeと表示

$ ruby -e 'puts "hoge"'

-n

プログラム全体がwhile gets ... endというループで囲まれているように動作する。ちょっとややこしい。

例: hoge.txtとfuge.txtの内容を表示

$ ruby -ne 'puts $_' hoge.txt fuge.txt
理解するには

Kernel.#gets、ARGF、$_について理解する必要がある

Kernel.#gets

ARGFから一行読み込んで、それを返します。...読み込んだ文字列は組み込み変数 $_ にもセットされます。...
http://rurema.clear-code.com/1.9.2/method/Kernel/m/gets.html

つまり、以下は同義

$ ruby -ne 'puts $_' hoge.txt fuge.txt
$ ruby -e 'while gets; puts $_; end' hoge.txt fuge.txt

ざっくりいうと、以下のような流れになる

hoge.txtとfuge.txtを連結した仮想ファイル(ARGF)をセット

ARGFから1行読み込み、$_にセット。読み込めた場合は$_を出力してループ

ちなみに

上記では出力の部分をputs $_としているが、これはKernel.#printで代替できる。

Kernel.#print

引数を順に標準出力 $stdout に出力します。引数が与えられない時には変数 $_ の値を出力します。...
http://rurema.clear-code.com/1.9.2/method/Kernel/m/print.html

つまり、以下は全て同義

$ ruby -ne 'print' hoge.txt fuge.txt
$ ruby -ne 'puts $_' hoge.txt fuge.txt
$ ruby -e 'while gets; puts $_; end' hoge.txt fuge.txt

-p

nオプションとほぼ同じ。nオプションと同じくプログラム全体をwhile gets...endループするが、さらに各ループの最後にprintする。つまり、printが省略できるということ。なので、以下は全て同義。

$ ruby -pe '' hoge.txt fuge.txt
$ ruby -ne 'print' hoge.txt fuge.txt
$ ruby -ne 'puts $_' hoge.txt fuge.txt
$ ruby -e 'while gets; puts $_; end' hoge.txt fuge.txt

$_を破壊的メソッドで変換して出力するような場合に便利

例: hoge.txtとfuge.txtの内容を全て大文字にして表示

$ ruby -pe '$_.upcase!' hoge.txt fuge.txt

-a

オートスプリット。nかpと一緒に使う。各ループの先頭で$F = $_.splitをを実行する。

例: カレントディレクトリの情報を配列に変換して表示

$ ls -laF | ruby -ane 'p $F'
-F

$;(入力フィールドセパレータ。splitのデフォルト区切り文字)を設定

例: 環境変数PATHを:で分割して改行表示

$ echo $PATH | ruby -F: -ane 'puts $F'

-r

ライブラリ読み込み。

例: yahooのhtmlを取得

$ ruby -r open-uri -e 'open("http://www.yahoo.co.jp") {|f| puts f.read}'

ちなみに、複数のライブラリを読み込みたい場合は-r を複数書く。

例: facebookapiからマークザッカーバーグJSONを取得しハッシュに変換

$ ruby -r open-uri -r json -e 'open("http://graph.facebook.com/zuck") {|f| p JSON.parse(f.read)}'

BEGIN, END

オプションでは無いのだが、普段あまり使わないRubyの構文BEGINとEND(begin...endとは全くの別物)がワンライナーだと結構使われるようだ。

例: 全行が整数のnumber.txtの合計値を表示

$ ruby -ne 'BEGIN{$sum = 0}; $sum += $_.to_i; END{puts $sum}' number.txt 

BEGINとENDをつけないと、毎行読み込むごとに$sum = 0; と puts $sumが実行されてしまうので意図した結果にならない

BEGIN

初期化ルーチンを登録します。BEGINブロックで指定した文は当該ファイルのどの文が実行されるより前に実行されます。...
http://doc.ruby-lang.org/ja/1.9.2/doc/spec=2fcontrol.html#BEGIN

END

「後始末」ルーチンを登録します。END ブロックで指定した文はインタプリタが終了する時に実行されます。Ruby の終了時処理について詳しくは 終了処理を参照してください。...
http://doc.ruby-lang.org/ja/1.9.2/doc/spec=2fcontrol.html#END

書籍プログラミング言語Rubyによると、この構文はawkを受け継いだものらしい。以下のサイトが参考になりそう。

Ruby one-liners
AWKのススメ « クックパッド開発者ブログ

終わり

とりあえずRubyワンライナー入門した!