LoginSignup
8
7

More than 3 years have passed since last update.

Perl One Linerのススメ

Last updated at Posted at 2019-05-13

TL;DR

自分はPerl One Linerを多用するが、使っているところを見られた時にだいたい「意味わからん」とかよく言われるので、なんとなく入門的なものを書こうかと。

  • どうしてPerl One Linerを使っているか
  • 基本的な使い方

あたりを、ざっくり簡単な書いていきます。

なぜPerl One Linerか

  • Linux環境なら、Perlはおよそインストールされているので、どこでも使える
  • 環境によって、正規表現の方言に悩まされない
    • 過去に実行環境におけるsedの拡張正規表現の方言に悩まされたこととかある
  • *Nixコマンド的に使えるので、パイプと組み合わせていろいろできて便利
  • その気になれば、Perlでできることはほぼ書ける
    • OSコマンド実行とか
    • ハッシュ使って集計とか

個人的な使い方

  • sedawkの代替として
  • 少し凝った条件で絞り込みをしたい時には、grepの代替として
    • findの結果を整形する
    • xargsをお供に

といった感じで。

即興でのログの集計や、findの結果をPerl One Linerで整形してコマンドを作って実行、などもよくやります。

基本形

次が、基本形です。

$ perl -w[なにか] -e '[One Line Program]' [ファイル名] [ファイル名]...

## ファイルを省略すると、標準入力から読み取る
$ ... | perl -w[なにか] -e '[One Line Program]'
  • -w … 警告を有効にする(より良いスクリプトを書く時には、基本お供で。use warnings;
  • -e … このオプションの後に、実行するスクリプトを指定する

「なにか」の部分には-p-nを指定します。

これは、ざっくり以下のイメージで実行されます。

while (<>) { ## 指定されたファイルの中身や、パイプで渡された標準入力の内容でループ
    # ここで、「-e」で指定したスクリプトが実行される
}

-p-nの差は、printが入るかどうかです。

これを、覚えておきましょう。

ちなみに、-eで指定するスクリプトは"(ダブルクォート)ではなく、'(シングルクォート)で囲うようにしましょう。$がPerlの前にシェルに評価されるのを抑止します。
※これを理解したうえで、意図的に使うこともたまにありますが…

ユースケースとともに、簡単な使い方を書いていってみましょう。

サンプルに、こんなテキストファイルを用意。

sample.txt

foo,bar
hoge,fuga
shell,shell

こちらを使って、少し例を書いていきます。

sedの代わりに

個人的には、1番よく使います。

$ perl -wp -e 's/[pattern]/[replace]/' [ファイル名]
$ ... | perl -wp -e 's/[pattern]/[replace]/'

# 「-i」を付けると、対象のファイル自体を書き換える
$ perl -wpi -e 's/[pattern]/[replace]/' [ファイル名]

# 「-i」に引数を追加すると、指定の拡張子でバックアップを作ってくれる
$ perl -wp -i.bk -e 's/[pattern]/[replace]/' [ファイル名]

-pを付けると、実行時にprintを含めるようになります。見た目、ほとんどsedですね。

例。

# 「foo」を「var」に置換
$ perl -wp -e 's/foo/var/' sample.txt 
var,bar
hoge,fuga
shell,shell


# gを使った例
# 「shell」という文字を、すべて「perl」に置換
$ perl -wp -e 's/shell/perl/g' sample.txt 
foo,bar
hoge,fuga
perl,perl

# gなしの場合
$ perl -wp -e 's/shell/perl/' sample.txt 
foo,bar
hoge,fuga
perl,shell

# ファイルそのものを書き換える場合(バックアップ付き)
$ perl -wp -i.bk -e 's/shell/perl/g' sample.txt
$ diff sample.txt sample.txt.bk 
3c3
< perl,perl
---
> shell,shell

awkの代わりに

awkcutの代わりとして。

$ perl -wnal -e 'print $F[...]' [ファイル名]
$ ... | perl -wnal -e 'print $F[...]'

# splitのパターンを指定するには、「-F」(正規表現可)を使う
$ perl -wnal -F'...' -e 'print $F[...]' [ファイル名]
$ ... | perl -wnal -F'...' -e 'print $F[...]'

-nprintが入らないので、自分で出力する必要があります。splitした結果は@F配列に入るので、$F[index]で指定します。

-lは、printに改行を追加するオプションです。

例。

$ perl -wnal -F',' -e 'print $F[0]' sample.txt 
foo
hoge
shell

grepの代わりに

printifの組み合わせで、grepのようにも使えます。

$ perl -wnl -e 'print if ...' [ファイル名]
$ ... | perl -wnl -e 'print if ...'

printの後ろには、$_(現在処理している行)が隠れています。

例。

$ perl -wn -e 'print if /foo|shell/' sample.txt 
foo,bar
shell,shell

$ perl -wn -e 'print if /foo/ and /bar/' sample.txt 
foo,bar

if文が使えるので、それなりに柔軟な表現が可能です。

grepよりもキータイプ数が増えてしまうのですが、grepだとANDを表現するには

$ grep ... | grep ... | grep ...

とパイプを介することになって、やややりづらいのと、数が増えてくるとプロセス数のオーバーヘッドも大きくなるのでPerl One Linerで書いた方が高速になります。

もちろん、「そんな複雑な条件をOne Linerで書くな」というのはもっともですが…。

ひとこと

このあたりのオプション(-p-n-a-l)とprintの組み合わせで書いていくのが、Perl One Linerの基本だと思います。

ファイルを直接読むだけではなくて、標準入力からも読み込めるので、あるコマンドの結果を整形するなどもよくやります。

Perl自体を知っていると、;で区切って複数の行を書いたり、変数を宣言できたりしていろいろ広がりますが、ほどほどに。

知っておくと便利なPerlの変数

他にもいろいろありますが、まずはこのくらい。

  • $_ … 現在処理している行の内容
  • $. … 現在処理している行番号

少し応用

たとえば、以下のようなアクセスログがあるとしましょう。

108.81.70.158 - - [16/Jun/2015:13:59:34 +0000] "GET /item/electronics/3717 HTTP/1.1" 200 86 "-" "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11"
176.33.96.225 - - [16/Jun/2015:13:59:35 +0000] "GET /category/cameras HTTP/1.1" 200 88 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11"
32.129.29.109 - - [16/Jun/2015:13:59:35 +0000] "GET /item/toys/2278 HTTP/1.1" 200 129 "/search/?c=Toys" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
192.75.60.93 - - [16/Jun/2015:13:59:35 +0000] "POST /search/?c=Giftcards+Electronics HTTP/1.1" 200 127 "/category/giftcards" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11"
72.42.173.76 - - [16/Jun/2015:14:01:35 +0000] "GET /category/software HTTP/1.1" 200 66 "-" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
168.180.150.153 - - [16/Jun/2015:14:01:35 +0000] "GET /category/books HTTP/1.1" 200 107 "/item/software/3528" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
172.171.186.182 - - [16/Jun/2015:14:15:35 +0000] "POST /search/?c=Software+Networking HTTP/1.1" 200 80 "-" "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11"
212.147.150.123 - - [16/Jun/2015:14:15:35 +0000] "GET /category/electronics HTTP/1.1" 200 49 "/category/books?from=20" "Mozilla/5.0 (Windows NT 6.0; rv:10.0.1) Gecko/20100101 Firefox/10.0.1"
140.210.67.42 - - [16/Jun/2015:15:00:35 +0000] "GET /item/electronics/1466 HTTP/1.1" 200 80 "/category/software" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0.1) Gecko/20100101 Firefox/9.0.1"
104.33.70.151 - - [16/Jun/2015:15:00:35 +0000] "GET /category/garden?from=10 HTTP/1.1" 200 89 "/item/garden/2967" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
152.90.29.208 - - [16/Jun/2015:15:30:35 +0000] "GET /category/electronics HTTP/1.1" 200 41 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0.1) Gecko/20100101 Firefox/9.0.1"

