綾小路龍之介の素人思考

[perl] Linux 上の Perl で書いた 1 行スクリプト

ここまでは Activeperl を対象に 1 行スクリプトを書いてきたが、これからは Linux で動く Perl もしくは Cygwin 上で動く Perl で 1 行スクリプトを書こうと思う。

[linux] 総和の方向で結果が異なるのは仕方ない。

その理由はやはり丸め込み誤差だろうな誤差を小さくするためにはどうすればいいのだろう。

$ perl -wle 'for($i=1;$i<=10**8;$i++){$S+=$i**-1;} print join qq#\t#,($i,$S,(times)[0]);'
100000001       18.9978964138477        103.31
$ perl -wle 'for($i=10**8;$i>=1;$i--){$S+=$i**-1;} print join qq#\t#,($i,$S,(times)[0]);'
0       18.9978964138532        157.2
$ 

判定と 2 重 for でどっちが早いのか

これは宿題

たとえば、for ブロックの中で、10 回に 1 回出力したいとき、ブロックの最後に if( $i % 10 == 0){print;} とかすると思う。これはよく紹介されている手法だが、スピード的にはどうかと思った。だって、100 回ループさせたら、100 回チェックが行われるわけで、そのうちの 90 回は出力されない。もしループ回数が多くなって、より出力の回数が減ったら、さらに無駄な判断を繰り替えすことになる。もったいなくないか。

$  perl -wle 'for($i=1;$i<=10**7;$i++){$S+=$i**-1; if($i%10**6==0){ print join qq#\t#,($i,$S,(times)[0]);} }'
1000000 14.3927267228648        1.25
2000000 15.0858736534248        2.49
3000000 15.4913386781997        3.73
4000000 15.7790207089843        4.98
5000000 16.0021642352982        6.22
6000000 16.1844857754256        7.47
7000000 16.338636443348 8.71
8000000 16.4721678270439        9.96
9000000 16.5899508557549        11.2
10000000        16.6953113658567        12.46
$ perl -wle 'for($j=0;$j<10;$j++){ $s=1+$j*10**6; $e=($j+1)*10**6; for($i=$s;$i<=$e;$i++){$S+=$i**-1;} print join qq#\t#,($i,$S,(times)[0]);}'
1000001 14.3927267228648        0.96
2000001 15.0858736534248        1.92
3000001 15.4913386781997        2.88
4000001 15.7790207089843        3.84
5000001 16.0021642352982        4.81
6000001 16.1844857754256        5.77
7000001 16.338636443348 6.73
8000001 16.4721678270439        7.68
9000001 16.5899508557549        8.64
10000001        16.6953113658567        9.6
$ cat test.pl
#!/usr/bin/perl
$a = 100;
$b = 10**6;
$ab= $a*$b;
for($k=0;$k<1000;$k++){
        $c = (times)[0];
        for($i=0; $i<$a; $i++){
                print ((times)[0]-$c);
                print "\t";
                for($j=0; $j<$b; $j++){
                        #$a_j = $i * $b + $j;
                        $a_j++;
                        #print "$a_j ";
                }
        }
        $c = (times)[0];
        for($i=0; $i<$ab; $i++){
                if(0==$i % $b){
                        print ((times)[0]-$c);
                        print "\t";
                }
        }
        print "\n";
}
$ nohup nice perl test.pl > test.dat &
$ cat test.plt
#!/usr/bin/gnuplot
!perl -alne 'map{$S[$_]+=@F[$_]}(0..$#F);$i++; END{print join"\n",map{$_/$i}@S;}' test.dat > test2.dat
set xlabel "output setp [times]";
set ylabel "CPU time [sec]";
set grid
set terminal svg
set output "test.svg"
set key left top
plot    "<perl -ne 'print if 1 .. 100' test2.dat" title "if()",\
        "<perl -ne 'print if 101 .. 200' test2.dat" title "double for()"
CPU 時間とステップ数

改行コードの変換 (nkf の代替)

nkf は改行コードと文字コードの変換によく使う。unix、mac、windows それぞれのシステムにおける改行コードは決まっているので、それらにおける改行コードが具体的に何か知らなくても下のような感じで使えば、改行コードの変換が出来る。

$ nkf --unix hoge.c > tmp.c
$ nkf --mac hoge.c > tmp.c
$ nkf --windows hoge.c > tmp.c

このようにすることで、改行コードを unix や mac や windows のものにして hoge.c の内容を tmp.c にリダイレクトできる。これを perl で代替するには (それぞれのシステムの改行コードを具体的に知らなければならないが) 下のようになる。

$ perl -pe 's/\x0D\x0A|\x0D|\x0A/\x0A/g;' hoge.c > tmp.c
$ perl -pe 's/\x0D\x0A|\x0D|\x0A/\x0D/g;' hoge.c > tmp.c
$ perl -pe 's/\x0D\x0A|\x0D|\x0A/\x0D\x0A/g;' hoge.c > tmp.c

もらったファイルの改行コードで悩むことって、*nix 使っている限りあまりないのだけれど、自分のシステムの改行コードに合わせるには下のようにする。置換の変換先を \n つまりシステム標準の改行コードにするということだ。

$ perl -pe 's/\x0D\x0A|\x0D|\x0A/\n/g;' hoge.c > tmp.c

cpan にモジュールとかありそうなものだけど、まぁ手書きでも出来ないこともない。重要なのは置換元の並び順は先頭から評価されるため、置換前の改行コードの並び順が \x0D\x0A|\x0D|\x0A の順番でないとうまく動作しないということ。-p で print; 付きの各行読み込み。s// で置換、Win か Mac か Unix の改行コードを Unix の改行コードに。読み込みは hoge.c、標準出力をリダイレクトして tmp.c に。リダイレクト先と入力を別にしておくことに注意。リダイレクト先を hoge.c にしてしまうと hoge.c の内容がクリアされてしまう。

次のような 3 つの入力を置換することを考える。

in:
This is a pen.\x0D\x0A
This is a pen.\x0D
This is a pen.\x0A

このとき、それぞれの変換は次のように進んでいると思われる。変換の評価ポイントが一文づつずれていく。評価ポイント上の文字が変換前の文字列とマッチするか評価する。変換前の文字列のプライオリティは \x0D\x0A が最高なので、評価ポイント上の文字 + 次の 1 文字でまず評価され、この文字列が \x0D\x0A でない場合、評価ポイント上の文字を評価する。

