概要
表題の通り。Perlのワンライナーを使ったちょっとした遊びです。
Perlオプション
-n:引数で指定したファイルの中身を一行ごと$_に渡す
-p:-nの処理に加えprintまで行う
-a:n, pと一緒に用いると、区切り文字(デフォルト空文字)でsplitされ配列@Fに格納される
-F:区切り文字を変更します
-e:実行するコードを指定
-l:print時に自動で行末処理をする(行ごとに改行される)
-i:指定した文字列を末尾に付与したバックアップファイルを作成する
-w:use warningsに相当
cat
- 例
> cat test.txt
aa bb cc dd ee
11 22 33 44 55
99 88 77 66 55
# ファイルの内容が一行ごとに$_に入りコード実行が繰り返される
> perl -wnle 'print;' test.txt
aa bb cc dd ee
11 22 33 44 55
99 88 77 66 55
# -pを指定した場合、コード内にprintを書かなくてもprintされる
> perl -wple '' test.txt
aa bb cc dd ee
11 22 33 44 55
99 88 77 66 55
grep
- 例
> cat test.html
<html>
<body>
Sample HTML
<table border="1">
<tr>
<th>No</th>
<th>Name</th>
<th>Site</th>
</tr>
<tr>
<td>1</td>
<td>Yahoo</td>
<td>https://www.yahoo.co.jp</td>
</tr>
<tr>
<td>2</td>
<td>Google</td>
<td>https://www.google.com</td>
</tr>
</table>
</body>
</html>
# 文字列検索('table'を検索)
> perl -wnle '/table/ and print;' test.html
<table border="1">
</table>
# '/'を含む文字列検索
> perl -wnle 'm|https?://| and print;' test.html
<td>https://www.yahoo.co.jp</td>
<td>https://www.google.com</td>
# ’<’を含まない行を検索
# grep -v '<' に相当
> perl -wnle '/</ or print;' test.html
Sample HTML
sed
- 例
> cat test.html
<html>
<body>
Sample HTML
<table border="1">
<tr>
<th>No</th>
<th>Name</th>
<th>Site</th>
</tr>
<tr>
<td>1</td>
<td>Yahoo</td>
<td>https://www.yahoo.co.jp</td>
</tr>
<tr>
<td>2</td>
<td>Google</td>
<td>https://www.google.com</td>
</tr>
</table>
</body>
</html>
# 置換してprint
> perl -wpe 's/table/test/g' test.html
<html>
<body>
Sample HTML
<test border="1">
<tr>
<th>No</th>
<th>Name</th>
<th>Site</th>
</tr>
<tr>
<td>1</td>
<td>Yahoo</td>
<td>https://www.yahoo.co.jp</td>
</tr>
<tr>
<td>2</td>
<td>Google</td>
<td>https://www.google.com</td>
</tr>
</test>
</body>
</html>
# -iオプションでbackupを取ってからファイルを置換
> perl -i'.bak' -wpe 's/table/test/g' test.html
> ls
test.html test.html.bak
> diff -u test.html test.html.bak
--- test.html 2019-09-24 15:07:46.000000000 +0900
+++ test.html.bak 2019-09-24 14:31:52.000000000 +0900
@@ -1,7 +1,7 @@
<html>
<body>
Sample HTML
- <test border="1">
+ <table border="1">
<tr>
<th>No</th>
<th>Name</th>
@@ -17,6 +17,6 @@
<td>Google</td>
<td>https://www.google.com</td>
</tr>
- </test>
+ </table>
</body>
</html>
awk
- 例
# 1列目と2列目を足し算する(1行目は文字列なのでwarningが出る)
# awk '{print $1 + $2}' test.txt に相当
> cat test.txt
aa bb cc dd ee
11 22 33 44 55
99 88 77 66 55
> perl -wnale '($first, $second)=@F;print $first + $second;' test.txt
Argument "bb" isn't numeric in addition (+) at -e line 1, <> line 1.
Argument "aa" isn't numeric in addition (+) at -e line 1, <> line 1.
0
33
187
# 区切り文字(デフォルト空白)を','にしてawk実行
# awk -F',' '{print $1 + $2}' test.csv に相当
> cat test.csv
aa,bb,cc,dd,ee
11,22,33,44,55
99,88,77,66,55
> perl -wnalF',' -e '($first, $second)=@F;print $first + $second;' test.csv
Argument "bb" isn't numeric in addition (+) at -e line 1, <> line 1.
Argument "aa" isn't numeric in addition (+) at -e line 1, <> line 1.
0
33
187
# 行末の列番号表示
# awk '{print NF}' test.txt に相当
> perl -wnale '$num=@F; print $num;' test.txt
5
5
5
# 行末の列の値を表示
# awk '{print $NF}' test.txt に相当
> perl -wnale '$num=@F; print $F[$num-1];' test.txt
ee
55
55
# backupファイルを作成した上で特定の列情報でファイルを上書きする
# 下記の例だと$first変数を使用していないのでwarningが出る
> perl -i.bak -wnale '($first, $second)=@F;print $second;' test.txt
Name "main::first" used only once: possible typo at -e line 1.
> cat test.txt
bb
22
88
感想
cat, grep, awkでいいやん!ってなるけど、Perl使いの人は全てをperlでやる方が効率が良い、のかもしれない。
Perlが手慣れているなら、awkを新たに覚えるより良いのかもしれない。ちょっとしたログ調査とかだったらawkで十分だけど、ちょっと手の込んだことやろうと思うとPerlの方が良いときもあるかもね!
参考
- perldoc
- ミニマルPerl ―Unix/LinuxユーザのためのPerl習得法(絶版本ですが...)