PerlTips(コマンドラインオプション)【閉鎖されたページを焼き直し】


毛利です。

以下、非常に重宝してたのだけど Not Found になったので、引用しながら検証。
(オリジナルを書いた方、問題あれば連絡ください)

ちなみに Internet Archiveこちらから。

Perl には、何気に便利なコマンドラインオプションがたくさんあります。そのいくつかをご紹介いたします。特に -i オプションはとても便利ですので、最低でもそこまでは読み進めていただければ幸いです。

なお、オプションの一覧は perl -h で見ることができます。ここでは、

perl -v

This is perl, version 5.005_03 built for i386-linux

というバージョンの Perl について書きます。

【加筆】本記事では環境はバラバラです。会社(Linux)と自宅(Mac OS X)でちょっとずつ書いたので。。


■-e オプション

-e オプションは、指定された文字列をプログラムとして実行し、すぐに Perl を終了するオプションです。

perl -e 'print "hogehoge"'

として実行すれば、画面に hogehoge が出力されます。

このオプションを指定した場合は、perl コマンドの引数としてプログラムを指定することはできません。これ単体ではあまり大した価値はないのですが、後に紹介するオプションと組み合わせて利用します。


【加筆】実行結果例

# 改行なしでほげほげ、改行ありでほげほげ2連発
# perl -e 'print "hogehoge"'
hogehoge#  perl -e 'print "hogehoge\n"'
hogehoge
# perl -e 'print "hogehoge", "\n"'
hogehoge
# 


【加筆】ちなみに、以降のオプションと組み合わせる場合、-e オプションを最後に持ってくる必要があるようです。(-ne → ◯、-en →× )

■-n オプション、-p オプション

-n オプションは、与えたプログラムの外側に下のようなループがあるのと同じような動作をします(実際に等価な処理は少し違います)。

while(<>) {
....
}


「<>」は、ダイアモンド演算子と呼ばれ、「標準入力から入力が与えられた場合はそちらを読み込み、ファイル名が引数として与えられた場合はファイルから読み込む」というとても便利な演算子です。

-p オプションも同様ですが、ループブロックの内側の最後に print $_; があるように振舞います。つまり、こんな感じです(実際に等価な処理は少し違います)。

while(<>) {
....
print $_;
}


例えば、
perl -p -e 'print ++$i, ": "' /etc/passwd

とすれば、/etc/passwd に行番号をつけて出力することができますし、


perl -p -e 's/^[^:]+/\*\*\*/;' /etc/passwd

とすれば、/etc/passwd のユーザ名を *** に置き換えて出力できます。 BEGIN { } と END { } で、ループ前後の処理を記述することが可能です。


perl -p -e 'BEGIN{ print "-- /etc/passwd --\n";} END {print "done.\n";}' /etc/passwd

とすると、「-- /etc/passwd --\n」を print してから、/etc/passwd を一行ずつ print し、最後に「done.\n」を print します。



perl -p -e 'BEGIN{$i=1;} print $i++, ": "' /etc/passwd

という感じですね。


【加筆】実行結果例

# 番号(++$i の部分)付きで出力
# perl -p -e 'print ++$i, ": "' /etc/passwd
1: root:x:0:0:root:/root:/bin/bash
2: bin:x:1:1:bin:/bin:/sbin/nologin
3: daemon:x:2:2:daemon:/sbin:/sbin/nologin
4: adm:x:3:4:adm:/var/adm:/sbin/nologin
:

【加筆】実行結果例

# 最初(BEGINブロックで処理)と最後(ENDブロックで処理)にそれぞれ「-- /etc/passwd --」「done.」を出力
# perl -p -e 'BEGIN{ print "-- /etc/passwd --\n";}  END {print "done.\n";}' /etc/passwd
-- /etc/passwd --
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
:
oracle:x:502:502::/home/oracle:/bin/bash
postgres:x:26:26:PostgreSQL Server:/var/lib/pgsql:/bin/bash
done.

■-l オプション

このオプションを -p や -n と同時に用いると、-p や -n オプションのループブロック内の最初でchomp が行われます(実はもう一つ用法がありますが、割愛します)。 つまり、

while(<>) {
chomp($_);
....
}

と同様です(-n と同時に用いた場合。なお、実際に等価な処理は少し違います)。


【加筆】実行結果例(-l を付加した場合)

# perl -n -l -e 'BEGIN{$i=1;} $_ = "***$_***"; print $i++, ": ", $_' /etc/passwd
1: ***root:x:0:0:root:/root:/bin/bash***           # print に \n をつけてないのに改行されてる 
2: ***bin:x:1:1:bin:/bin:/sbin/nologin***
3: ***daemon:x:2:2:daemon:/sbin:/sbin/nologin***
:
# 
  • 「実はもう一つ用法」というのは、print に "\n" を付加してくれるようです

【加筆】実行結果例(-l を付加しない場合)