This is a pen.\x0D\x0A の場合
|------------*####
This is a pen.\x0D\x0A  -->  This is a pen.\x0D\x0A
|------------*
This is a pen.\x0D\x0A  -->  This is a pen.\x0D\x0A
|-------------****####
This is a pen.\x0D\x0A  -->  This is a pen.\x0A
|-----------------*#
This is a pen.\x0A      -->  This is a pen.\x0A
This is a pen.\x0D の場合
|------------*####
This is a pen.\x0D      -->  This is a pen.\x0D
|------------*
This is a pen.\x0D      -->  This is a pen.\x0D
|-------------****#
This is a pen.\x0D      -->  This is a pen.\x0D
|-------------****
This is a pen.\x0D      -->  This is a pen.\x0A
|-----------------*#
This is a pen.\x0A      -->  This is a pen.\x0A
This is a pen.\x0A の場合
|------------*####
This is a pen.\x0A      -->  This is a pen.\x0A
|------------*
This is a pen.\x0A      -->  This is a pen.\x0A
|-------------****#
This is a pen.\x0A      -->  This is a pen.\x0A
|-------------****
This is a pen.\x0A      -->  This is a pen.\x0A
|-----------------*#
This is a pen.\x0A      -->  This is a pen.\x0A

こんな感じだ。最初の評価が 2 文字ということがポイントだと思う。\x0D の 1 文字の評価のプライオリティが最高だと、\x0D\x0A がまず \x0A\x0A に変換されてしまい、このときの変換ポイントが次の評価で次に進み、期待通りの結果が得られない。

s/\x0D|\x0D\x0A|\x0A/\x0A/g; の場合
out:
This is a pen.\x0A\x0A
This is a pen.\x0A
This is a pen.\x0A

今回のネタは、nkf が導入されていないシステムで改行コードを変更しようとしたことが発端である。そもそも nkf は日本生まれなので、今回作業に躓いたサーバでは管理者が日本人でなかったためか nkf が導入されていなかった。でも、海外の人はどうやって改行コードの変換を行っているのだろう、ところ変われば品変わるのかな。

自分しか編集しないファイルの場合は改行コードを気にすることはほとんどないが、だれかとやり取りする場合は結構気にする。全然改行のないテキストをメモ帳で見て、ああこの人はどうやってテキストを編集しているのだろうかと本気で悩んだことがあった。Web を作ったときにアップロードの前後でファイルサイズが異なっていることがかなり気になったことがあった。そんなこともあったということで。

文字コードの変更

結論的には nkf 入れるのがもっとも素直でよいと思う。しかし nkf が無いような場合、iconv で文字コードを変換するのだけれど、iconv には nkf -guess のような文字コード判定をしてくれるような機能が付いてはいない。そのため、たくさんの文書を一気に変更する場合だと使いにくい。nkf 無でも perl 位はあるだろう、ということで perl の 1 行スクリプトを書いてみた。まずは適当に作ったファイル hoge.txt を euc-jp に変換してみる。

$ perl -MEncode -MEncode::Guess -pe '$_=encode("euc-jp",decode("Guess",$_));' hoge.txt > hoge_euc-jp.txt
$ nkf -guess hoge_euc-jp.txt
EUC-JP

たしかに euc-jp に変換されているが、この 1 行スクリプトは入力ファイルが euc-jp の場合にはうまく変換されない。

$ nkf -guess hoge.txt
UTF-8
$ perl -MEncode -MEncode::Guess -pe '$_=encode("utf8",decode("Guess",$_));' hoge_euc-jp.txt > hoge_euc-jp_utf8.txt
No appropriate encodings found! at /usr/local/lib/perl/5.8.8/Encode.pm line 170

その理由は Encode:Guess が "By default, it checks only ascii, utf8 and UTF-16/32 with BOM." というルールになっているからだ。つまり、入力ファイルの文字コードが euc-jp の場合を想定して "Guess" が行われないため、文字コード判定に失敗してしまうわけだ。これを回避するためには Encode::Guess の set_suspect メソッドで入力ファイルの文字コードとして可能性のあるものを挙げておく。

$ perl -MEncode -MEncode::Guess -pe 'BEGIN{Encode::Guess->set_suspects(qw/euc-jp/);} $_=encode("utf8",decode("Guess",$_));' hoge_euc-jp.txt > hoge_euc-jp_utf8.txt
$ nkf -guess hoge_euc-jp_utf8.txt
UTF-8

これで適切な変換が行われた。当然ながらこのままでは shiftjis とか iso-2022-jp とかの場合にはやはりエラーが出る。入力ファイルとして日本語という足かせを付けて置くならば、可能性のある文字コードは euc-jp shiftjis 7bit-jis iso-2022-jp 位を考えれば十分だろう。set_suspect メソッドの引数にこれらの配列を渡せば「入力ファイルが日本語で書かれた文書」の文字コードの変更を自動的に行う 1 行スクリプトとなるはずだが、そうは問屋がおろさない。

$ perl -MEncode -MEncode::Guess -pe 'BEGIN{Encode::Guess->set_suspects(qw/euc-jp shiftjis 7bit-jis iso-2022-jp/);}  $_=encode("utf8",decode("Guess",$_));' hoge_euc-jp.txt > hoge_euc-jp_utf8.txt

例えば下のようなスクリプトを使って、文字コードの相互変換テストしてみるとやはりエラーが出る。

$ cat hoge.txt
日本語入力のテスト
$ cat enctest.sh
for to in utf8 euc-jp shiftjis 7bit-jis iso-2022-jp
do
        e="\$_=encode('${to}',decode(\"Guess\",\$_));"
        #e="print '${to}';"
        #perl -le "$e"
        perl -MEncode -MEncode::Guess -pe "$e" hoge.txt > hoge_${to}.txt
        echo -n "hoge_${to}.txt: "
        nkf -guess hoge_${to}.txt
done
for from in utf8 euc-jp shiftjis 7bit-jis iso-2022-jp
do
        echo -n "hoge_${from}.txt: "
        nkf -guess hoge_${from}.txt
        for to in utf8 euc-jp shiftjis 7bit-jis iso-2022-jp
        do
                #e="'\$_=encode(\"${to}\",decode(\"Guess\",\$_));'"
                e="BEGIN{Encode::Guess->set_suspects(qw/euc-jp shiftjis 7bit-jis iso-2022-jp/);} \$_=encode('${to}',decode(\"Guess\",\$_));"
                #perl -MEncode -MEncode::Guess -pe $e hoge_${from}.txt > hoge_${from}_${to}.txt
                perl -MEncode -MEncode::Guess -pe "$e" hoge_${from}.txt > hoge_${from}_${to}.txt
                echo -n "hoge_${from}_${to}.txt: "
                #echo $e
                nkf -guess hoge_${from}_${to}.txt
        done
