Perlワンライナー集

障害対応でのログ解析、ソースコードの調査といったテキスト処理で使った Perl ワンライナー集です。
マルチライナーやいけてないのもありますw


Perl ワンライナーの好きなところ

  • PerlOracle Database (10g以降) に同梱されているので、Windows プラットフォームでも使える*1
  • awksedgrep正規表現の書き方などをそれぞれ覚えれない。awksedgrep でできることはだいたい Perl でできるので、Perl に絞ると覚えることを減らせる*2
  • 最小限の労力で最大限の仕事ができる。ちょっとしたプログラムを書くような処理でも Perl ワンライナーを使うとたった1行で済むことがあります*3

Perlワンライナー

一部、Perl 以外に bash、find、xargs なども含んでいます。

  • レコードセパレータを変更する
perl -wple ...       # 行モード(デフォルト)
perl -00 -wle ...    # 段落モード(1つ以上の空行をレコードセパレータと認識する)
perl -0777 -wle ...  # ファイルモード(ファイル全体を1レコードとして認識する)
  • カスタムフィールドセパレータを使う(改行コードをフィールドセパレータとする)
perl -00 -F'\n' -lane 'print $F[1] if($F[0] =~ /neo/)' hoge.txt
perl -lne 'print if $.<2' file  # 1行目だけ表示する
perl -pe 'exit if $. > 10' file # 10行目まで表示する
perl -ne 'print if 2.. 5' file  # 2行目から5行目まで表示する
  • CSV の列数をカウントする
perl -F, -lane 'printf("%s:%d\n",$ARGV,$#F);$.>0 and close ARGV' *.csv
  • CSV の任意の列のみ抽出する(1列目、3列目、11列目以外を抽出する)
perl -F, -lane 'print join(",",@F[1,3..9,11..$#F])' *.csv
  • ファイル中の空行を削除する
perl -i.org -ne '/^\s*$/ or print' test.sql
  • ダブルクオートをシングルクオートに置換する
perl -ple 's/\"/\'/g' hoge.csv
  • 「ORA-」メッセージをエラー番号別に集計する
perl -nle 'BEGIN{%h=();}/(ORA-[0-9]+)/ and $h{$1}++;END{map{print qq/$_:$h{$_}/} keys %h;}' alert_orcl.log
  • Java アプリケーションのログから発生した Exception の回数を集計する
perl -wnle 'BEGIN{%h=();}/([a-zA-Z]*Exception)/ and $h{$1}++;END{map{print "$_:$h{$_}"} sort keys %h;}' *.log
  • リスナーログから接続元ホスト毎に接続回数を算出する
perl -nle 'if(/(\d{2}-\w{3}-\d{4} \d{2}:\d{2}:\d{2}).*CONNECT_DATA.*HOST=([\w\-\.]+).*HOST=([\w\-\.]+)/i){$h->{qq/$2,$3/}->{count}++;$h->{qq/$2,$3/}->{date}=$1};END{map{print qq/$_,$h->{$_}->{count},$h->{$_}->{date}/} keys %$h}' listener.log
  • 再帰的にファイル名に接頭辞をつける
