Perl入門

コマンドの達人を目指すために、避けては通れないプログラミング言語はたくさんあると思いますが、私はPerlで育ったエンジニアなので、Perlの学習は外せないと考えています。

Perlは、Linuxディストリビューションには標準で入っていますので、インストール不要ですが、apt-get install perl(Debian系)や、yum install perl(Red Hat系)ですぐに利用できます。

Perl 実行

perlコマンドでPerlスクリプトを実行できます。-eオプションはスクリプトをコマンドラインでそのまま記述することができるオプションです。

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 変数

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配列操作です。

まずは配列の初期化。空の()を代入すればよいです。

takk@deb9:~/pl$ perl -e '@a=1..1;@a=();print "@a\n"';

takk@deb9:~/pl$

文字列の配列はクォートで区切る方法と、

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$

次は要素の取出しです。

添え字でインデックスを指定して要素を取得することができますが、配列を操作して要素を取り出すことができます。

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$

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$

では要素の追加です。

配列の最後に要素を追加するには、pushを使います。

takk@deb9:~/pl$ perl -e '@a=1..5;push @a,10;print "@a\n"';
1 2 3 4 5 10
takk@deb9:~/pl$

配列の先頭に追加するには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 取出し

要素の途中から一気に取り出すには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$

コメント

タイトルとURLをコピーしました