done
exit
$ sh ./enctest.sh
hoge_utf8.txt: UTF-8
hoge_euc-jp.txt: EUC-JP
hoge_shiftjis.txt: Shift_JIS
hoge_7bit-jis.txt: ISO-2022-JP
hoge_iso-2022-jp.txt: ISO-2022-JP
hoge_utf8.txt: UTF-8
hoge_utf8_utf8.txt: UTF-8
hoge_utf8_euc-jp.txt: EUC-JP
hoge_utf8_shiftjis.txt: Shift_JIS
hoge_utf8_7bit-jis.txt: ISO-2022-JP
hoge_utf8_iso-2022-jp.txt: ISO-2022-JP
hoge_euc-jp.txt: EUC-JP
hoge_euc-jp_utf8.txt: UTF-8
hoge_euc-jp_euc-jp.txt: EUC-JP
hoge_euc-jp_shiftjis.txt: Shift_JIS
hoge_euc-jp_7bit-jis.txt: ISO-2022-JP
hoge_euc-jp_iso-2022-jp.txt: ISO-2022-JP
hoge_shiftjis.txt: Shift_JIS
hoge_shiftjis_utf8.txt: UTF-8
hoge_shiftjis_euc-jp.txt: EUC-JP
hoge_shiftjis_shiftjis.txt: Shift_JIS
hoge_shiftjis_7bit-jis.txt: ISO-2022-JP
hoge_shiftjis_iso-2022-jp.txt: ISO-2022-JP
hoge_7bit-jis.txt: ISO-2022-JP
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_7bit-jis_utf8.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_7bit-jis_euc-jp.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_7bit-jis_shiftjis.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_7bit-jis_7bit-jis.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_7bit-jis_iso-2022-jp.txt: ASCII
hoge_iso-2022-jp.txt: ISO-2022-JP
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_iso-2022-jp_utf8.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_iso-2022-jp_euc-jp.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_iso-2022-jp_shiftjis.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_iso-2022-jp_7bit-jis.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_iso-2022-jp_iso-2022-jp.txt: ASCII

つまり、入力ファイルを見ただけでは 7bit-jis か iso-2022-jp なのかわからんといわれているわけだ。これはそんなに不思議なことではない。というのも、hoge.txt には半角カタカナは含まれないため、それを変換した hoge_7bit-jis.txt にも hoge_iso-2022-jp.txt にも含まれない、従って「JIS X 0201 片仮名」で半角カタカナに割り当てられるようなビット列が含まれない。また、7bit-jis の「JIS X 0201 片仮名」(いわゆる半角カタカナ) を iso-2202-jp では定義していない。そのため、これらのいわゆる半角カタカナの含まれないファイルの文字コードを guess しても 7bit-jis と iso-2022-jp の区別が付かないのである。

では、半角カタカナの含まれる utf8 のファイルを先のスクリプトを通して iso-2022-jp に変換するとどうなるのか。この結果は半角カタカナが全角カタカナに変換される。また、7bit-jis に変換するとどうなるか。この結果は半角カタカナの部分がおかしなことになってしまった。うーんよくわからんな。テストしてみたコードは下のような感じ。

$ cat hoge.txt
日本語入力のテスト
ニホンゴニュウリョクノテスト
$ sh enc.sh
hoge_utf8.txt: UTF-8
hoge_euc-jp.txt: EUC-JP
hoge_shiftjis.txt: Shift_JIS
hoge_7bit-jis.txt: ISO-2022-JP
hoge_iso-2022-jp.txt: ISO-2022-JP
hoge_utf8.txt: UTF-8
hoge_utf8_utf8.txt: UTF-8
hoge_utf8_euc-jp.txt: EUC-JP
hoge_utf8_shiftjis.txt: Shift_JIS
hoge_utf8_7bit-jis.txt: ISO-2022-JP
hoge_utf8_iso-2022-jp.txt: ISO-2022-JP
hoge_euc-jp.txt: EUC-JP
shiftjis or euc-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_euc-jp_utf8.txt: UTF-8
shiftjis or euc-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_euc-jp_euc-jp.txt: EUC-JP
shiftjis or euc-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_euc-jp_shiftjis.txt: Shift_JIS
shiftjis or euc-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_euc-jp_7bit-jis.txt: ISO-2022-JP
shiftjis or euc-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_euc-jp_iso-2022-jp.txt: ISO-2022-JP
hoge_shiftjis.txt: Shift_JIS
hoge_shiftjis_utf8.txt: UTF-8
hoge_shiftjis_euc-jp.txt: EUC-JP
hoge_shiftjis_shiftjis.txt: Shift_JIS
hoge_shiftjis_7bit-jis.txt: ISO-2022-JP
hoge_shiftjis_iso-2022-jp.txt: ISO-2022-JP
hoge_7bit-jis.txt: ISO-2022-JP
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_7bit-jis_utf8.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_7bit-jis_euc-jp.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_7bit-jis_shiftjis.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_7bit-jis_7bit-jis.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_7bit-jis_iso-2022-jp.txt: ASCII
hoge_iso-2022-jp.txt: ISO-2022-JP
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_iso-2022-jp_utf8.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_iso-2022-jp_euc-jp.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_iso-2022-jp_shiftjis.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_iso-2022-jp_7bit-jis.txt: ASCII
7bit-jis or iso-2022-jp at /usr/local/lib/perl/5.8.8/Encode.pm line 170
hoge_iso-2022-jp_iso-2022-jp.txt: ASCII

とりあえず今日はここまで。なんだかよくわからないことになってしまった。とりあえず guess 機能のチェックからはじめよう。

Hello world と表示させる

さて、最も単純に考えると下のようになる。

$ perl -e 'print "Hello World!!\n";'

もう少し短くしたければ -l オプションで print 文と fprint 文の最後に \n を自動的に付加させる。下の 2 つは同じことである。

$ perl -le 'print "Hello World!!";'
$ perl -e '$\="\n"; print "Hello World!!";'

シェルの違いシステムの違いでスクリプトが動かなくなることがたまにある。これを解決するために文字列リテラル中の半角スペースの代わりに \x20 を使う。\x20 は ASCII コードにおける半角スペース (SP) を 16 進数であらわした (20) もの。

$ perl -le 'print "Hello\x20World!!";'

今回のネタはシステムの違いをいかに吸収して 1 行スクリプトを書くかについて考えているときに思い浮かんだ。

[rename] ファイルの名前を一括変換

いや、あえて Perl をつかって表現する必要はどこにもないのだけれど、そんなことも可能だよということで。おそらくこのシリーズはネタ切れのタイミングで続くな。まずは普通にやってみる。mv コマンドのほうが短いし、はっきり言ってメリットは無い。

$ mv hoge.txt hage.txt
$ perl -le 'print rename("hoge.txt", "hage.txt");'

このままだと面白くない、File::Rename モジュールを使おう。無い場合は CPAN から導入してくれと管理者にでも頼んでくだされ。まずは上と同じことをモジュールを使ってやってみる。

