二つのファイルの行を一つの行にまとめて出力する
二つのファイルの行を一つの行に、たとえばタブ区切りで並べて出力できればグラフにする時に便利だなあとずっと思っていたので、perlで作ってみました。名前はmerge_2file.plとしてみました。
ソース
#!/usr/bin/perl use strict; use warnings; #引数が二個入力されてなかったら終了 if (@ARGV != 2){ die "usage: merge_2file.pl FILE1 FILE2\n"; } my $i = 0; #FILE1読み込み open(IN1, "$ARGV[0]") or die "file open error:$!\n"; #FILE1の中身を配列に格納 my @array1 = <IN1>; #FILE1のクローズ close(IN1); #FILE2読み込み open(IN2, "$ARGV[1]") or die "file open error:$!\n"; #FILE2の中身を配列に格納 my @array2 = <IN2>; #FILE2のクローズ close(IN2); foreach (@array2){ #改行コード削除 chomp($array1[$i]); #出力 print $array1[$i] , "\t" , "$_"; $i += 1; }
使い方
merge_2file.pl tmp.list tmp2.list
tmp.listの中身が
10:01 10:02 :
tmp2.listの中身が
10.5 20.5 :
だとすると、実行すると
10:01 10.5 10:02 20.5 :
になります。素晴らしい。
・・・と、自己満足に浸っていたところ、LinuxやSolarisの標準コマンドで同じ機能が実現されている事を知りました。
ここやここで説明されているpasteというコマンドを使ってみたら、私の作ったスクリプトと同じ動作になりました。いや、pasteは三つ以上のファイルも読み込めるみたいなので、むしろ優れています(当たり前です)。
ああ、これが「便利な道具があるにも関わらず、一から同じ道具を作ってしまう無駄」、すなわち車輪の再発明ってやつですか・・・。がっかり。
せっかくなので使い方の説明です。
pasteの使い方
paste tmp.list tmp2.list
ファイルは上と同じです。実行すると
10:01 10.5 10:02 20.5 :
となります。
paste -d "," tmp.list tmp2.list
とdオプションをつけてやると、
10:01,10.5 10:02,20.5 :
というように、区切り文字を変更できます。
せっかく作った道具がすでに存在したのは残念ですが、一から作った満足感は残ったので良しとしますわ・・・。
ではー。
perlで世界のナベアツ問題 その後
昨日突然、会社の同期にブログを見つけられて、二年前に書いたperlで世界のナベアツ問題について指摘を受けました。曰く、「こんなのワンライナーじゃねえ」はいその通りです。
で、指摘してくれた同期によるお手本が以下の通り。アルゴリズムを替えずにこんなに短くなるものなのですね。
体裁を整えるため改行していますが実際は「\」の無い一行となります。
バージョン0.1
perl -le 'print ((!($_%8))?"$_ぅぅういえぇえあ"\ :(!($_%3) or $_=~/3/)?"$_っ!!":$_)for(1..40);'
バージョン0.2
perl -le 'print ((!($_%8))?"$_ぅぅういえぇえあ"\ :(!($_%3) or /3/)?"$_っ!!":$_)for(1..40);'
バージョン0.3(最新バージョン)
perl -le 'print !($_%8)?"$_ぅぅういえぇえあ"\ :(!($_%3) or /3/)?"$_っ!!":$_ for(1..40);'
以下の通りご本人による解説を書いていますので見てみよう。
http://www.jp-z.jp/changelog/2010-01-05.html#2010-01-05-1
ではー。
perlで世界のナベアツ問題
昨日見つけた、世界のナベアツ問題を、perlでやってみました。perl初心者なので素直に書いたつもりです。
#!/usr/bin/perl for ($i = 1; $i <= 40; $i++) { $aho = $i % 3; $aho2 = $i; if ( $aho2 >= 10 ) { for ( $aho3 = 0; $aho2 > 10; $aho3++ ) { $aho2 = $aho2 - 10; } } $ii = $i % 8; if ( $ii == 0 ) { print "$iぅぅういえぇえあ\n"; } elsif ( $aho == 0 || $aho2 == 3 || $aho3 == 3 ) { print "$iっ!!\n"; } else { print "$i\n"; } }
ネットで色々調べた結果、以下のように短く出来ました。
#!/usr/bin/env perl foreach ( 1..40 ) { if ( $_ %8 == 0 ) { print "$_ぅぅういえぇえあ\n"; } elsif( $_ %3 == 0 || $_ =~/3/ ) { print "$_っ!!\n"; } else { print "$_\n"; } }
まだまだ長いでしょうが、ワンライナーに無理やりしたら以下の通りです。
perl -e 'foreach(1..40){if($_%8==0){print"$_ぅぅういえぇえあ\n";}elsif($_%3==0||$_=~/3/){print"$_っ!!\n";}else{print"$_\n";}}'
ではー。
2010年1月6日追記:
その後を書きました。
標準出力を引数としてコマンドを実行する(xargs)
何を言っているのか自分でもよく分からないですが、例えば
#ls -1 a1.txt a2.txt a3.xls a4.xls
というようにファイルがあって、これらを全て消したい場合、
# rm -rf ./*
でも良いんですが、これでも可能です。
# ls -1|xargs rm -rf
別に使わなくてもいいじゃん、とか思いますが、これが便利なのです。例えば*.xlsだけ消したいときは、
# find . -name "*.xls"|xargs rm -rf
とすれば良いのです。これは便利。ファイルが多ければ多いほど効果を発揮します。
ちなみに、標準出力は行ごとに処理するようなので、ls -1でもfindでもいいでしょう。
Perlでやるとこんな感じ。この方がより複雑な事が出来るので、良い感じです。
# ls | perl -nle 'printf "cp %s /var/tmp \n",$_'| sh -x
活用してみようー。
ではー。