Perlのよく使うワンライナー(テキスト処理)

今や懐かしいPerlのワンライナー。
古くなったとはいえ、ちょっとした、でもsedコマンドだけではできないような複雑なテキスト加工処理(CSVファイルも)には適している。
いろいろオプションはあるのだが、最小限に絞って備忘録的に。

よく使うオプション

基本

  • -e 'スクリプト': 実行するPerlスクリプトを指定(1行)
perl -e 'print "Hello"'

output:

Hello

入力が前提

  • -l:(入力から改行を取り除いたうえで最後に)出力結果を改行する。表示制御の目的で使われることが多い。データ加工の際は使わないほうが安全(使うとしても最後の処理として)。
perl -le 'print "Hello"'

output:

Hello
  • -n:ファイルを読み込んで1行ずつ(whileで)処理する。行の内容は特殊変数$_に格納される。
perl -ne 'func' file

perl -e 'open(FILE, "< file"); while(<FILE>) { func($_); }'

と同じことになる。

perl -ne 'print $.": ".$_' rand.txt

output:

1: 9
2: 4
3: 13
4: 53
5: 45
  • -p-nに加えて結果の$_printする。sed -eみたいなことをするときに使う。
perl -pe 's/[0-3]/A/g' rand.txt

output:

9
4
AA
5A
45
  • -0777:入力を行ごとに区切らない。-p-eと同時に用いられる。行ごとに変わるはずのない文字コード判別などの際に使う。
perl -MEncode::Guess -0777 -ne 'print guess_encoding($_, qw/shiftjis eucjp utf8/)->name' multibyte.txt

output:

utf8
  • -i ファイル名:ファイルの上書き
  • -i拡張子 ファイル名:ファイルの上書き&拡張子付きのバックアップファイル生成

-iオプションは末尾(スクリプトの後)に記述すること。それ以外の位置だとマルチバイト文字を含む場合などうまくいかないことがある。

perl -pe 's/[0-3]/A/g' -i.bak rand.txt

CSVの処理

  • -a:Delimiter-separated valuesの処理。値は@Fに格納される
  • -Fデリミタ:デリミタの指定(デフォルトの区切り文字はホワイトスペース)

行ループの-nと改行文字の削除-lをセットで使うのが普通なので、perl -lane 'コマンド'がセットになる。

perl -F, -lnae 'print $F[1]' log.txt

output:

20:03:06
20:03:07
20:03:07
perl -lnae '(@F[12..$#F] !~ /mobi/i && $F[8] != 200) && next; $,="\t"; print $F[0], $F[6]' ~/logs/access_log

output:

180.76.15.21    /
189.45.203.245  /user
220.181.108.160 /
185.92.73.125   /aaa/bbb.html

各オプションを実際のPerlのコードに対応させると

use strict; # -w
while(<STDIN>){ # -n
    chomp($_); # -l
    my @F = split(/ /, $_); # -a -F' '
    print $_; # -p
}

拡張機能

  • -Mモジュール名:外部モジュールを指定
$ perl -MLWP::Simple -e 'getprint "http://example.com/"'

output:

<!doctype html>
<html>
<head>
    <title>Example Domain</title>

    <meta charset="utf-8" />
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style type="text/css">
      :
</html>

ワンライナーではないが細かい処理でちょっと便利なメモ

常用対数を使いたいとき

log10(9999)はないのでlog(9999)/log(10)で代用する。

高度な算術演算子

  • 5 % 3: 剰余
  • 5 ** 3: べき乗

特殊変数

  • $.: 現在の行番号
  • $,: print引数のセパレータ
  • $/: 入力(open)時の行区切り文字
  • $\: 出力(print)時の行区切り文字
  • $!: エラーの内容

参考になるワンライナーの例

置換

バックスラッシュをエスケープ

perl -pe 's/\\/\\\\/g;' customer.dat1 > customer.dat2
perl -pe 's/\\/\\\\/g;' -i customer.dat1

perlでCSVファイルの操作

perl -F/,/ -lane 'print $F[1]' data.csv > out
perl -F/,/ -lane 'print @F[1,2]' data.csv > out

# 一部の列を抽出して置換し、タブ区切りテキストとして出力
perl -lnae '$F[5] =~ s/aid=(\w+).*bid=(\w+).*/$1\t$2/g; $F[10] =~ s/.*([fgh]id=[\w\-]+).*/$1/g; $,="\t"; print $F[5], $F[9], $F[10], $F[11]' 123456.log > 123456.txt

文字コード変換

EUC-JPからUTF-8に変換する場合。

perl -MEncode -pe '$_ = encode("utf8", decode("eucjp", $_))' data1.txt > data2.txt
perl -MEncode -pe 'Encode::from_to($_, "eucjp", "utf8");' data1.txt > data2.txt

encode()は戻り値として変換した文字列を返す関数。
from_to()$_自体を変換する関数。

ファイル自体を上書き(バックアップは保存)する

perl -MEncode -pe '$_ = encode("utf8", decode("eucjp", $_))' -i.bak data1.txt

その他の置換処理など続けて記述することができる。

perl -MEncode -pe '$_ = encode("utf8", decode("eucjp", $_)); $_ =~ s/\r\n/\n/g;' -i.bak data1.txt

文字コード判別

perl -MEncode::Guess -0777 -ne 'print guess_encoding($_, qw/shiftjis eucjp utf8/)->name' data1.txt

現在のディレクトリ直下の全ファイルに対して文字コード自動判別からのUTF-8への置換と改行コード置換

perl -MEncode -MEncode::Guess -0777 -pe '$_ = encode("utf8", decode(guess_encoding($_, qw/shiftjis eucjp/)->name, $_)); $_ =~ s/\r\n/\n/g;' -i.bak ./*

参考ページ

ワンライナーそのもの

ワンライナーではないが参考に