部屋を掃除したら漫画が沢山出てきたので書く日記

漫画とか合唱とかUNIXとかLinuxとかについて書く日記です。

二つのファイルの行を一つの行にまとめて出力する

二つのファイルの行を一つの行に、たとえばタブ区切りで並べて出力できればグラフにする時に便利だなあとずっと思っていたので、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
:

になります。素晴らしい。

・・・と、自己満足に浸っていたところ、LinuxSolarisの標準コマンドで同じ機能が実現されている事を知りました。

ここここで説明されている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

活用してみようー。
ではー。