# perl -n -e 'BEGIN{$i=1;} $_ = "***$_***"; print $i++, ": ", $_' /etc/passwd
1: ***root:x:0:0:root:/root:/bin/bash           # $_ に改行コードが入っている
***2: ***bin:x:1:1:bin:/bin:/sbin/nologin     # *** の後に改行がない
***3: ***daemon:x:2:2:daemon:/sbin:/sbin/nologin
:
*** #

■-i オプション

このオプションと今までご紹介したオプションを使用すると、とても便利なことができます。一番お伝えしたかったのはここになります。 例えば、HTML ファイルが 100 コあるとします。そのなかで、ある要素の color 属性値に #ffffdd という色を指定していたとします。しかし、すべてを #aaaacc に色変更したい、といった要望があったとします。そういう場合に、すべてエディタで置換するのは大変です。そこで、


perl -i.bak -p -e 's/#ffffdd/#aaaacc/ig;' *.html


とすると、オリジナルファイルが .bak という拡張子をつけてバックアップとしてコピーされ、オリジナルファイルの #ffffdd が #aaaacc に置換されます。上記のようにワイルドカード(*)で指定すれば、100 個のファイルすべてを一気に置換することができます。便利ですよね?

なお、-i オプションの後ろの拡張子を指定しない場合は、バックアップファイルは作成されません。


【加筆】実行結果例

# テスト用に3行のみのファイルを事前に作成
# ls passwd_3line*
passwd_3line
# cat passwd_3line
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
# perl -i.bak.$(date '+%Y%m%d') -p -e 's/nologin/tcsh/' passwd_3line
# バックアップファイルと作成したファイルを比較
# diff passwd_3line.bak.20120528 passwd_3line
2,3c2,3
< bin:x:1:1:bin:/bin:/sbin/nologin
< daemon:x:2:2:daemon:/sbin:/sbin/nologin
---
> > bin:x:1:1:bin:/bin:/sbin/tcsh
> > daemon:x:2:2:daemon:/sbin:/sbin/tcsh
# ls passwd_3line*
passwd_3line
# cat passwd_3line
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
# バックアップファイルを作成せず上書き
# perl -i -p -e 's/nologin/tcsh/' passwd_3line
# !cat
cat passwd_3line
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/tcsh
daemon:x:2:2:daemon:/sbin:/sbin/tcsh
# ls passwd_3line*
passwd_3line


私は ed に馴染めないので perl が便利。



■-a オプション、-F オプション

-a オプションを指定すると、入力行($_)が自動的に split され、結果が配列 @F にセットます。つまり、

perl -a -n -e 'print join("," , @F), "\n";' /etc/services


は、下記と同様です。

while(<>) {
@F = split(' ');
print join("," , @F), "\n";
}


ps axuwww | perl -n -e '@F = split(" "); print join("," , @F), "\n";'


当然、こんな感じでの利用もできます。

  • F オプションに続いて split の区切り文字を指定することもできます。

perl -F':' -a -n -e 'print join("," , @F), "\n";' /etc/passwd

といった感じです。

【加筆】私は、CSVデータのある列の値を変える、という場合に使用してます。

例えば

perl -F':' -a -n -l -e 'print join("," , @F);' /etc/passwd

だと /etc/passwd をコンマ区切りで出力してくれるだけであまり面白みはないですが、

/usr/bin/false を "CHANGE" に変更して出力したい場合は
# perl -F':' -anle '$F[6]=~s[/usr/bin/false][CHANGE];  print join("," , @F);' /etc/passwd
nobody,*,-2,-2,Unprivileged User,/var/empty,CHANGE
root,*,0,0,System Administrator,/var/root,/bin/sh
daemon,*,1,1,System Services,/var/root,CHANGE
:

などとします。

他にもx番目の列の値を変えたいとか。(ここではユーザ名を伏せる例を書きます)
# perl -F':' -anle '$F[0]="XXXX";  print join("," , @F);' /etc/passwd
XXXX,*,-2,-2,Unprivileged User,/var/empty,/usr/bin/false
XXXX,*,0,0,System Administrator,/var/root,/bin/sh
XXXX,*,1,1,System Services,/var/root,/usr/bin/false
:
あるカラムを1000倍にする。
# perl -F':' -anle '$F[2]*=1000;  print join("," , @F);' /etc/passwd
nobody,*,-2000,-2,Unprivileged User,/var/empty,/usr/bin/false
root,*,0,0,System Administrator,/var/root,/bin/sh
daemon,*,1000,1,System Services,/var/root,/usr/bin/false
:

■-c オプション

-c オプションを利用すると、Perl プログラムを実際には実行せずに、構文チェックだけを行うことができます(BEGIN、END ブロックと use は実行されるようです)。

■-w オプション

-w オプションを利用すると、一度しか出現しない識別子や、初期化されずに参照される変数など、危険、無駄と思われる処理の警告を出力します。実際にはもっとたくさんの種類のチェックをするようです。

最後の2つ(-c, -w)はほとんど使いませんな…。
スクリプト書くときは use strict, warning をつける)


手元にないけど、この辺に書いてあったりするかも。




いじょ。