$ perl -MFile::Rename -le 'print File::Rename::rename("hoge.txt", "hage.txt");'

File::Rename モジュールはただの use 文では組み込み関数 CORE::rename をオーバーライドしてくれないので、File::Rename モジュールの rename 関数を呼ぶ場合には枕詞をつけて明示的に rename 関数を呼ばなければならない。もし、下のように書いた場合は CORE::rename を使っていることになる。

$ perl -MFile::Rename -le 'print rename("hoge.txt", "hage.txt");'

rename 組み込み関数を qw() でオーバーライドしてみる。オーバーライドは何回も renme 関数を呼ぶ場合には効果的だが、今回のように 1 回だけ呼ぶ場合にはメリットがあまり無い。

$ perl -le 'use File::Rename qw(rename); print rename("hoge.txt", "hage.txt");'

File::Rename::rename には一括変換の機能もあるので、もう少しブリコってみよう。例えば、カレントディレクトリの中に 100 個の .jpg がありこれらを .jpeg に変える場合、mv コマンドを 100 回たたく代わりに 1 行スクリプトで 1 回で終わらせる。

perl -e 'use File::Rename qw(rename); @F=<*>; rename(@F,sub{s/\.jpg$/.jpeg/},1);'

これでカレントディレクトリ中の .jpg でファイルネームが終わるファイル全てを .jpeg で終わるようにリネームできる。<*> はカレントディレクトリのファイル全て、rename 関数に与える引数を絞るにはここで grep{} するほうがいいと思う。@F は適当な名前の一時的配列だが、rename 関数の中の @F を <*> に変えるをエラーがでるので注意。rename 関数の第 3 引数は詳細な出力をさせるオプショナルなフラグ、1 だと詳細出力それ以外は出力なし。

[unlink] ファイルを一括削除

ファイルを選んで消そう、削除しようと思う。

$ rm hoge.txt
$ perl -le 'print unlink("hoge.txt");'

これで OK。前使ったダイヤモンド演算子を使ってワイルドカード指定してみる。

$ rm *.txt
$ perl -le 'print unlink(<*.txt>);'

やはり、シェル本来の機能を使ったほうがいい。もう少しトリッキーな例を挙げてみよう。

$ find . -regex '^[A-Z][0-9]{4}.*\.txt$' -print0 | xarg -0 rm
$ perl -le '@F=grep{m/^[A-Z][0-9]{4}.*\.txt$/}<*>; print unlink(@F);'

これでカレントディレクトリのファイルの内、1 文字目がアルファベットの大文字でその後ろの 4 文字が数字で最後が .txt でおわるファイルを削除する。これはシェルでは面倒だろうとおもったが、find であっさり解決。更新日時とかファイルタイプとかで篩い分けしてもシェルに軍配があるような気がする。

シンボリックリンク

まぁ Perl をつかって貼ることもないんだけれど。perl でも出来るよということで。

$ perl -e 'symlink("hoge","hage")'

n 進数

ここまでくるとほとんど 1 行で書くメリットなどほとんどないように思われる。でも、自分の思考をすぐに書け、これを確かめられるのはとてもよいことだとと思う。その点において、あえてファイルにしなくてもシェルでかき始められる 1 行スクリプトは結構役に立つと思う。まぁ能書きはおいといて 10 進数から 20 進数へ変換してみた。

$ perl -le 'print join(" ",&sin(130344445,20)); sub sin{my $s=shift @_; my $p=shift @_; my $i=0; do{push(@a, $s%$p); $s=int($s/$p); $i++;}while($s!=0); return reverse @a;}'
2 0 14 13 1 2 5
|      i              =      a_0 * n ^ 0     +     a_1 * n ^ 1     +     a_2 * n ^ 2     + ...
|     (i) / n ^ 0     =     (a_0 * n ^ 0     +     a_1 * n ^ 1     +     a_2 * n ^ 2     + ...) / n ^ 0
|     (i) / n ^ 0     =      a_0 * n ^ 0     +     a_1 * n ^ 1     +     a_2 * n ^ 2     + ...
| mod((i) / n ^ 0, n) =  mod(a_0 * n ^ 0     +     a_1 * n ^ 1     +     a_2 * n ^ 2     + ..., n)
| mod((i) / n ^ 0, n) =  mod(a_0 * n ^ 0, n) + mod(a_1 * n ^ 1, n) + mod(a_2 * n ^ 2, n) + ...
| mod((i) / n ^ 0, n) =      a_0             +     0               +     0               + ...
| mod((i) / n ^ 0, n) =      a_0
|      i - a_0 * n ^ 0              =     a_1 * n ^ 1     +     a_2 * n ^ 2     + ...
|     (i - a_0 * n ^ 0) / n ^ 1     =    (a_1 * n ^ 1     +     a_2 * n ^ 2     + ...) / n ^ 1
|     (i - a_0 * n ^ 0) / n ^ 1     =     a_1 * n ^ 0     +     a_2 * n ^ 1     + ...
| mod((i - a_0 * n ^ 0) / n ^ 1, n) = mod(a_1 * n ^ 0     +     a_2 * n ^ 1     + ..., n)
| mod((i - a_0 * n ^ 0) / n ^ 1, n) = mod(a_1 * n ^ 0, n) + mod(a_2 * n ^ 1, n) + ...
| mod((i - a_0 * n ^ 0) / n ^ 1, n) =     a_1             +     0               + ...
| mod((i - a_0 * n ^ 0) / n ^ 1, n) =     a_1
|      i - a_0 * n ^ 0 - a_1 * n ^ 1              =      a_2 * n ^ 2     + ...
|     (i - a_0 * n ^ 0 - a_1 * n ^ 1) / n ^ 2     =     (a_2 * n ^ 2     + ...) / n ^ 2
|     (i - a_0 * n ^ 0 - a_1 * n ^ 1) / n ^ 2     =     (a_2 * n ^ 0     + ...)
| mod((i - a_0 * n ^ 0 - a_1 * n ^ 1) / n ^ 2, n) = mod((a_2 * n ^ 0     + ...), n)
| mod((i - a_0 * n ^ 0 - a_1 * n ^ 1) / n ^ 2, n) = mod((a_2 * n ^ 0, n) + ...
| mod((i - a_0 * n ^ 0 - a_1 * n ^ 1) / n ^ 2, n) =      a_2             + ...
| mod((i - a_0 * n ^ 0 - a_1 * n ^ 1) / n ^ 2, n) =      a_2
| i = a_0 * n ^ 0 +  a_1 * n ^ 1 +  a_2 * n ^ 2  + ...
| i = a_0 * n ^ 0 + (a_1 * n ^ 0 +  a_2 * n ^ 1  + ...) * n ^ 1
| i = a_0 * n ^ 0 +  a_1 * n ^ 0 + (a_2 * n ^ 0  + ...) * n ^ 2
| i = a_0 * n ^ 0 +  a_1 * n ^ 0 +  a_2 * n ^ 0 (+ ...) * n ^ 3

コーヒーの色は水色

「1000cc バイクの色は青、ツツジの色は灰色、フリースの色は白、オフィスの色は空色」コーヒーの色は水色というネタを読んで、じゃぁ探してみようという気になった。HTML の色指定で使えるアルファベットは a-f の 6 種類、これを 6 つ並べるとこんな感じ。まぁ、どれが意味あるアルファベットかは人間様が判断するのだけれど。

$ perl -e '@F=qw(a b c d e f); for($i=0;$i%lt;6*6*6*6*6*6;$i++){@a=&sin($i,6); $#a=6; @a=reverse @a; foreach(@a){print "$F[$_]"}print"\n";} sub sin{my $s=shift @_; my $p=shift @_; my @a=(); my $i=0; do{push(@a, $s%$p); $s=int($s/$p); $i++;}while($s!=0); return @a;}'| head
aaaaaaa
aaaaaab
aaaaaac
aaaaaad
aaaaaae
aaaaaaf
aaaaaba
aaaaabb
aaaaabc
aaaaabd

で、まず考えてしまったのが、6 重の for ループ。これだと 1 行スクリプトとしては長すぎ。ということで、6 進数を考えて 6 の n 乗の係数をそれぞれの桁として使う。これで解決。

出力が多すぎなので考え方逆に。単語辞書から 6 文字の単語だけ抽出、意味は大文字が小文字になったところで変わらないので小文字に正規化。sort | uniq で重複削除

$ perl -lne 'while(m/(\w+)/g){print lc $1}' edict | perl -ne 'if(m/^\w{6}$/){print;}' | sort | uniq | less

16 進数で表せる 0 から f までの文字でそれ以外の文字と形が似ているものをピックアップすると下のようになる。

0 - O,D,o
1 - I,l
2 - z,Z
3,4,5
6 - b
7,8,9,a
b - 6
c,d,e,f
A,B,C
D - 0
E
c0ffee - coffee

右から左への変換を行い、変換後の文字列が [0-9a-fA-F]{6}となれば OK である。ただし、変換前は全部小文字になっているので、右は全部小文字にしておく。また、b を 6 にする変換は必要ない。変換しなくても 16 進で表せるから。同様に 6 を b にする変換と 0 を D にする変換も。したがって必要な変換はこんな感じ。

tr/odilz/00112/;

一気に全部やって篩にかけるにはこんな感じ。

$ perl -lne 'while(m/(\w+)/g){print lc $1}' edict | perl -ne 'if(m/^\w{6}$/){print;}' | sort | uniq | perl -ne 'chomp; $a=$_; tr/odilz/00112/; if(m/^[0-9A-Fa-f]{6}$/){print "$a\t$_\n";}' | less

で、篩にかけた結果、残ったのが下。初めのカラムが変換前で 2 番目のカラムが色コードである。3 カラム目には適当な意味を手作業で付けた。

1000cc  1000cc  排気量
289bce  289bce  ?
800000  800000  ?
abelia  abe11a  《植物》ツクバネウツギ属の低木  スイカズラ科の植物
ablaze  ab1a2e  輝いている、燃え立っている、興奮している
acacia  acac1a  《植物》アカシア
accede  acce0e  継ぐ、継承する、就く、就任する、権力の座に就く
acidic  ac101c  酸っぱい、酸の、酸性の
adelie  a0e11e  南極の地名
albedo  a1be00  アルベド◆太陽の光を地球が反射する割合。
alcedo  a1ce00  カワセミ
allele  a11e1e  対立遺伝子
allied  a111e0  同盟している
azalea  a2a1ea  ツツジ
babble  babb1e  せせらぎ(の音)
baddie  ba001e  悪役、悪者◆映画などに登場する
balboa  ba1b0a  バルボア◆パナマの貨幣単位
ballad  ba11a0  民間伝承の物語詩、バラード形式の詩・曲、歌謡、民謡
baobab  ba0bab  《植物》バオバブ
beefed  beefe0  強化?
befall  befa11  〔災難・異変などが〕起こる、生じる
belief  be11ef  信仰、信条
belize  be112e  ベリーズ(中米の国)
biblid  b1b110  ?
bifida  b1f10a  二叉
billed  b111e0  くちばしのある
biloba  b110ba  《植物》イチョウ◆学名
bladed  b1a0e0  ブレード[刀]の付いた
bobbed  b0bbe0  ショートカットの、断髪の
bodice  b001ce  女性用胴着
bodied  b001e0  ~な体を持つ
boiled  b011e0  ボイルした、ゆでた、煮た
cabala  caba1a  密教、神秘的教義
caddie  ca001e  《ゴルフ》キャディー、使い走りや雑用をする人
calico  ca11c0  キャラコ、キャリコ、更紗、サラサ
called  ca11e0  ~と呼ばれている
celiac  ce11ac  セリアック病患者
celica  ce11ca  神々しい、天上の
celled  ce11e0  ~細胞の[を持つ]
cicada  c1ca0a  《虫》セミ
cobble  c0bb1e  敷石、栗石、丸石、玉石
coffee  c0ffee  コーヒー
coiled  c011e0  ひもなどでグルグル巻きにされた
collie  c0111e  コリー◆スコットランド原産の牧羊犬
cooled  c001e0  冷却される
coolie  c0011e  クーリー、日雇い労働者◆インド、中国での
dabble  0abb1e  〔戯れ程度に〕水を跳ねかける
dacelo  0ace10  ワライカワセミ
daidai  0a10a1  ?
dazzle  0a221e  輝くもの、まぶしい光、まぶしさ、輝き
decade  0eca0e  10年間、10年
decide  0ec10e  決定する、決心する、決意する
decode  0ec00e  〔符号化された情報の〕復号、デコード
decola  0ec01a  ?
deface  0eface  ~の外観を損なう
defile  0ef11e  ~の美観を損なう、汚す、不潔にする
docile  00c11e  従順な、素直な、おとなしい、御しやすい
doodle  00001e  いたずら書き
ebcdic  ebc01c  拡張2進化10進コード(extended binary coded decimal interchange code)
edible  e01b1e  食料品、料理、食事
efface  efface  〔絵・文字・痕跡などを〕こすって消す、削除する
eiffel  e1ffe1  エッフェル
elodea  e100ea  カナダモ(植物)
fabled  fab1e0  寓話として名高い、伝説的な
facade  faca0e  〈フランス語〉表面、外観、外見、一面、うわべ、見せかけ
facial  fac1a1  美顔術
facile  fac11e  手軽な、容易な、たやすく得られる、軽快な、軽薄な、器用な
failed  fa11e0  失敗した、不成功に終わった
feeble  feeb1e  体力の弱った、弱々しい、か弱い、力がない、もろい
fiddle  f1001e  《楽器》フィドル、バイオリン
filial  f111a1  子供の、子供としてふさわしい
filled  f111e0  一杯詰まった、満杯の、充満した、詰め物をした
fizzle  f1221e  弱く消えてしまうようにシューと音を出す
fleece  f1eece  フリース◆ポリエステル起毛の合成繊維。
folded  f010e0  折られた、折り畳まれた
fooled  f001e0  だまされれる
icicle  1c1c1e  つらら、氷柱
iodide  10010e  ヨウ化物
labial  1ab1a1  唇音
lablab  1ab1ab  (植物)フジマメ
laddie  1a001e  若いの
leaded  1ea0e0  有鉛の
leafed  1eafe0  葉のある
liable  11ab1e  〔法的に〕責任がある、責任を負うべき、~を免れない
libido  11b100  《精神分析》リビドー、性的衝動、性欲
lidded  1100e0  〔容器などに〕ふたのある
loaded  10a0e0  荷物を積んだ
locale  10ca1e  現場、場所
office  0ff1ce  事務所
zodiac  2001ac  十二宮図、黄道帯、獣帯

意味が取れないものがいくつか混じっているが後は目で抜く。とりあえず下にまとめた。

意味の取れる色のリスト
1000cc1000cc応用が利きそう。
acaciaacac1a《植物》アカシア、色との対応が付いていたらすごい。
allelea11e1e対立遺伝子、lがそれぞれ1に対応している。でも、単語がなじみなさすぎ
azaleaa2a1eaツツジ、実物との対応
babblebabb1eせせらぎ(の音)、実物との対応
balboaba1b0aバルボア◆パナマの貨幣単位、2箇所変化
baobabba0bab《植物》バオバブ、1箇所変化
befallbefa11〔災難・異変などが〕起こる、生じる、2箇所変化
cabalacaba1a密教、神秘的教義、1箇所変化
cobblec0bb1e敷石、栗石、丸石、玉石、2箇所変化
coffeec0ffeeコーヒー、1箇所変化
doodle00001eいたずら書き、ほとんど原形とどめず
effaceefface〔絵・文字・痕跡などを〕こすって消す、削除する、一切変化なしの完全形
feeblefeeb1e体力の弱った、弱々しい、か弱い、力がない、もろい、1箇所変化
fleecef1eeceフリース◆ポリエステル起毛の合成繊維。、1箇所変化
locale10ca1e現場、場所、3箇所変化
office0ff1ce事務所、2箇所変化
zodiac2001ac十二宮図、黄道帯、獣帯、ほとんど原形とどめず。

使えそうなのは 1000cc、azalea、cabala、coffee、fleece、office ぐらいか。ということで最初の話に戻る。

MD5 とか SHA1 でハッシュ計算

これもあえて Perl でしなくてもいいことなのだけど、ごくたまに MD5 とか SHA1 のハッシュを求めんといかん状況に追い込まれることがある。ごくたまにしかない状況のためにいちいち探してインストールしてというのはあまり好きじゃない。ということで、あるメッセージのハッシュ値を計算させるには下のようにする。

$ perl -le 'use Digest::MD5 qw(md5_hex); print md5_hex("admin");'
21232f297a57a5a743894a0e4a801fc3
$ perl -le 'use Digest::SHA1 qw(sha1_hex); print sha1_hex("admin");'
d033e22ae348aeb5660fc2140aec35850c4da997

MD5 のモジュールはすでにインストール済みだったが、SHA1 はインストールされてなかったので導入した。

1 行で CPAN モジュールの導入

うちのサーバなので、勝手にモジュールのインストールが可能だ。このような場合は何をするにも楽なのだ。root になって Diget::SHA1 を導入してみる。

# cpan -i Digest::SHA1

1行とかこだわらなければ

# cpan
cpan> install Digest::SHA1
cpan> quit

あえてタイプ数の多い方法を使いたければ

# perl -MCPAN -e shell

こんな感じで OK。perl ってのはすごいね。きっと Windows だろうが Linux だろうが、perl の世界にはいってしまえば OS 間の差異は取り払われてしまうのかもしれない。まぁそれはそれとて、導入できたか確認とバージョンの確認をしてみよう。下の 2 つは同じこと、失敗しているとここでエラーメッセージが出る。

$ perl -MDigest::SHA1 -le 'print $Digest::SHA1::VERSION;'
$ perl -MDigest::SHA1 -le 'print Digest::SHA1->VERSION;'

よくあるバージョン確認の方法は上の 2 つ。つまり、モジュールを use してモジュール内の $VERSION 変数を参照する方法。バージョンチェックの 3 番目は下のような感じ。長くなった割にあまり面白くないが、モジュールを読み込まないので多少メモリの節約になるかもしれない。

$ perl -MExtUtils::MakeMaker -le 'print MM->parse_version(shift)' /usr/lib/perl5/site_perl/5.8.6/i386-linux-thread-multi/Digest/SHA1.pm

たった 1 つのモジュールのバージョンをチェックするだけなら MakeMaker をつかうのはもったいない。モジュールのリストとバージョンをチェックするには下のようにするとよさげ。MakeMaker を使う方法では、実際にモジュールを読み込んでいるわけではないので (use とか require で) コンパイルが通るかどうかまではわからない。

$ perl -MExtUtils::MakeMaker -MFile::Find -le 'find(sub{$a=$File::Find::name; if(m/pm$/){print MM->parse_version($a)."\t".$a}},@INC);' | less

当然といえば其の通りだが勝手にどんどんインストールしてしまうとトラブルが起きる。うちの etch では「apt-get update 後 (perl のアップグレード含む) の perl プログラムが軒並み下のエラーメッセージを出して終了する」というトラブルが起きた。

# apt-get update
# apt-get upgrade
# cpan(例えば。)
Errno architecture (i486-linux-gnu-thread-multi-2.6.18-6-k7) does not match executable architecture (i486-linux-gnu-thread-multi-2.6.18-6-686) at /usr/local/share/perl/5.8.8/Errno.pm line 11.
Compilation failed in require at /usr/local/share/perl/5.8.8/CPAN.pm line 1107.
BEGIN failed--compilation aborted at /usr/local/share/perl/5.8.8/CPAN.pm line 1107.
Compilation failed in require at /usr/local/bin/cpan line 175.
BEGIN failed--compilation aborted at /usr/local/bin/cpan line 175.
Can't call method "has_usable" on an undefined value at /usr/local/share/perl/5.8.8/CPAN/HandleConfig.pm line 502.
END failed--call queue aborted at /usr/local/bin/cpan line 175.

たしか、apt-get upgrade のときに perl を upgrade していたような気がする。んで、upgrade の前には cpan コマンドでいくつかのモジュールを install & update していた。Errno.pm でまずだめになっているようなのでどこにあるか探すと 2 つ出てくる。

$ locate Errno.pm
/usr/lib/perl/5.8.8/Errno.pm
/usr/local/share/perl/5.8.8/Errno.pm

Errno architecture とか言ってくるのはどっちだと言うことで grep

# grep "Errno architecture" /usr/lib/perl/5.8.8/Errno.pm /usr/local/share/perl/5.8.8/Errno.pm
/usr/local/share/perl/5.8.8/Errno.pm:   die "Errno architecture (i486-linux-gnu-thread-multi-2.6.18-6-k7) does not match executable architecture ($Config{'archname'}-$Config{'osvers'})";

ということで/usr/local/share/perl/5.8.8/Errno.pm を読みに言っている様子。じゃぁ @INC の順番を調べる。なぜかワンライナーはいけるようで下のような感じ。

# perl -e 'print "@INC"'
/etc/perl /usr/local/lib/perl/5.8.8 /usr/local/share/perl/5.8.8 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.8 /usr/share/perl/5.8 /usr/local/lib/site_perl .

なるほど。やはりこの順番でよみにいってるのだな。2 つの Errno.pm のバージョンチェック

# grep "VERSION" /usr/lib/perl/5.8.8/Errno.pm /usr/local/share/perl/5.8.8/Errno.pm
/usr/lib/perl/5.8.8/Errno.pm:our (@EXPORT_OK,%EXPORT_TAGS,@ISA,$VERSION,%errno,$AUTOLOAD);
/usr/lib/perl/5.8.8/Errno.pm:$VERSION = "1.09_01";
/usr/lib/perl/5.8.8/Errno.pm:$VERSION = eval $VERSION;
/usr/local/share/perl/5.8.8/Errno.pm:our (@EXPORT_OK,%EXPORT_TAGS,@ISA,$VERSION,%errno,$AUTOLOAD);
/usr/local/share/perl/5.8.8/Errno.pm:$VERSION = "1.10";
/usr/local/share/perl/5.8.8/Errno.pm:$VERSION = eval $VERSION;

つまり、version 1.10 のほうを読みに行っているのね。それはおそらく cpan コマンドで更新されたものだな。ということでこちらを Errno.pm から Errno.pm.org にリネーム

# mv /usr/local/share/perl/5.8.8/Errno.pm{,.org} -i

で、cpan を走らせてみる。

# cpan
cpan shell -- CPAN exploration and modules installation (v1.9301)
ReadLine support enabled
cpan[1]>

走るな。じゃぁいままでだめだったプログラムを走らせて見る。

$ perl ./hoge.pl

走ったか。全く良くわからんがリネームでこのトラブルは回避できた予感。今回のトラブルは cpan がインストールするディレクトリと apt-get がインストールモジュールのディレクトリが違うことで起こってしまったと思われる。同じにしたらしたで問題ありなのでとりあえずこの状態で様子見運用します。

cpan のダウンロード先変更

まずは今の設定をチェックする。

# cpan
cpan> o conf urllist
        ftp://ftp.ring.gr.jp/pub/lang/perl/CPAN/
cpan> o conf urllist pop ftp://ftp.ring.gr.jp/pub/lang/perl/CPAN/
cpan> o conf urllist push http://ftp.riken.jp/lang/CPAN/
cpan> o conf urllist
        http://ftp.riken.jp/lang/CPAN/
cpan> o conf commit
cpan> q

変更が出来たらその設定済みの内容を Config.pm に反映する。再度 cpan を起動して設定内容が破棄されていないかどうかを確認。

# cpan
cpan> o conf urllist
        http://ftp.riken.jp/lang/CPAN/
  1. cpan urllist - Google 検索
  2. CPAN の ダウンロード先(URL リスト)を変更する方法 :: Drk7jp
  3. CPAN/SITES

html エスケープ

html を直に書くとき、> を &gt; に書き換えたりする。これを HTML エスケープというのかは知らないが、タグにはさまれた内容に含めていけないものを含めてもよい形に変換することをぼくは HTML エスケープと呼んでいる。これを 1 行でしてみよう。

$ perl -pe 'BEGIN{use HTML::Entities qw(encode_entities);} encode_entities($_,"<>&");' hoge.txt
$ perl -MHTML::Entities -pe 'encode_entities($_,"<>&");' hoge.tex

こんな感じで標準出力にエスケープされた内容が表示される。BEGIN{} ブロックで HTML::Entities をロードして、この中でユーザ空間の encode_entities を HTML::Entities::encode_entities に差し替える。-p オプションで hoge.txt の内容をいちいち出力、出力前にエスケープ処理を行う。モジュールをよく読めばその下のようにもかける。このように出力されたものを pre の中に含め、ブラウザで表示させると、エスケープ前の文字列が見える。

CGI.pm を使うという手もある。CGI.pm は確か何もしなくても導入してあったと思う。色々入れすぎて訳わかんなくなってるけど。例えば下のような感じ、こちらのほうが短くてすむので少しうれしゅうございますだ。でも、これだとうまくいかない場合がある。やはり正攻法は encode_enities だろう。

$ perl -MCGI -pe '$_=CGI::escapeHTML($_);' hoge.tex

URL エンコードと URL デコード

hoge.txt はテキストファイル。日本語とか英語とか空行とか改行とか <>&" とかごちゃ混ぜに入っている。

$ cat hoge.txt
日本語
eigo
<
>
&
"
$ perl -MCGI::Util -pe '$_=CGI::Util::escape($_);' < hoge.txt
%E6%97%A5%E6%9C%AC%E8%AA%9E%0Aeigo%0A%0A%3C%0A%3E%0A%26%0A%22%0A%0A%0A

変換された内容を見てみるとなんとなく正しく変換されているようだ。元に戻すには下のようにする。

$ perl -MCGI::Util -pe '$_=CGI::Util::escape($_);' < hoge.txt > hoge_escape.txt
$ perl -MCGI::Util -pe '$_=CGI::Util::unescape($_);' < hoge_escape.txt
日本語
eigo
<
>
&
"

ところでこういう処理のこと URL エンコード、URL デコードっていうのは正しいのかな。それとも URL エスケープ、URL アンエスケープって言うのが正しいのかな。

  1. 知らないことがあってもへっちゃらさ: URL エンコードされた文字を Perl でデコードしてみる
  2. JavaScriptでエンコードした値をPerlでデコード - Vox

バブルソート

暇ではないが、暇と認めたくはないが、本当は全く持って暇でもなんでもないのでけれど、暇だと思いたい。小人閑居して不全をなす。バブルソートをあえて 1 行で書く必要など全くないのだけれど、これも腐りきった脳髄の刺激のためにやっておく必要があるだろう。

$ perl -le '@F=qw(8 4 3 7 6 5 2 1); for($i=0;$i<@F;$i++){for($j=1;$j<@F-$i;$j++){print "@F | $i $j"; if($F[$j]<$F[$j-1]){@F[$j-1,$j]=@F[$j,$j-1];}}} print "@F";'
8 4 3 7 6 5 2 1 | 0 1
4 8 3 7 6 5 2 1 | 0 2
4 3 8 7 6 5 2 1 | 0 3
4 3 7 8 6 5 2 1 | 0 4
4 3 7 6 8 5 2 1 | 0 5
4 3 7 6 5 8 2 1 | 0 6
4 3 7 6 5 2 8 1 | 0 7
4 3 7 6 5 2 1 8 | 1 1
3 4 7 6 5 2 1 8 | 1 2
3 4 7 6 5 2 1 8 | 1 3
3 4 6 7 5 2 1 8 | 1 4
3 4 6 5 7 2 1 8 | 1 5
3 4 6 5 2 7 1 8 | 1 6
3 4 6 5 2 1 7 8 | 2 1
3 4 6 5 2 1 7 8 | 2 2
3 4 6 5 2 1 7 8 | 2 3
3 4 5 6 2 1 7 8 | 2 4
3 4 5 2 6 1 7 8 | 2 5
3 4 5 2 1 6 7 8 | 3 1
3 4 5 2 1 6 7 8 | 3 2
3 4 5 2 1 6 7 8 | 3 3
3 4 2 5 1 6 7 8 | 3 4
3 4 2 1 5 6 7 8 | 4 1
3 4 2 1 5 6 7 8 | 4 2
3 2 4 1 5 6 7 8 | 4 3
3 2 1 4 5 6 7 8 | 5 1
2 3 1 4 5 6 7 8 | 5 2
2 1 3 4 5 6 7 8 | 6 1
1 2 3 4 5 6 7 8

できて当たり前だぎゃね。まぁ今回は配列要素の入れ替えの勉強になったからよしとしよう。C 言語だとこんな感じの書き語って許されてたんだっけかなぁ。たしか一時変数に保存して上書きするという方法でやっていたような気がする。

stat で ls の代わり

やってみそ

$ perl -le 'foreach(<*>){print join("\t",stat(),$_);}'

やってみた、これ以上書くとなんだかせっかくの 1 行のメリットが失われる感があるのでやめておこう。それぞれのカラムが何を意味しているのかは調べてほしい。stat 系のモジュールは結構たくさんある。出力を ls っぽくしてくれるものとか。やっぱり考えることはみな同じだなぁ。

ディレクトリを作れ

カレントに test というディレクトリをつくるにはこんな感じ。でも、コマンドで mkdir -p のようにしてやるような多重階層のディレクトリ作成は無理っぽい。どっかのモジュールにあったような気がするんだけれど思い出せん。

$ perl -e 'mkdir("test");'

1 行で平均値

それぞれのコラムに対応した平均値を求めてみる。入力ファイルは 5 コラムで 4 行のタブ区切りテキストで下のような感じ。

$ cat hoge.dat
1       2       3       4       5
6       7       8       9       10
11      12      13      14      15
16      17      18      19      20

で、下のようにして平均値を出力した。重要なのは、hoge.dat の最後に改行がないということ。最後に改行があると、最後に $i が 5 となるため、正確でない値がでる。

$ perl -alne 'map{$S[$_]+=@F[$_]}(0..$#F);$i++; END{print join"\t",map{$_/$i}@S;}' hoge.dat
8.5     9.5     10.5    11.5    12.5

特定の行のみ出力 (head の代替)

head の代替をするなら下のような感じ。

$ head -n 100 hoge.dat
$ perl -ne 'print if 1 .. 100' hoge.dat

行数を知っていれば tail の代替もできる。行数が 200 行として最後から 50 行を出力するならば下のような感じ。

$ tail -n 50 hoge.dat
$ perl -ne 'print if 251 .. 200' hoge.dat

gnuplot と組み合わせるとかなり便利な感じ。

文字列の検索 (grep の代替)

hoge が含まれる行を表示する。-p オプションを使わないのがミソ。-p オプションは常に出力が必要な場合に付ける。1 行 if 文の書き方がわかれば 2 行目でも同じこと。

$ perl -ne 'if(m/hoge/){print;}' file
$ perl -ne 'print if /hoge/' file

大文字小文字の区別を無視するなら、m//i で下のようにする。これだと Hoge も HOGE も hOgE にも引っかかる。

$ perl -ne 'if(m/hoge/i){print;}' file
$ perl -ne 'print if /hoge/i' file

perl で google PageRank を取得する

あるサイトの PageRank を知りたいと思ったときに GoogleToolbar を入れなくてもわかる方法がある。其の方法とは google toolbar が叩いている URL をブラウザなりで叩くことだが、この場合には pagerank を調べてたいサイトに対応したチェックサム (ch=) の計算が必要になる。この計算と受信した内容をパースしてくれるモジュールが WWW::Google::PageRank。

$ perl -MWWW::Google::PageRank -e 'print scalar WWW::Google::PageRank->new->get("http://www.google.com/");'
  1. page rank チェック toolbar - Google 検索
  2. ページランクとGoogle toolbarについて by Eva
  3. features=Rank - Google 検索
  4. WWW::Google::PageRank - Perlメモ - perlmemoグループ
  5. はっぴぃ・りなっくす - Google PageRank - Tools > Google - SmartSection
  6. Geekなぺーじ : Googleページランクの取得(WWW::Google::PageRank)
  7. Perl Tips | Perl で、Google の PageRank を表示する方法
  8. oogle PageRank perl - Google 検索
  9. WWW::Google::PageRank - Query google pagerank of page - search.cpan.org
  10. MobileRead Forums - View Single Post - Google PageRank Checksum Algorithm
  11. Google PageRank Checksum Algorithm - MobileRead Forums

UNIX time が「1234567890」になる時間

$ perl -le 'print scalar localtime 1234567890'
Sat Feb 14 08:31:30 2009

ソーシャルブックマーク

  1. はてなブックマーク
  2. Google Bookmarks
  3. del.icio.us

ChangeLog

  1. Posted: 2007-08-25T15:52:36+09:00
  2. Modified: 2007-08-25T04:05:38+09:00
  3. Generated: 2017-08-07T23:09:16+09:00