これは、こちらに記載されていたものを、少しいじったものです。

ここから、分あたりのアクセス数を出力してみます。

まず、アクセスログの時間の部分から、分までを抜き出します。

$ perl -wp -e 's/.+ \[([^:]+:\d\d:\d\d):\d\d \+\d+\] .+/$1/g' accesslog 
16/Jun/2015:13:59
16/Jun/2015:13:59
16/Jun/2015:13:59
16/Jun/2015:13:59
16/Jun/2015:14:01
16/Jun/2015:14:01
16/Jun/2015:14:15
16/Jun/2015:14:15
16/Jun/2015:15:00
16/Jun/2015:15:00
16/Jun/2015:15:30

あとは、sortuniqコマンドを組み合わせれば、あっさり出せたりします。

$ perl -wp -e 's/.+ \[([^:]+:\d\d:\d\d):\d\d \+\d+\] .+/$1/g' accesslog | sort | uniq -c
      4 16/Jun/2015:13:59
      2 16/Jun/2015:14:01
      2 16/Jun/2015:14:15
      2 16/Jun/2015:15:00
      1 16/Jun/2015:15:30

printする範囲を変えれば集計範囲が変わるので、例えば時間とパスを出力して、時間別のURLパス単位に集計することも可能です。

自分はたいてい、他のコマンドと組み合わせて使うことが多いのですが参考までに。
※とはいえ、この例だとsedでも十分ですね。

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7