camel

まあ、ruby のコマンドラインオプションって、Perl由来ですから。

Rubyでワンライナーを書く方法のまとめ
まぁ、Perlもあるしあんまり需要が無いのかも知れませんが。

というわけで、Rubyistにも役立つPerlのワンライナー入門です。

基本中の基本

コマンドとしてのperlは、スイッチがない場合、引数はスクリプト名として扱われます。

% cat hello.pl
print "Hello, world!\n";
% perl hello.pl
Hello, world!
%

コマンドライン中の文字列をスクリプトとして解釈させるには、-eを使います。

% perl -e 'print "Hello, World!\n"'
Hello, world!
%

ちなみに、perlとコマンド名だけで起動すると、標準入力をスクリプトとして返します。

% perl
print 1+1, "\n";
[ctrl-D]
2
%

なお、ここまでの挙動はrubyも同じです。

強制改行

perlには、長らくrubyのputs相当が不在でした。その代わり、-lをコマンドライン指定するとprintputs代わりになります。

% perl -e 'print "Hello, World!"'
Hello, World! %
% perl -le 'print "Hello, World!"'
Hello, World!
%

実は-lにはprintを強制改行させるだけではなく、行を強制chompする機能もあるのですが、これに関しては後述します。

5.10.0 以降では、sayが加わったので、それを使うことも出来ます。ただし、これをワンライナーから利用するには-eではなく-Eを指定する必要があります。

% perl -e 'say "Hello, World!"'
String found where operator expected at -e line 1, near "say "Hello, World!""
        (Do you need to predeclare say?)
syntax error at -e line 1, near "say "Hello, World!""
Execution of -e aborted due to compilation errors.
% perl -E 'say "Hello, World!"'
Hello, World!

-MModuleでモジュールを利用

コマンドラインでモジュールを使うには、-e 'use Module; ...'としてもよいのですが、-MModuleとすることも出来ます。こちらの方が一般的です。

たとえば、以下のワンライナーはhttp://www.dan.co.jp/の内容を標準出力に書き出します。(もちろんLWPがインストールされていれば)。

% perl -MLWP::Simple -le 'print get shift' http://www.dan.co.jp/

-MO=Deparseでスクリプト化

ここで、-MO=Deparseも覚えておきましょう。perlにはさまざまなコマンドラインスイッチがあるので、複雑なものを利用した場合、実際に実行されるコードがどうなっているのかわかりにくくなったりしますが、これを使えば「もしコマンドラインではなく、スクリプトだったらどうなるか」を確認することできます。

% perl -MO=Deparse -le 'print "Hello, World!"' 
BEGIN { $/ = "\n"; $\ = "\n"; }
print 'Hello, World!';
-e syntax OK
%

-nで一行づつ処理、-pでそれをprint

ワンライナーの用途で最も多いのが、テキストを一行ずつ処理するというもの。この時使うと便利なのが-n-p。通常どちらも-lと組み合わせて使います。

例:行番号を表示
% perl -nle 'print "$.:$_"' script.pl
1:while(<>){
2:    print "$.:$_"
3:}

findと組み合わせてperlを使う時にも、これが大活躍します。詳しくは

もあわせてご覧ください。なお、このオプションはrubyにも受け継がれています。

-iでファイルをまとめて書き換え

ここまでの例では、結果は全て標準出力でしたが、-iを指定すると、ファイルの出力は入力元となったファイル自身になります。たとえば

% perl -i -ple 's/\r\n/\n/g' *.txt

で、DOS式の改行(\r\n)をすべてUnix式(\n)に変更できます。

元のファイルを上書きせず残しておきたい場合は、-i.extとします。

% perl -i.bak -ple 's/\r\n/\n/g' *.txt

とすると、元のファイルは.txt.bakとして残されます。ちなみに-i.bakの後、

% find . -name \*\.bak | perl -nle '$o=$_;s/\.bak$//;rename $o,$_'

とすれば、「アンドゥ」したのと同じことになります。

-aでawkっぽく

-nまたは-pさらに-aを加えると、awkっぽい処理も可能になります。ただし、$1, $2といった、awkではフィールド変数にあたる変数はperlではregexpで使われているので、フィールドは@Fという配列に格納されます。

例: PIDを列挙する
% ps aux | perl -anle 'print $F[1]'

このオプションも、rubyに移植されています(rubyの場合は配列オブジェクト$F)。

Unicode使うなら-CIO

Perl 5.8.1から加わったオプションです。Perl 5.8からは、UTF-8を単なるバイト列ではなく文字列としても扱うようになりましたが、下位互換性を保つため、STDINSTDOUTといった標準ファイルハンドルは、何もしなければバイト列扱いで、これを切り替えるにはbinmode STDIN, ':utf8';などとしなければなりません。スクリプトならたかだか一行ですが、ワンライナーにはそれでもかったるい。-Cはその悩みを解消します。ちなみにIは入力を、Oは出力をそれぞれ切り替えます。

口で言うとまだるっこしいのですが、以下を見ればその意味がおわかりいただけるかと。

% perl -MHTML::Entities -ple '$_=encode_entities($_)'
Dan Kogai (小飼弾)
Dan Kogai (小é£&frac14;å&frac14;&frac34;)
% perl -CIO -MHTML::Entities -ple '$_=encode_entities($_)'
Dan Kogai (小飼弾)
Dan Kogai (&#x5C0F;&#x98FC;&#x5F3E;)

最近の端末エミュレーターは、OS X標準装備のTerminal.appも含めてUTF-8にはじめから対応しているものが多いので、日本語処理もワンライナーでやりやすくなりました。ぜひ活用してみましょう。

まとめ

このようにワンライナーに強いのがperlの美点の一つです。私のshellのhistoryには、こうして出来たone-linerがどっさりたまっています。

% echo $SHELL 
/bin/tcsh
% echo histdup
erase
% echo $savehist 
1024
% history | perl -nle '/perl/ and $p++;END{print $p}'
257

なんと1/4がワンライナーでした。みなさんも是非。

Dan the One-Liner Monger

See Also: