ablog

不器用で落着きのない技術者のメモ

変態的な Perl ワンライナー

id:sugyan さんにトラックバックされた。

変態的に書きたいときはこんなカンジでw

perl -pe '$\=$_}{' hoge.log
最終行だけ表示するPerlワンライナー - すぎゃーんメモ

ちょっと何書いてるのか理解できないですね(汗

% perl -le 'print for(1..10)' > hoge.log
% cat hoge.log
1
2
3
4
5
6
7
8
9
10
% perl -pe '$\=$_}{' hoge.log
10

おぉ。ちゃんと動く。
全く理解できんので、スクリプト化してみる。

% perl -MO=Deparse -pe '$\=$_}{' hoge.log
LINE: while (defined($_ = <ARGV>)) {
    $\ = $_;
}
{
    ();
}
continue {
    die "-p destination: $!\n" unless print $_;
}
-e syntax OK

一行ずつ読込んだ内容($_)をレコードセパレータ($\)にして、その後はよくわからん。。。
指先一つPerl一行野郎でダウンさ〜(俺が)


Perl のスローガン

There's more than one way to do it

を肌で感じた。


追記:

力を全部読み終わるまで"$\ = $_;"が繰り返され上書きされ続け、最終的に最後の行だけが$\に入ります。
あとは最後に一度だけ"print $_"が呼ばれますが、この時点では$_はundefになっていて、代わりに$\(出力レコードセパレータなのでprint文の後に必ず出力される)だけが出力されることになります。
$\でない他の変数を使おうとすると
perl -pe '$a=$_}{$_=$a' hoge.log
というカンジでしょうか。TIMTOWTDIこわいですね ><

変態的な Perl ワンライナー - ablog

id:sugyan さんからコメントいただいちゃった。
ありがとうございます!

この時点では$_はundefになっていて、代わりに$\(出力レコードセパレータなのでprint文の後に必ず出力される)だけが出力されることになります。

なるほど。頭いいっすね!

あとは最後に一度だけ"print $_"が呼ばれますが、この時点では$_はundefになっていて、

これがなんでかわからない。

そもそも

perl -pe '$\=$_}{' hoge.log

の「}{」がなんなのかわからない。
スクリプト化すると、

{
	();
}

となっている。なんじゃこれ?

プログラミングPerl〈VOLUME1〉
P. 143

4.5 裸のブロック
BLOCK単体(ラベルはあってもなくてもよい)は、意味的には、1回だけ実行されるループと等価である。

ってことは、乱暴に言うと

$i=0;
while ($i<1) {
	$i=1;
}

みたいな感じか。

あと、continue { ... } ってなんだろ?

ミニマルPerl ―Unix/LinuxユーザのためのPerl習得法
P.348

ループの次の繰り返しの前に実行すべきコードを中に含むcontinueブロックを付けることができます。

ふむふむ。

ループ {();} をはさんで、$_ をクリアしてから print $_; するから、レコードセパレータのみ表示されるってことか。


厳密には違うところがあると思うけど、自分のようなサルでわかるコードにしてみた。

% cat hoge1.pl
open (IN, "< $ARGV[0]");
while (<IN>) {
	$\ = $_;
}
continue {
	print $_;
}
close(IN);
% perl hoge1.pl hoge.log
1
1
...
10
10

となっちゃうからループをはさんでやると、

% cat hoge2.pl
open (IN, "< $ARGV[0]");
while (<IN>) {
	$\ = $_;
}
$i=0;
while ($i<1) {
	$i=1;
}
continue {
	print $_;
}

close(IN);
% perl hoge2.pl hoge.log
10

最終行のみ表示される。


}{ と書いたらなんで、{();} になるのかについてはわからなかった><


追記(2009/08/28):

UNIXのtailコマンドじゃダメ?
tail -n 1 hoge.log

最終行だけ表示するPerlワンライナー - すぎゃーんメモ

最終行を表示するだけなら tail で良いのですが、もう少し複雑なことをしたかったので Perl を使いました。具体的に以下のようなことをしました。

% perl -wne 'if($.==2){$a=substr($_,0,12)}elsif(eof){$b=substr($_,0,12);print substr($ARGV,3,7) . " $a $b\n";close ARGV}' hoge<1-500>.log