perl -MFile::Find -e 'find sub{rename($_,"prefix_$_") if -f}, @ARGV' .
perl -nle '$a=length if($a<length);END{print $a}' orcl_ora_879.trc 
perl -MList::Util=max -lne 'push(@a,length);END{print max(@a)}' orcl_ora_879.trc 
perl -0777 -MEncode::Guess -wne '$e=guess_encoding($_,qw/euc-jp shiftjis 7bit-jis/);print "$ARGV:".$e->name."\n" if(ref($e))' **/*
 ||<
-任意のディレクトリ配下にどのような拡張子のファイルがどれだけあるか集計する
>|perl|
perl -MFile::Find -MFile::Basename -e 'find sub{$h{(fileparse($_,qw{\.[^\.]+$}))[2]}++ if -f},@ARGV;END{map{print "$_:$h{$_}\n"}keys %h}' .
  • 2つ以上の空白文字は1つにするがシングルクオートで囲まれたリテラルは無視する
perl -pe 's/\G((\x27[^\x27]*\x27[^\x27]*?)*[^\x27]*?)[ ]+/$1 /g' foo.c
  • レコードセパレータが1つ以上の空行で、レコード内に改行文字を1つ含むデータを1行1レコードに変換する
perl -i.org -00 -pe 's/^(.*)\n(.*)\n+$/$1 $2\n/' aaa.txt
  • 1ファイル中に「DECODE」と「DISTINCTまたはUNIQUE」を含みかつ「ORDER BY」を含まないファイルをリストアップする
find . -type f -print0|xargs -0 perl -0777 -nle '/decode/i and /distinct|unique/i and !/order\s*by/i and print qq/$ARGV:$./;eof and close ARGV'
find . -type f -name '*.sh' -print0|xargs -0 perl -i.org -0777 -pe 's/(\s*#![\w\s\/]+\/bash)/$1\nexport LANG=C\n/'
  • ASCIIコードを文字に変換する
perl -e 'map{print chr($_)} @ARGV' 112 101 114 108  # ASCIIコードが10進数の場合
perl -e 'map{print chr(hex($_))} @ARGV' 70 65 72 6C # ASCIIコードが16進数の場合
  • メモリを大量消費する
perl -e 'while(1){$i++;$h{$i}=$i}' 
perl -e 'while(1) {}'
for i in {1..4}
do
perl -e 'while(1){}' &
done
  • iostat の出力結果を特定の列でソートするこの例では、デバイスを sdk だけに絞って、(11列目) でソートしています。
perl -lane '/^sdk/ and push(@tmp,[@F]);END{map{print join(qq/ /,@{$_})}sort{$a-[11]<=>$b->[11]}@tmp}' iostat.log
  • V$SYSSTAT から特定のデータベース統計情報の差分を出す
perl -F, -lane '/global cache blocks lost/ and printf(qq/%s,%s\n/,$F[0],$F[2]-$tmp) and $tmp=$F[2]' sysstat.log
  • V$SYSSTAT から特定の統計に絞らずにデータベース統計情報の差分を出す
perl -F, -lane 'printf(qq/%s,%s,%s\n/,$F[1],$F[0],$F[2]-$h{$F[1]})if(exists($h{$F[1]}));$h{$F[1]}=$F[2]' sysstat.log
  • vmstat の出力結果から時間帯毎のCPU使用率を算出する
perl -lane '$.>2 and @t=split(q/:/,$F[1]) and $h->{$t[0]}->{sum}+=$F[21]+$F[22] and $h->{$t[0]}->{cnt}++;END{map{printf(qq/%02d\t%.1f\n/,$_,$h->{$_}->{sum}/$h->{$_}->{cnt})}sort keys %$h}' vmstat.log
  • top の行頭に時刻を追加
perl -ne '/^top - (\d\d:\d\d:\d\d)/ and $t=$1;print qq/$t $_/' top.log > top_time.log
  • 2行にまたがっている iostat のログを1行にする
perl -pe '/sssn[0-9]+s:/ and chop' iostat.txt > iostat_tmp.txt
perl -pe 's/(sssn[0-9]+s:[\/\w]+)iostat\s[0-9\/]{10}\s[0-9:]{8}/$1/' iostat_tmp.txt > iostat_mod.txt
perl -i -MEncode -pe 'Encode::from_to($_,"utf8","shiftjis");' *.txt"
  • ファイル名一括置換
perl -0777 -ne '$o=$ARGV;$ARGV=~s/\s/-/g;rename($o,$ARGV);' *.jpg
  • 16進数から10進数に変換
perl -e 'map{print hex($_)} @ARGV' 4a98
  • 10進数から16進数に変換
perl -e 'printf qq/%X/, 19096'
perl -MTime::HiRes -e 'while(1){for(1..10000){};Time::HiRes::sleep(0.0001)}'
  • strace をグラフ化するために加工する
perl -lane '$F[2]=~s/^([a-z]+).*/$1/; $h->{$F[1]}->{$F[2]}++;END{map{$t=$_ and print $t;map{print qq/$t\t$_\t$h->{$t}->{$_}/} keys %{$h->{$_}}} keys %$h}'' strace.log
  • top を時系列・PID別にグラフ化するために加工する
grep -hA 20 '^top -' top.logperl -lane '/top - ([\d:]+)/ and $t=$1;$F[0]=~/\d+/ and printf (qq/%s %s%s\n/,$t,$_,lc(sprintf(q/%X/,$F[0])))'
  • 素のASHをCSVなどデリミタ区切りに変換する
perl -lane '/^-+/ and map {$l=length($_);$l++;print qq/a$l/} @F' ash.lst
a11a11a76a2a11a16a11a11a11a14a2a17a11a25a14a21a65a20a17a31a31a12a9a22a26a16a20a15a14a19a11a65a11a11a11a65a11a65a11a65a11a65a14a11a8a12a12a17a25a17a2a13a14a15a13a16a65a18a17a17a11a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a13a49a65a65a65a65a11a65a17a22a14a18a17a11a23a24a20a21a28a14a21a1001
perl -nle '@s=unpack(q/a11a11a76a2a11a16a11a11a11a14a2a17a11a25a14a21a65a20a17a31a31a12a9a22a26a16a20a15a14a19a11a65a11a11a11a65a11a65a11a65a11a65a14a11a8a12a12a17a25a17a2a13a14a15a13a16a65a18a17a17a11a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a13a49a65a65a65a65a11a65a17a22a14a18a17a11a23a24a20a21a28a14a21a1001/, $_);@t=map{$_=~s/^ *(.*?) *$/$1/;$_} @s;print join(q/|/, @t)' ash.lst > ash_mod.txt 
$ perl -MTime::Piece -e 'printf(qq/%08d %08d %08d %08d %s\n/,$_,2..4,$t=localtime->datetime) for 1..50000000'
00000001 00000002 00000003 00000004 2015-07-28T14:23:18
00000002 00000002 00000003 00000004 2015-07-28T14:23:18
00000003 00000002 00000003 00000004 2015-07-28T14:23:18
perl -lane '($s)=$F[1]=~/^(^\w+).*/;($t)=$F[$#F]=~/<([0-9\.]+)>/;print qq/$F[0]\t$t\t$s\t@F[1..$#F-1]/' strace.13859 > strace.13859.tsv

*1:Oracle Database使い向け

*2:記憶力がよくない人向け

*3:Perlに限りませんが