コマンドの達人を目指すために、避けては通れないプログラミング言語はたくさんあると思いますが、私はPerlで育ったエンジニアなので、Perlの学習は外せないと考えています。
Perlは、Linuxディストリビューションには標準で入っていますので、インストール不要ですが、apt(apt-get) install perl(Debian系)や、yum install perl(Red Hat系)ですぐに利用できます。
Contents
Perl入門
Perlを実行する(まずはHello World)
Perlはperlコマンドで実行しますが、通常Perlで書かれたスクリプトファイルを引数に与えます。
しかし-eオプションを使えば、スクリプトファイルを用意しなくともperlを実行することができます。
-eに続けて”で囲んだPerlスクリプトを書けばよいです。
takk@deb9:~/pl$ perl -e 'print "Hello World!\n"' Hello World! takk@deb9:~/pl$
パイプでperlへ標準入力させることができます。
-nオプションを使うと、標準入力の各行をすべて繰り返し処理してくれます。
$_は、標準入力の各行の文字列です。改行を含みます。
takk@deb9:~/pl$ seq 10 | perl -ne 'print "[PERL]$_"' [PERL]1 [PERL]2 [PERL]3 [PERL]4 [PERL]5 [PERL]6 [PERL]7 [PERL]8 [PERL]9 [PERL]10 takk@deb9:~/pl$
$_に格納されているのは、標準入力の各行なので文字列なんですが、そのまま数値計算することもできます。各行の数字に10を足して表示してみます。
takk@deb9:~/pl$ seq 10 | perl -ne '$_ += 10;print "[PERL]$_"' [PERL]11[PERL]12[PERL]13[PERL]14[PERL]15[PERL]16[PERL]17[PERL]18[PERL]19[PERL]20takk@deb9:~/pl$
perlに読み込まれた直後の$_は改行を含んでいましたが、10を足した後の$_は、改行を含んでいませんので注意が必要です。
printする時に改行を含んでみましょう。
takk@deb9:~/pl$ seq 10 | perl -ne '$_ += 10;print "[PERL]$_\n"' [PERL]11 [PERL]12 [PERL]13 [PERL]14 [PERL]15 [PERL]16 [PERL]17 [PERL]18 [PERL]19 [PERL]20 takk@deb9:~/pl$
最初に改行を削除して、最後に改行をつける癖をつけても良いかもしれません。
改行を削除するにはchompを使います。chompを実行すると、$_中の最後の改行が削除されます。
takk@deb9:~/pl$ seq 10 | perl -ne 'chomp;print "[$_]\n"' [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] takk@deb9:~/pl$
Perlで検索する
私はPerlの素晴らしさは、とことん簡略化できるところと、その言語仕様に正規表現が組み込まれているところにあると考えてます。ならば入門として最初に覚えるべきことは、正規表現を使うことでしょう。
ということで、grepでやれるような検索を、Perlでもやってみましょう。その前にperlで検索するための実験用ファイル作成しておきます。
takk~$ seq 50 | pr -t5 | tee test.txt 1 11 21 31 41 2 12 22 32 42 3 13 23 33 43 4 14 24 34 44 5 15 25 35 45 6 16 26 36 46 7 17 27 37 47 8 18 28 38 48 9 19 29 39 49 10 20 30 40 50 takk~$
ではPerlでgrepしてみます。
takk~$ perl -ne 'if(/24/){ print }' test.txt 4 14 24 34 44 takk~$
test.txt内で”24″が含まれている行を抽出しました。
書式 perl -ne ‘スクリプト’
-n ファイルの毎行を処理するオプション
-e スクリプトを指定するオプション
(-n と -eオプションを続けて-neと書くことができます。)
スクリプトは、下のように改行を入れて見やすく書くこともできます。
takk~$ perl -ne ' > if(/24/){ > print; > } > ' test.txt 4 14 24 34 44 takk~$
if文は倒置することで{}を使わずに記述できます。
takk~$ perl -ne 'print if/24/' test.txt 4 14 24 34 44 takk~$
先ほどと同じことができました。
「if(/24/){print}」こう書いても、
「print if/24/」こう書いても同じ判定をしてくれます。
Perl 変数
Bashの変数は代入時は$をつけず参照時に$がつくのに対して、Perlの変数名は常に頭に$がつきます。
Bash
takk@deb9:~/pl$ a=10 takk@deb9:~/pl$ echo $a 10 takk@deb9:~/pl$
Perl
takk@deb9:~/pl$ perl -e '$a=10;print"$a\n"' 10 takk@deb9:~/pl$
変数は数値でも文字列でも同じです。文字列の結合は .(ドット)でできます。
C言語風に、
$a = $a + 数値;
の場合、
$a += 数値;
に省略で、
$a = $a . 文字列;
の場合、
$a .= 文字列;
というように省略できます。
takk@deb9:~/pl$ perl -e '$a=10;$a += 10;$a .= " HELLO";print"$a\n"' 20 HELLO takk@deb9:~/pl$
ただし$がつくのはスカラー変数です。値が一つしか格納できません。
複数のスカラー値を格納するには、配列を使います。
配列は@から始まる変数名で表します。
takk@deb9:~/pl$ perl -e '@a=1..5;print "@a\n"' 1 2 3 4 5 takk@deb9:~/pl$
配列の各要素にアクセスするには、$変数名[インデックス]を指定します。
インデックスは0はじまりです。
takk@deb9:~/pl$ perl -e '@a=1..5;print"$a[4]\n";' 5 takk@deb9:~/pl$
Perl 配列操作
Perl配列操作です。
Perl 配列初期化
まずは配列の初期化。空の()を代入すればよいです。
takk@deb9:~/pl$ perl -e '@a=1..1;@a=();print "@a\n"'; takk@deb9:~/pl$
Perl 文字列の配列初期化
文字列の配列はクォートで区切る方法と、
takk@deb9:~/pl$ perl -e '@a=("aa","bb","cc");print "@a\n"'; aa bb cc takk@deb9:~/pl$
qw演算子を使ってスペース区切りにする方法があります。
takk@deb9:~/pl$ perl -e '@a=qw(aa bb cc);print "@a\n"'; aa bb cc takk@deb9:~/pl$
次は要素の取出しです。
Perl pop 末尾要素の取り出し
添え字でインデックスを指定して要素を取得することができますが、配列を操作して要素を取り出すことができます。
popは配列の最後の要素を取り出します。
takk@deb9:~/pl$ perl -e '@a=1..5;$b=pop @a;print "$b\n@a\n"'; 5 1 2 3 4 takk@deb9:~/pl$
Perl shift 先頭要素の取り出し
shiftは配列の先頭の要素を取り出します。
takk@deb9:~/pl$ perl -e '@a=1..5;$b=shift @a;print "$b\n@a\n"'; 1 2 3 4 5 takk@deb9:~/pl$
Perl push 末尾に要素の追加
では要素の追加です。
配列の最後に要素を追加するには、pushを使います。
takk@deb9:~/pl$ perl -e '@a=1..5;push @a,10;print "@a\n"'; 1 2 3 4 5 10 takk@deb9:~/pl$
Perl unshift 先頭に要素の追加
配列の先頭に追加するにはunshiftを使います。
takk@deb9:~/pl$ perl -e '@a=1..5;unshift @a,10;print "@a\n"'; 10 1 2 3 4 5 takk@deb9:~/pl$
アセンブリ言語の経験があると、pushが追加でpop取出しと想像つきますが、
shiftとunshiftがどちらがどちらか毎度迷います。
私は、単語の文字数が多い方が追加と覚えてます。
push 追加
pop 取出し
unshift 追加
shift 取出し
Perl splice 途中の要素の取り出し
要素の途中から一気に取り出すにはspliceを使います。spliceの第二引数が0はじまりのインデックスになります。
takk@deb9:~/pl$ perl -e '@a=10..15;@b=splice @a,3;print "@a\n@b\n"'; 10 11 12 13 14 15 takk@deb9:~/pl$
Perl 連想配列/ハッシュ/辞書
連想配列です。Perlではハッシュと呼びます。
ハッシュ変数は%が頭につきます。
takk@deb9:~/pl$ cat test.pl %a = ( 'dog' => '犬', 'cat' => '猫', 'fox' => '狐' ); print $a{'dog'} . "\n"; takk@deb9:~/pl$
対応する要素が取得できます。
takk@deb9:~/pl$ perl test.pl 犬 狐 猫 takk@deb9:~/pl$
=>は、,カンマと同じなので、カンマで書くこともできます。
takk@deb9:~/pl$ cat test.pl %a = ( 'dog' , '犬', 'cat' , '猫', 'fox' , '狐' ); print $a{'dog'} . "\n"; print $a{'fox'} . "\n"; print $a{'cat'} . "\n"; print $a{'bird'} . "\n"; takk@deb9:~/pl$ perl test.pl 犬 狐 猫 takk@deb9:~/pl$
キーの文字列は、クォートを省略できます。
takk@deb9:~/pl$ cat test.pl %a = ( dog , '犬', cat , '猫', fox , '狐' ); print $a{'dog'} . "\n"; print $a{'fox'} . "\n"; print $a{'cat'} . "\n"; print $a{'bird'} . "\n"; takk@deb9:~/pl$ perl test.pl 犬 狐 猫 takk@deb9:~/pl$
値の文字列はクォートを省略できません。
takk@deb9:~/pl$ cat test.pl %a = ( dog , 犬, cat , 猫, fox , 狐 ); print $a{'dog'} . "\n"; print $a{'fox'} . "\n"; print $a{'cat'} . "\n"; print $a{'bird'} . "\n"; takk@deb9:~/pl$ perl test.pl Unrecognized character \xE7; marked by <-- HERE after = ( dog , <-- HERE near column 14 at test4.pl line 1. takk@deb9:~/pl$
keysとvaluesで全キー、全値を取得できます。
takk@deb9:~/pl$ cat test.pl %a = ( dog , '犬', cat , '猫', fox , '狐' ); @all_keys = keys %a; @all_values = values %a; print "@all_keys\n"; print "@all_values\n"; takk@deb9:~/pl$ perl test.pl cat dog fox 猫 犬 狐 takk@deb9:~/pl$
Perl ファイル読み込み
Perlでテキスト読み込みします。
入門書ではPerlのテキスト読み込みは<>から入るのが王道かもしれませんが、
初めてPerlを使う人はいきなり<>から入ると、理解するのに時間がかかると思うんです。
なので、ファイルオープンから始めます。
ファイルの読み込み。ファイル名を指定してテキストを読み込むには、openを使ってファイルハンドラを取得します。
“”でファイル名を指定しますが、読み込みの場合は”<ファイル名"、書き込みは">ファイル名”となります。
takk@deb9:~/pl$ cat open-read-close.pl open(FH,"<in.txt"); $text = <FH>; print $text; close(FH); takk@deb9:~/pl$
ファイルをオープンして、一行だけ読み込んで表示するスクリプトです。
読み込むファイルも作りましょう。
takk@deb9:~/pl$ seq 101 105 > in.txt takk@deb9:~/pl$ cat in.txt 101 102 103 104 105 takk@deb9:~/pl$
実行するとファイルの最初の一行が表示されます。
takk@deb9:~/pl$ perl open-read-close.pl 101 takk@deb9:~/pl$
takk@deb9:~/pl$ cat read6.pl open(FH,"<in.txt"); $text = <FH>; print $text; $text = <FH>; print $text; $text = <FH>; print $text; $text = <FH>; print $text; $text = <FH>; print $text; print "undef\n" unless(defined $text); $text = <FH>; print $text; print "undef\n" unless(defined $text); close(FH); takk@deb9:~/pl$ perl read6.pl 101 102 103 104 105 undef takk@deb9:~/pl$
読み込む行がないと$text変数にはundefが入りますので、
全行を読み込むにはwhileを使えます。
takk@deb9:~/pl$ cat read-while.pl open(FH,"<in.txt"); while($text = <FH>){ print $text; } close(FH); takk@deb9:~/pl$ perl read-while.pl 101 102 103 104 105 takk@deb9:~/pl$
$textという変数に読み込みしていますが、デフォルト変数($_)が使えます。
$_を使うとスクリプトを簡潔に書くことができます。
takk@deb9:~/pl$ cat read.pl open(FH,"<in.txt"); while(<FH>){ print $_; } close(FH); takk@deb9:~/pl$ perl read.pl 101 102 103 104 105 takk@deb9:~/pl$
配列を使えばファイルの全行を一括取得することができます。
takk@deb9:~/pl$ cat read-all.pl open(FH,"<in.txt"); @all = <FH>; print @all; close(FH); takk@deb9:~/pl$ perl read-all.pl 101 102 103 104 105 takk@deb9:~/pl$
Perl 標準入力
標準入力から読み込みします。
ファイルを開いて読み込みするスクリプトと実行結果はこうでした。
takk@deb9:~/pl$ cat read.pl open(FH,"<in.txt"); while(<FH>){ print $_; } close(FH); takk@deb9:~/pl$ perl read.pl 101 102 103 104 105 takk@deb9:~/pl$
対して標準入力を読み込んで表示するスクリプトと実行結果はこうなります。
takk@deb9:~/pl$ cat stdin.pl while(<STDIN>){ print $_; } takk@deb9:~/pl$ seq 101 105 | perl stdin.pl 101 102 103 104 105 takk@deb9:~/pl$
標準入力を使う場合は、open/closeが不要になります。
標準入力のファイルハンドラは、
takk@deb9:~/pl$ cat stdin.pl while(<>){ print $_; } takk@deb9:~/pl$ seq 101 105 | perl stdin.pl 101 102 103 104 105 takk@deb9:~/pl$
さらにwhile(<>)のループを書かなくても実施してくれる-nオプションを使うとこう書けます。
takk@deb9:~/pl$ cat stdin.pl print $_; takk@deb9:~/pl$ seq 101 105 | perl -n stdin.pl 101 102 103 104 105 takk@deb9:~/pl$
さらに、デフォルト変数$_をprintする場合は、printだけで良いため、
takk@deb9:~/pl$ cat stdin.pl print; takk@deb9:~/pl$ seq 101 105 | perl -n stdin.pl 101 102 103 104 105 takk@deb9:~/pl$
さらに、printも-pで省略できます。
takk@deb9:~/pl$ cat stdin.pl takk@deb9:~/pl$ seq 101 105 | perl -p stdin.pl 101 102 103 104 105 takk@deb9:~/pl$
ここまでくるとスクリプトには何をかけばいいのか良くわからなくなるのですが、デフォルト変数$_を変更する処理を書きます。
takk@deb9:~/pl$ cat stdin.pl $_ = "PERL:" . $_; takk@deb9:~/pl$ seq 101 105 | perl -p stdin.pl PERL:101 PERL:102 PERL:103 PERL:104 PERL:105 takk@deb9:~/pl$
takk@deb9:~/pl$ cat stdin.pl $_ = $_ * 2 . "\n"; takk@deb9:~/pl$ seq 101 105 | perl -p stdin.pl 202 204 206 208 210 takk@deb9:~/pl$
Perl ファイル書き込み
読み込みの後は書き込みです。
書き込みするファイルを開くのにもopenを使います。
open(ハンドラ,”>ファイル名”);
書き込みするには、
print ハンドラ 書き込む文字列;
使ってみます。
takk@deb9:~/pl$ cat write.pl open(FH,">out.txt"); print FH "HELLO\n"; close(FH);
out.txtというファイルを作成してHELLOという文字列を書き込むスクリプトです。
実行結果です。
takk@deb9:~/pl$ ls write.pl takk@deb9:~/pl$ perl write.pl takk@deb9:~/pl$ ls out.txt write.pl takk@deb9:~/pl$ cat out.txt HELLO takk@deb9:~/pl$
次は、ファイル読み書きをしてファイルコピーするスクリプトです。
takk@deb9:~/pl$ cat read-write.pl open(FH_IN,"<in.txt"); open(FH_OUT,">out.txt"); @a = <FH_IN>; print FH_OUT @a; close(FH_IN); close(FH_OUT);
コピー元ファイルを作ります。
takk@deb9:~/pl$ seq 20 | pr -t4J > in.txt takk@deb9:~/pl$ cat in.txt 1 6 11 16 2 7 12 17 3 8 13 18 4 9 14 19 5 10 15 20
実行してみます。
takk@deb9:~/pl$ perl read-write.pl takk@deb9:~/pl$ cat out.txt 1 6 11 16 2 7 12 17 3 8 13 18 4 9 14 19 5 10 15 20 takk@deb9:~/pl$ diff in.txt out.txt takk@deb9:~/pl$
コピーされました。
標準出力は、そのままprintするだけですね。
どうしてもハンドラを使いたいなら、STDOUTが使えます。
takk@deb9:~/pl$ cat stdout.pl print STDOUT "HELLO\n"; takk@deb9:~/pl$ perl stdout.pl HELLO takk@deb9:~/pl$
標準エラー出力するには、STDERRを指定します。
takk@deb9:~/pl$ cat stderr.pl print STDERR "HELLO\n"; takk@deb9:~/pl$ perl stderr.pl 1>/dev/null HELLO takk@deb9:~/pl$
コメント