とほほのperl入門(概要編)

トップ > とほほのperl入門 > 概要編

Perlとは

インストール

# RHEL7系
# yum -y install perl

# RHEL8/9系
# dnf -y install perl

# Ubuntu系
$ sudo apt -y install perl

実行方法

引数指定

コマンドプロンプトから次のように入力してください。-e オプションは次に続く '...' を Perl スクリプトとして実行します。

perl -e 'print "Hello world!\n"'
ファイル指定

次のような内容のテキストファイル(仮にhello.plとします)を作成してください。

print "Hello world!\n";

上記で作成したファイルの名前を引数にして、コマンドプロンプトから次のようにperlコマンドを実行してください。

perl hello.pl
自己完結型

Linux系OSで使用可能な方法です。次のような内容のテキストファイル(仮にhello.plとします)を作成してください。

#!/usr/bin/env perl
print "Hello world!\n";

env を使用すると環境変数 PATH の中から perl コマンドを探してくれます。perl コマンドのパスが明確であれば下記の様に記述しても構いません。

#!/usr/bin/perl
print "Hello world!\n";

#! というのは Linux系特有の機能で、その後ろに続くコマンドを起動するためのおまじないです。chmodコマンドを次のように使用してファイルのモードを変更してください。

chmod 755 hello.pl

コマンドプロンプトから次のように入力して実行してください。

./hello.pl
Hello world!
標準入力読み込み型

perlコマンドの引数が指定されていない場合、標準入力から読み取った内容をperlスクリプトとして実行します。

echo 'print "Hello World!\n";' | perl

基礎知識

簡単な実行例

次の例では変数 $x に 5 を、変数 $y に 2 を代入し、その合計を変数 $z に代入した後、結果を表示します。

$x = 5;
$y = 2;
$z = $x + $y;
print "$z\n";      # => 7
これだけは覚えて

以後の説明を楽にするために、上記の実行例についてこれだけは覚えておいてください。

strictモードとwarningモード

プログラムの先頭に下記の2行を記述することが推奨されています。strict は Perl の文法を厳密にチェックします。warnings は詳細な警告メッセージを出力します。プログラムのミスを早期に見つけるのに効果的です。

use strict;
use warnings;

strictモードの場合、上記のプログラムは下記の様に変数宣言 my をつける必要があります。

my $x = 5;
my $y = 2;
my $z = $x + $y;
print "$z\n";         # => 7
関数の括弧

print などの関数に引数を渡す場合、次のように括弧無しで渡すことができます。

print "...";

C言語などのように括弧付きで記述することもできます。

print("...");

定数

数値

次のような表現の数値を使用することができます。

12345          # 整数
12_345         # 整数(アンダーバーは無視されます。桁区切りに使用可能)
1.23           # 小数
1.23E45        # 浮動小数点数(E)
1.23e45        # 浮動小数点数(e)
0x12345        # 16進数(0x)
012345         # 8進数(0)
0o12345        # 8進数(0o)
0b10011        # 2進数(0b)
0x1.23ap-4     # 16進浮動小数点数(0x...ap...)

use bigint を宣言すると整数や浮動小数点数が Math::BigInt として扱われます。use bignum を宣言すると整数は Math::BigInt、浮動小数点数 は Math::BigFloat オブジェクトとして扱われます。処理速度は遅くなりますが有効桁数が増えます。

sqrt(2);      # => 1.4142135623731
2**128;       # => 3.40282366920938e+38
use bigint;
sqrt(2);      # => 1 (整数になってしまうので注意が必要)
2**128;       # => 340282366920938463463374607431768211456
use bignum;
sqrt(2);      # => 1.41421356237309504880168872420969807857
2**128;       # => 340282366920938463463374607431768211456
use bignum(a=>50);   # 桁数を指定
sqrt(2);      # => 1.4142135623730950488016887242096980785696718753769
2**128;       # => 340282366920938463463374607431768211456
文字列

文字列はダブルクォーテーション(")またはシングルクォーテーション(')で囲みます。

"abc"
'abc'

ダブルクォーテーションの中ではシングルクォーテーションを、シングルクォーテーションの中ではダブルクォーテーションを使用することができます。

"abc'def'ghi"
'abc"def"ghi'

"..." の中でダブルクォーテーション、'...' の中でシングルクォーテーションを使いたいときはバックスラッシュ(\)でエスケープします。

"ダブルクォーテーションの中でダブルクォーテーション(\")を使う"
'シングルクォーテーションの中でシングルクォーテーション(\')を使う'

ダブルクォーテーションの中では変数が評価されますが、シングルクォーテーションの中では単なる文字として認識されます。

my $ans = 7;
"Answer is $ans.";     # => Answer is 7.
'Answer is $ans.';     # => Answer is $ans.\n
エスケープシーケンス(\)

"..." の中ではバックスラッシュ(\)に続く文字は特別な意味を持ちます。'...' の中では \' と \\ のみ使用できます。

\t        # タブ文字(\x09)
\n        # 改行(\x0a)
\r        # リターン(\x0d)
\f        # フォームフィード(\x0c)
\b        # バックスペース(\x08)
\a        # アラーム(\x07)
\e        # エスケープ文字(\x1b)
\033      # 文字コード(8進数)
\x1b      # 文字コード(16進数)
\x{319F}  # Unicode(16進数)
\c[       # コントロール文字
\l        # 次の1文字を小文字にする
\u        # 次の1文字を大文字にする
\L        # \Eまでの文字列を小文字にする
\U        # \Eまでの文字列を大文字にする
\E        # \Lや\Uを終了させます
\"        # ダブルクォーテーション(")
\'        # シングルクォーテーション(')
\\        # バックスラッシュ(\)
\!など     # !などの文字
数値と文字列

数値と文字列の扱いはあいまいで、数値として評価されたり、文字列として評価されたりします。次の例では1行目では文字列の 12345 が代入されていますが、2行目では加算演算子により数値として解釈され、12350 が表示されます。

my $foo = "12345";
print $foo + 5;
真(true)と偽(false)

if ( ) 文の中などの真偽判断では、空文字列("")、ゼロ(0)、ゼロを示す文字列("0")、未定義値は偽として、それ以外は真として扱われます。true や false という特殊な変数がある訳ではないので、return(false) は、"false" という文字列を返却することになります。真偽を返却する関数を評価する際は if (func() == true) {...} ではなく、if (func()) {...} と評価してください。

バイナリデータ

バイナリデータを扱うことができます。pack と unpack はバイナリデータに対して、文字表現、数値表現の変換を行います。次の例を実行すると [ABC] [65 66 67] [ABC] と表示されます。

my $str1 = "ABC";                      # 文字列としての代入
my @bin = unpack("C*", $str1);          # 数値の配列に変換
my $str2 = pack("C*", @bin);            # 数値の配列を文字列に変換
print "[$str1] [@bin] [$str2]\n";         # => [ABC] [65 66 67] [ABC]

変数

変数宣言(my)

変数の使用を始めるには my を用いて変数を宣言します。my を省略することもできますが、strictモード の場合は必須となります。

my $foo1;
my $foo2 = 123;
my $foo3 = "ABC";
my @foo4 = (1, 2, 3);
my %foo5 = (name => "Yamada", age => 26);
my ($a, $b, $c) = (1, 2, 3)
スカラ($XXX)

$変数名スカラー変数 と呼ばれます。スカラー変数には数値、文字列、リファレンス のいずれかをひとつだけ代入することができます。

my $foo = 5;           # スカラー変数$fooに数値5を代入
my $baa = "abc";       # スカラー変数$baaに文字列abcを代入
print "$foo $baa\n";    # スカラー変数$fooと$baaの値を表示
配列(@XXX)

@変数名配列 と呼ばれます。配列は複数のスカラー値を持つことができます。配列の個々の値を要素と呼びます。$配列[添え字] で個々の配列要素を参照することができます。添え字は 0 からはじまる整数です。

my @foo = (123, 12.3, "ABC");      # 配列@fooを定義して、123 と 12.3 と "ABC" を代入
print "$foo[0]\n";                 # => 123
print "$foo[1]\n";                 # => 12.3
print "$foo[2]\n";                 # => ABC

配列には下記の様にして要素を代入することができます。

my @foo = (123, 12.3, "ABC");
$foo[0] = 234;               # 0番目の要素に234を代入
@foo[1..2] = (23.4, "XYZ");  # 1番目の要素に23.4、2番目の要素に"XYZ"を代入
print "$foo[0]\n";           # => 234
print "$foo[1]\n";           # => 23.4
print "$foo[2]\n";           # => XYZ

$#変数名 は、@変数名 の配列の最後の添え字(個数から1をひいたもの)を表わします。

my @foo = ("A", "B", "C", "D");
print "$#foo\n";                     # => 3

@変数名 をスカラ値として参照すると配列の個数を返します。

my @foo = ("A", "B", "C", "D");
my $num = @foo;
print "$num\n";                     # => 4

配列を用いて複数の変数に一括して代入することもできます。

my ($year, $mon, $day) = (2023, 9, 24);
print "$year/$mon/$day\n";         # => 2023/9/24

()は空配列を示します。

my @foo = ();          # 0個の要素を持つ配列

((...), (...)) は二次元配列にはなりません。二次元配列を生成するには リファレンス や無名配列コンストラクタを用います。

# 1次元配列になってしまう例
@arr = ((1, 2), (3, 4));       # (1, 2, 3, 4) と等価
print "$arr[3]\n";             # => 4

# 配列リファレンス(\@)の配列を定義する
@arr1 = (1, 2);
@arr2 = (3, 4);
@arr = (\@arr1, \@arr2);
print "$arr[1][1]\n";          # => 4

# 無名配列コンストラクタを利用する例
@arr = ([1, 2], [3, 4]);
print "$arr[1][1]\n";          # => 4

配列を操作するためのいろいろな関数が用意されています。

shift(@arr);            # 最初の要素を取り除き、これを返す
pop(@arr);              # 最後の要素を取り除き、これを返す
push(@arr, $arr);       # 最後に要素を追加する
push(@arr, @arr2);      # 最後に配列の値を追加する
splice(@arr, 3, 2);     # $arr[3..4](2個)を取り除き、これを返す
reverse(@arr);          # @arrの要素を逆順にしたものを返す
sort(@arr);             # @arrの要素をソートした結果を返す

すべての配列要素について処理を行うには foreach を用います。

my @arr = ("A", "B", "C", "D", "E");
foreach my $i (@arr) {
    print "$i\n";
}

配列を表す際に qw(...) を使用することでシングルクォートとカンマを省略することができます。qw(...) の代わりに qw[...] や qw/.../ などを使用することもできます。下記はすべて同じ動作となります。

@arr = ('AAA', 'BBB', 'CCC');
@arr = qw(AAA BBB CCC);
@arr = qw[AAA BBB CCC];
@arr = qw/AAA BBB CCC/;

配列の要素に配列を含むことはできません。配列を含めたい場合は リファレンス を用います。

my @arr1 = (1, 2, 3);
my @arr2 = (4, 5, 6);
my @arrs = (\@arr1, \@arr2);
foreach my $arr (@arrs) {
    printf("%d, %d, %d\n", $arr->[0], $arr->[1], $arr->[2]);
}
ハッシュ(%XXX)

%変数名ハッシュ と呼ばれます。ハッシュは文字列を添え字とした複数のスカラー値を持つことができます。連想配列 と呼ばれることもあります。

my %foo;                       # ハッシュ %foo を定義
$foo{"name"} = "Tanaka";
$foo{"age"} = 26;
printf("%s(%d)\n", $foo{"name"}, $foo{"age"});    # => Tanaka(26)

次の様に初期化することもできます。

my %foo = (
    "name" => "Tanaka",
    "age" => 26,
);

添え字が 変数名に使える文字 であれば添え字のダブルクォート(")を省略することができます。

my %foo = (
    name => "Tanaka",
    age => 26,
);

while を用いてすべてのハッシュ要素に対して処理を行うことができます。

while (my ($key, $value) = each(%foo)) {
    print "$key = $value\n";
}

$foo{index1, index2, ...} で多次元のハッシュも使用できます。この場合、高次元ハッシュの添え字の区切り文字($;)で各添え字を連結した文字列が添え字として使用されます。

my %foo;
$foo{"A", "B", "C"} = "X";   # $foo{join($;, "A", "B", "C")} と同じ

%foo{index1, index2, ...} でハッシュの部分集合となるハッシュを参照することができます。

my %foo = (A => 1, B => 2, C => 3, D => 4);
my %baa = %foo{"B", "C"};                  # => (B => 2, C => 3)

ハッシュの要素に配列やハッシュを含めることはできません。含めたい場合は リファレンス を利用します。

my @a = (1, 2, 3);
my @b = (4, 5, 6);
my %hash = ("A"=>\@a, "B"=>\@b);
while (my ($k, $v) = each(%hash)) {
    printf("%s: %d, %d, %d\n", $k, $v->[0], $v->[1], $v->[2]);
}
型グロブ(*XXX)

*変数名 は型グロブと呼ばれます。*foo は $foo, @foo, %foo の総称のようなものです。下記の例では baa は foo のエイリアスのように動作します。my 変数に対しては動作しません。

$foo = 123;
@foo = (1, 2, 3);
%foo = (A => 1, B => 2, C => 3);
*bar = *foo;
print $bar; print "\n";    # => 123
print @bar; print "\n";    # => 1 2 3
print %bar; print "\n";    # => A1B2C3

open(my $fh, ...) のようにファイルハンドルをローカル変数として記述できない時代は、ファイルハンドルをローカル変数化する際に使用されていました。現在では使用されるケースは少ないかもしれません。

sub read_file {
    local(*IN) = @_;        # INをローカル変数のように使用できる
    while (<IN>) { print }
}
open(IN, "file.txt");
read_file(*IN);
close(IN);
変数名に使える文字

$変数名、@変数名、%変数名 の変数名には、アンダーバー(_)、半角英字などの文字(letter)、数字(digit)を使用できます。最初の文字は数字(digit)であってはなりません。

$fileName      # ○ 使用可能
$file_name     # ○ 使用可能
$my-name       # × ハイフン(-)は使用できない
$32foo         # × 数値で始まってはならない

use utf8 を宣言しておけば Unicode で Letter(L) や Digit(Nd) に分類されている文字であれば日本語でも使用できるようです。

$名前          # ○ use utf8 を宣言すれば letter(L) に日本語も使用可能
$名前𝟝         # ○ use utf8 を宣言すれば digit(Nd) も使用可
$名前⑤         # × ⑤ は Number(N) ではあるけど Digit(Nd) ではないのでNG

大文字の変数名と小文字の変数名は別の変数として扱われます。

my $foo = 123;
my $FOO = 456;

$変数名、@変数名、%変数名 はそれぞれ個別の名前空間をもっているので、$foo と @foo と %foo はそれぞれ別の変数になります。

my $foo = 123;
my @foo = ("A", "B", "C");
my %foo = ("A" => "a", "B" => "b");

"..." の中で後方の文章と変数名の境界が不明確になる場合などは、変数名を {...} で囲むことができます。

print "You get ${point}points.\n";
print "You get ${point[0]}points.\n";
print "You get ${point{'Yamada'}}points.\n";
定義済変数みと未定義変数

変数には定義済みの状態と未定義の状態があります。最初は未定義ですが、最初に何か値が代入されると定義済みになります。定義済みかどうかを調べるためにdefined()を使用できます。

my $foo = 123;
if (defined($foo)) {
    print "Defined\n";
} else {
    print "Not defined.\n";
}

未定義のスカラ変数は、数値コンテキストでは 0、文字列コンテキストでは空文字("")、未定義の配列は空配列、未定義のハッシュは空ハッシュとみなされます。

say 5 + $foo;                     # 数値 0 とみなされる
say "5" . $foo;                   # 空文字 "" とみなされる
say (1, 2, @foo);                 # 空配列 () とみなされる
say (A=>1, B=>2, %foo);           # 空ハッシュ () とみなされる
省略時の変数($_)

perlには省略の美学というものがあり、いろいろな箇所で変数名を省略することができます。省略した場合は、$_ を指定したものとみなされます。

while (<IN>)    # while ($_ = <IN>) と同じ意味
print;          # print $_; と同じ意味
if (/^From:/)   # $_ =~ /^From:/ と同じ意味

これを用いて、

while ($foo = <IN>) {
    if ($foo =~ /^From:/) { print $foo; }
}

というコードは、次のように書くことができます。

while (<IN>) {
    if (/^From:/) { print; }
}
環境変数($ENV{'XXX'})

%ENV は環境変数の値を持つハッシュです。

print $ENV{"PATH"};           # 環境変数PATHの値を表示する
$ENV{"TZ"} = "Asia/Tokyo";    # 環境変数TZに値を設定する
コマンド引数($ARGV)

@ARGV はコマンドラインからの引数をもつ配列です。

@ARGV           # 引数の配列
$#ARGV          # 配列の個数 - 1
$ARGV[0]        # 最初の引数
$ARGV[$#ARGV]   # 最後の引数
シグナル($SIG{'XXX'})

%SIG はシグナルハンドラを制御するハッシュです。下記の例では Ctrl-C で SIGINT シグナルをうけとると sigint サブルーチンを呼び出し、SIGINT!!! と表示してプログラムを終了させます。

sub sigint {
    print "SIGINT!!!\n";
    exit;
}

$SIG{"INT"} = "sigint";
その他の特殊変数($X)

他にも次のような特殊変数があります。

$.       # ファイルから読み込み時の現在の行数
$/       # 入力時のレコード区切り文字。通常は改行。
$,       # 出力時の項目区切り文字。print @foo; の時に有効
$"       # 出力時の項目区切り文字。print "@foo"; の時に有効
$\       # 出力時の行末文字。print "$foo"; の後ろに付加される
$#       # 出力時の数値形式。通常は"%.20g"。詳細はprintfを参照
$%       # 出力時の現在のページ番号
$=       # 出力時の現在のライン数
$-       # 出力時の残り行数
$~       # 出力時のフォーマット名(デフォルトはハンドル名と同じ)
$^       # 出力時のヘッダフォーマット名
$|       # 0以外が代入されると出力をバッファリングしなくなる。
$$       # プロセスID
$?       # 最後に実行されたコマンドのステータス
$&       # パターンマッチにマッチした部分文字列
$`       # パターンマッチにマッチした部分の前側の文字列
$'       # パターンマッチにマッチした部分の後側の文字列
$+       # パターンマッチの最後の()に対応する文字列
$*
$0       # perlスクリプトのコマンド名
$1       # パターンマッチの際の1番目の()に対応する文字列
$[       # 配列の最初の添え字。通常は0。
$]       # perlのバージョン情報
$;       # 高次元ハッシュの添え字の区切り文字
$!       # エラー番号、もしくはエラー文字列
$@       # 直前のevalコマンドのエラーメッセージ
$<       # このプロセスの実ユーザーID
$>       # このプロセスの実効ユーザーID
$(       # このプロセスの実グループID
$)       # このプロセスの実効グループID
$
$^D      # デバッグフラグの値
$^F      # システムファイルディスクリプタの最大値
$^I      # -iオプションで指定した拡張子の名前
$^L
$^P      # デバッガが使用する内部フラグ
$^T      # スクリプトを実行した時刻
$^W      # 警告スイッチの現在値
$^X      # perl自身の起動時の名前
@_       # サブルーチンへの引数
@INC     # perlライブラリ検索ディレクトリ
%INC     # 読み込まれたライブラリファイルの配列
__LINE__ # スクリプト中の現在の行数
__FILE__ # スクリプトのファイル名

演算子

代入演算子(=)

次の例は「$fooは5である」という意味ではなく、「変数$fooに値5を代入する」という意味を持ちます。

$foo = 5;
算術演算子(+ - * / % **)

単項演算子の + は正数、- は負数を示します。

$foo = +5;           # => 5
$foo = -5;           # => -5

下記の算術演算子を使用することができます。

$foo = 5 + 2;        # 足し算
$foo = 5 - 2;        # 引き算
$foo = 5 * 2;        # 掛け算
$foo = 5 / 2;        # 割り算
$foo = 5 % 2;        # 5を2で割った余り(=1)
$foo = 5 ** 2;       # 5の2乗(=25)

下記の様に演算と代入をまとめておこなうことができます。

$foo += 2;           # $foo = $foo + 2; と同じ
$foo -= 2;           # $foo = $foo - 2; と同じ
$foo *= 2;           # $foo = $foo * 2; と同じ
$foo /= 2;           # $foo = $foo / 2; と同じ
$foo %= 2;           # $foo = $foo % 2; と同じ
$foo **= 2;          # $foo = $foo ** 2; と同じ
インクリメント・デクリメント演算子(++ --)

下記の例のように $foo の値をひとつ増やしたり(インクリメント)、減らしたり(デクリメント)することができます。

$foo++;              # $fooの値を1加算する(戻り値は加算前の値)
$foo--;              # $fooの値を1減算する(戻り値は減算前の値)
++$foo;              # $fooの値を1加算する(戻り値は加算後の値)
--$foo;              # $fooの値を1減算する(戻り値は減算後の値)
数値比較演算子(== != < > <= >= <=>)

if文やwhile文の中で数値に対する条件判断を行うには、次のような比較演算子を用います。

if ($foo == $baa)            # $fooと$baaが等しければ
if ($foo != $baa)            # $fooと$baaが等しくなければ
if ($foo < $baa)             # $fooが$baaより小さければ
if ($foo > $baa)             # $fooが$baaより大きければ
if ($foo <= $baa)            # $fooが$baaより小さいか等しければ
if ($foo >= $baa)            # $fooが$baaより大きいか等しければ

下記の演算子は、$foo と $baa を比較して、$foo が小さければ -1 を、等しければ 0 を、大きければ 1 を返します。

$foo <=> $baa                # 比較結果を -1, 0, 1 のいずれかで返す
文字列比較演算子(eq ne lt gt le ge cmp)

文字列に対しては次のような演算子を用います。

if ($foo eq $baa)            # $fooと$baaが等しければ (EQual)
if ($foo ne $baa)            # $fooと$baaが異なっていれば (Not Equal)
if ($foo lt $baa)            # $fooが$baaより辞書順に小さければ (Less Than)
if ($foo gt $baa)            # $fooが$baaより辞書順に大きければ (Greater Than)
if ($foo le $baa)            # $fooが$baaより等しいか辞書順に小さければ (Less than or Equal)
if ($foo ge $baa)            # $fooが$baaより等しいか辞書順に大きければ (Greater than or Equal)
if ($foo cmp $baa)           # 比較し、-1, 0, 1 のいずれかで返す
ビット演算子(| & ^ ~ << >>)

下記のビット演算子を使用することができます。

printf("%04x\n", 0xff00 | 0x00ff);      # => ffff (OR)
printf("%04x\n", 0xfff0 & 0x0fff);      # => 0ff0 (AND)
printf("%04x\n", 0xfff0 ^ 0x0fff);      # => f00f (XOR)
printf("%x\n", ~0xffff0000);            # => ffffffff0000ffff (NOT)
printf("%04x\n", 0x00ff << 8);          # => ff00 (8ビット左シフト)
printf("%04x\n", 0xff00 >> 8);          # => 00ff (8ビット右シフト)

下記も使用できます。

$foo |= 0x00ff;          # $foo = $foo | 0x00ff と同じ
$foo &= 0x0fff;          # $foo = $foo & 0x0fff と同じ
$foo ^= 0x0fff;          # $foo = $foo ^ 0x0fff と同じ
$foo <<= 8;              # $foo = $foo << 8 と同じ
$foo >>= 8;              # $foo = $foo >> 8 と同じ

右辺または左辺が数値の時はうまくビット演算されるのですが、両辺が文字列の時は文字列としての演算が行われてしまいます。

$foo =  150  |  105;          # => 255 (0x96 | 0x69 は 0xFF)
$foo =  150  | "105";         # => 255 (0x96 | 0x69 は 0xFF)
$foo = "150" |  105;          # => 255 (0x96 | 0x69 は 0xFF)
$foo = "150" | "105";         # => 155 (文字列として演算してしまう)

この問題を改善するために Perl 5.22 で bitwise 機能がサポートされました。bitwise 機能では演算子 | & ~ ^ は常に算術演算を行います。代わりに常に文字列演算を行う演算子として |. &. ~. ^. をサポートします。use feature "bitwise" または use v5.28 を指定することで利用できます。

use feature "bitwise";
$foo =  150  |  105;          # => 255 (常に数値として演算)
$foo =  150  | "105";         # => 255 (常に数値として演算)
$foo = "150" |  105;          # => 255 (常に数値として演算)
$foo = "150" | "105";         # => 255 (常に数値として演算)

$foo =  150  |.  105;         # => 155 (常に文字列として演算)
$foo =  150  |. "105";        # => 155 (常に文字列として演算)
$foo = "150" |.  105;         # => 155 (常に文字列として演算)
$foo = "150" |. "105";        # => 155 (常に文字列として演算)
論理演算子(and or xor not && || // !)

and(かつ)、or(または)、xor(排他的論理和)、not(否定) 演算子を使用できます。

if (($foo==2) and ($baa==3))  # $fooが2、かつ、$baaが3であれば
if (($foo==2) or ($baa==3))   # $fooが2、もしくは、$baaが3であれば
if (($foo==2) xor ($baa==3))  # $fooが2 と $baaが3 どちらか一方のみ真であれば
if (not ($foo == 2))         # $fooが2でなければ

and の代わりに &&or の代わりに ||not の代わりに ! を使用できます。

if (($foo==2) && ($baa==3))   # $fooが2、かつ、$baaが3であれば
if (($foo==2) || ($baa==3))   # $fooが2、もしくは、$baaが3であれば
if (!($foo == 2))            # $fooが2でなければ

||= はデフォルト値を設定するのによく利用されます。下記の例では第二引数が省略された場合のデフォルト値として "Hello" を設定しています。

sub greet {
    my ($who, $greeting) = @_;
    $greeting ||= "Hello";
    print "$greeting $who!\n";
}
greet("Yamada", "Good morning");     # => Good morning Yamada!
greet("Yamada");                     # => Hello Yamada!

//|| と似ていますが、左辺が真か偽かではなく、定義されているか否かで判断する点が異なります。上記の ||= は下記の様に、$greeting が未定義であれば...のように書けます。

    $greeting //= "Hello";
条件演算子(? :)

A ? B : C は、A が真であれば B を、偽であれば C を値とします。下記の例では $baa の値が5未満であれば "small" を、さもなくば "big" を $foo に代入します。

$foo = ($baa < 5) ? "small" :  "big";
正規表現マッチング(=~ !~)

変数 $foo の文字列が 正規表現 にマッチするか否かを調べます。

if ($foo =~ /^[a-z]+$/)        # 正規表現にマッチすれば
if ($foo !~ /^[a-z]+$/)        # 正規表現にマッチしなければ

$_ を対象とする場合は $_ =~ を省略することができます。

if (/^[a-z]+$/)                # if ($_ =~ /^[a-z]+$/) と同義

置換文字列の中に括弧を書くと、その括弧に対応した文字列を $1, $2, ... で参照することができます。次の例は、12:34:56 の秒数を求めます。

my $foo = "12:34:56";
if ($foo =~ /([0-9][0-9]):([0-9][0-9]):([0-9][0-9])/) {
    print $1 * 3600 + $2 * 60 + $3;      # => 45296
}
文字列連結(.)

文字列を連結します。

$foo = $baa . $baz;    # 文字列 $baa と 文字列 $baz を連結します
$foo .= $baa;         # $foo = $foo . $baa と同様です。
文字列置換(s/.../.../)

変数名 =~ s/expr/str/ は、変数に格納された文字列の中の正規表現 expr にマッチする部分を文字列 str に置換します。下記の例では、文字列 $foo の中の abc にマッチする部分を ABC に置換します。

my $foo = "abcdefg";
$foo =~ s/abc/ABC/;     # => ABCdefg

正規表現の中に (...) を使用すると (...) に対応する文字列を $1, $2, $3,...で参照することができます。次の例は、"12:34:56" を "12時34分56秒" に置換します。

my $foo = "12:34:56";
$foo =~ s/([0-9][0-9]):([0-9][0-9]):([0-9][0-9])/$1$2$3秒/;

g オプションは、見つかった箇所をすべて置換します。g オプションをつけない場合は最初の1回のみを置換します。

my $foo1 = "abcabcabc";
my $foo2 = "abcabcabc";
$foo1 =~ s/abc/ABC/;         # => ABCabcabc (最初の1回のみ置換)
$foo2 =~ s/abc/ABC/g;        # => ABCABCABC (見つかったものすべてを置換)

i オプションは大文字と小文字を区別せずにマッチングします。

my $foo1 = "abcABC";
my $foo2 = "abcABC";
$foo1 =~ s/ABC/XYZ/g;         # => abcXYZ (ABCのみにマッチ)
$foo2 =~ s/ABC/XYZ/gi;        # => XYZXYZ (abcにもABCにもマッチ)

e オプションは、str の部分を文字列ではなく perlの実行文と解釈してその結果に置換します。次の例は、12:34:56を秒数に置換します。

my $foo = "12:34:56";
$foo =~ s/([0-9][0-9]):([0-9][0-9]):([0-9][0-9])/$1*3600+$2*60+$3/e;   # => 45296

正規表現の中でスラッシュ(/)を使用する際は、バックスラッシュ(\)でエスケープする必要があります。下記の例は /usr/bin/perl を /usr/local/bin/perl に置換しています。

$foo =~ s/\/usr\/bin\/perl/\/usr\/local\/bin\/perl/;

s/.../.../ の代わりに s|...|...| を使用すると / をそのまま記述することができます。| の代わりに ' や ! など他の記号文字を使用することができます。

$foo =~ s|/usr/bin/perl|/usr/local/bin/perl|;

str に変数を使用すると変数の値に展開されます。ただし s'...'...' の場合は展開されません。

my $foo1 = "abc";
my $foo2 = "abc";
my $baa = "ABC";
$foo1 =~ s/abc/$baa/;    # => ABC
$foo2 =~ s'abc'$baa';    # => $baa

次の例は、URLエンコードされた %20 のような文字列をデコードします。CGIなどではよく使用される構文です。

my $foo = "%41%42%43";          # "ABC"のASCIIコード
$foo =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg;   # => ABC
文字置換(tr/.../.../ y/.../.../)

$str =~ tr/SEARCHLIST/REPLACEMENTLIST/[cdrs] は、$str から SEARCHLIST の中の文字にマッチする文字を、対応する REPLACEMENTLIST の文字に置換します。下記の例では小文字をすべて大文字に置換します。

my $str = "abcdefg";
$str =~ tr/[a-z]/[A-Z]/;
print "$str\n";                  # => ABCDEFG

y/.../.../ も同様です。

my $str = "abcdefg";
$str =~ y/[a-z]/[A-Z]/;
print "$str\n";                  # => ABCDEFG

c オプションは、REPLACEMENTLIST に無い文字が置換の対象となります。下記は A~Z 以外の文字を * に置換します。

my $str = "GetFileName";
$str =~ tr/[A-Z]/*/c;
print "$str\n";                # => G**F***N***

d オプションは、見つからなかった文字を削除します。

my $str = "abcdefg1234567";
$str =~ tr/[a-z]//d;
print "$str\n";                # => 1234567

r オプションは、文字列の置換は行わず、置換した結果を返します。

my $str1 = "abcdefg";
my $str2 = ($str1 =~ tr/[a-z]/[A-Z]/r);
print "$str1\n";               # => abcdefg
print "$str2\n";               # => ABCDEFG

s オプションは置換後に重複する文字を削除します。下記の例では aaabbca を dddddca に置換後、重複文字を削除して dcd となります。

my $str = "aaabbbca";
$str =~ tr/ab/dd/s;
print "$str\n";                # => dcd
繰り返し演算子(x)

x は繰り返し演算子と呼ばれます。

print "a" x 5;        # "aaaaa"
$foo x= 5;            # $foo = $foo x 5 と同じ
@foo = (1) x 5;       # (1, 1, 1, 1, 1)
@foo = (1, 2) x 5;    # (1, 2, 1, 2, 1, 2, 1, 2, 1, 2)
範囲演算子(.. ...)

.. は配列の範囲を示します。

@foo = (1 .. 5);     # @foo = (1, 2, 3, 4, 5);と同じ
for (1 .. 5)         # for ($_ = 1; $_ <= 5; $_++) と同じ

.. をループ中で使用すると左辺が真になってから右辺が真にまるまでの間を判断するのにも使用できます。次の例は HTML ファイルから <head>~</head> の間を表示します。

while (<>) {
    if (/<head>/ .. /<\/head>/) { print }
}

.. の代わりに ... を使用すると、左辺が真になったら、右辺が真になるまで左辺をチェックしなくなります。

while (<>) {
    if (/AAA/ ... /BBB/) { print }
}
コンマ演算子(,)

, は配列要素の区切り文字として使用する他、スカラー文脈として使用されると左辺を評価後右辺を評価し、右辺を返します。

my ($a, $b, $c);
my @foo = (1, 2, 3);                       # => (1, 2, 3)
my $foo = ($a="A", $b="B", $c="C");        # => "C"
クォート風演算子(q qq qx qr m qw)

q/.../ は '...' と同じ意味を持ちます。同様に下記のようなクォート風演算子が利用できます。

q/STRING/            'STRING'
qq/STRING/           "STRING"
qx/STRING/           `STRING`
qr/STRING/           /STRING/
m/STRING/            /STRING/
qw/STR1 STR2 STR3/   ('STR1', 'STR2', 'STR3')

/.../ の代わりに |...|, (...), {...} !...! など大半の記号を使用することができます。

q|STRING|
q(STRING)
q{STRING}
q!STRING!

s/.../.../, tr/.../.../, y/.../.../ の代わりに下記を使用することもできます。{...} の代わりに (...) や [...] なども使用できます。

s{...}{...}
tr{...}{...}
y{...}{...}
スマートマッチング演算子(~~)

~~ は左辺が右辺に含まれているかを判断する演算子です。Perl 5.10 で導入されましたが Perl 5.18 で実験的機能に差し戻されました。実行すると Smartmatch is experimental 警告が発せられます

my @foo = [1, 2, 3];
if (1 ~~ @foo) { print "OK\n" } else { print "NG\n" }     # => OK
if (4 ~~ @foo) { print "OK\n" } else { print "NG\n" }     # => NG
マジカルインクリメント演算子(++)

インクリメント演算子 ++ は文字列に対しても使用できます。デクリメント -- は使用できません。

print ++($foo = "a") . "\n";     # => b
print ++($foo = "A") . "\n";     # => B
print ++($foo = "z") . "\n";     # => aa
print ++($foo = "01") . "\n";    # => 02
クラスインスタンス演算子(isa)

isa は左辺が右辺のクラスまたはそのサブクラスのインスタンスであれば真を返します。Perl 5.31.6 で実験的に追加された機能で、use feature "isa" または use v5.36 が必要です。

use feature "isa";
my $p = new Person();
if ($p isa Person) { print "OK\n" }
if ($p isa "Person") { print "OK\n" }
ファイル検査(-f)

次のようにして、ファイルが読み込み、書き込み可能かどうかなどを検査することができます。

if (-r "file.txt") { print "読み込み可能\n"; }
if (-w "file.txt") { print "書き込み可能\n"; }

アンダーライン(_)は最後に検査されたファイルを示します。

if (-r "file.txt") { print "読み込み可能\n"; }
if (-w _) { print "書き込み可能\n"; }

ファイルハンドルに対して検査することもできます。

open(my $in, "<", "file.txt");
if (-r $in) { print "読み込み可能\n"; }
close($in);

stat() の結果に対してアンダーライン(_)で検査することもできます。

stat("file.txt");
if (-r _) { print "読み込み可能\n"; }

-r や -w の他にも次のような検査が可能です。

-r      # 読み込み可能であれば
-w      # 書き込み可能であれば
-x      # 実行可能であれば
-o      # 自分のものであれば
-R      # 実uid/gidで読み込み可能であれば
-W      # 実uid/gidで書き込み可能であれば
-X      # 実uid/gidで実行可能であれば
-O      # 実uidのものであれば
-e      # 存在すれば
-z      # サイズが0であれば
-s      # サイズが0でなければ(ファイルサイズを返す)
-f      # 通常ファイルであれば
-d      # ディレクトリであれば
-l      # シンボリックリンクであれば
-p      # 名前付きパイプであれば
-S      # ソケットであれば
-b      # ブロック型スペシャルファイルであれば
-c      # キャラクタ型スペシャルファイルであれば
-u      # setuidビットが立っていれば
-g      # setgifビットが立っていれば
-k      # stickyビットが立っていれば
-t      # ファイルハンドルが tty としてオープンされていれば
-T      # ファイルがテキストファイルであれば
-B      # ファイルがバイナリファイルであれば
-M      # perl起動時における、ファイルの更新時刻からの日数
-A      # perl起動時における、ファイルの参照時刻からの日数
-C      # perl起動時における、ファイルの作成時刻からの日数
演算子の優先順位

演算子の優先順位は下記の様になります。上のものほど優先度が高いです。

左結合      ->
非結合      ++ --
右結合      **
右結合      ! ~ ~. \(単項) +(単項) -(単項)
左結合      =~ !~
左結合      * / % x
左結合      + - .
左結合      << >>
非結合      名前付き単項演算子
連鎖        < > <= >= lt gt le ge
連鎖/なし    == != eq ne <=> cmp ~~
非結合      isa
左結合      & &.
左結合      | |. ^ ^.
左結合      &&
左結合      || //
非結合      .. ...
右結合      ?:
右結合      = += -= *= など; goto last next redo dump
左結合      , =>
非結合      リスト演算子 (右方向に対して)
右結合      not
左結合      and
左結合      or xor

制御構文

if elsif else文

if (...) {...} は (...) が真の場合のみ {...} を実行します。

my $foo = 5;
if ($foo == 5) {
    print "等しい\n";
}

else は「さもなくば」を意味します。

my $foo = 5;
if ($foo == 5) {
    print "等しい\n";
} else {
    print "等しくない\n";
}

elsif は「さもなくばもし」を意味します。

my $foo = 5;
if ($foo > 5) {
    print "5より大きい\n";
} elsif ($foo < 5) {
    print "5より小さい\n";
} else {
    print "5と等しい\n";
}
unless文

unless (...) {...} は (...) が偽の場合に {...} を実行します。

my $foo = 4;
unless ($foo > 5) {
    print "5より大きくない\n";
}
while文

while (...) {...} は、(...) が真のあいだ {...} を繰り返し実行します。

my $foo = 1;
while ($foo < 10) {
    print "foo = $foo\n";
    $foo++;
}
until文

until (...) {...} は、(...) が真になるまで {...} を繰り返し実行します。

my $foo = 1;
until ($foo > 10) {
    print "foo = $foo\n";
    $foo++;
}
do文

do {...} を用いて、if (...), unless (...), while (...), until (...) を下記の様に記述することができます。

do { print "foo = $foo\n"; } if ($foo == 5);
do { print "foo = $foo\n"; } unless ($foo == 5);
do { print "foo = $foo\n"; $foo++; } while ($foo < 5);
do { print "foo = $foo\n"; $foo++; } until ($foo > 5);
do {...} の中身が単文の場合、do { ; } を省略することができます。
print "foo = $foo\n" if ($foo == 5);
print "foo = $foo\n" unless ($foo == 5);
print "foo = $foo\n" while ($foo++ < 5);
print "foo = $foo\n" until ($foo++ > 5);
for文

for (expr1; expr2; expr3) {...}expr1 を実行し、expr2 が真の間、expr3 と {...} を実行します。下記の例では、$i に 1 を代入し、$i が 10 より小さい間、$i をインクリメントしながら print を実行します。

for (my $foo = 1; $foo < 10; $foo++) {
    print "foo = $foo\n";
}

次の様に書くこともできます。

my @foo = (1, 2, 3, 4, 5, 6, 7, 8, 9);
for (@foo) {
    print "foo = $_\n";
}

次の様に書くこともできます。

for (1..9) {
    print "foo = $_\n";
}
foreach文

foreach var (...) {...} は配列の各要素を var に代入しながら {...} の処理を行います。

my @foo = ("A", "B", "C");
foreach my $x (@foo) {
    print "$x\n";
}

var を省略すると $_ が使用されます。

my @foo = ("A", "B", "C");
foreach (@foo) {
    print "$_\n";
}
last文

last は最も内側のループを抜けます。

foreach my $n (1..3) {
    if ($n == 2) { last }
    print "$n\n";
}

LABEL を指定すると LABEL で指定したループを抜けます。

LOOP1: foreach my $n (1..3) {
    LOOP2: foreach my $m (1..3) {
        if (($n == 2) && ($m == 2)) { last LOOP1 }
        print "$n$m\n";
    }
}
next文

next は最も内側のループの次のループに移ります。下記の例は行頭が # で始まらない行を表示します。

open(my $in, "<", "file.txt");
while (my $line = readline($in)) {
    if ($line =~ /^#/) {
        next;
    }
    print $line;
}
close($in);

LABEL を指定すると LABEL で指定したループの次のループに移ります。

LOOP1: foreach my $n (1..3) {
    LOOP2: foreach my $m (1..3) {
        if (($n == 2) && ($m == 2)) { next LOOP1 }
        print "$n$m\n";
    }
}
redo文

redo は最も内側のループを再度繰り返します。next が次のループを実行するのに対して、redo は今のループを繰り返します。下記の例では $n がカウントアップしない点が next と異なります。LABEL を指定すると指定したループを繰り返します。

my @num;
print "Please enter 3 numbers from 1 to 10.\n";
foreach my $n (1..3) {
    print "$n> ";
    $num[$n] = <STDIN>;
    chomp($num[$n]);
    if (($num[$n] < 1) || (10 < $num[$n])) {
        print "Wrong number.\n";
        redo;
    }
}
print "You entered $num[1], $num[2], $num[3].\n";
continue文

continue {...} は、ループを繰り返す際の処理を記述します。例えば次の2つのコードは同じ動作となります。

for (my $i = 1; $i < 10; $i++) {
    print "$i\n";
}
my $i = 1;
while ($i < 10) {
    print "$i\n";
} continue {
    $i++;
}
goto文

goto LABEL はラベルの位置にジャンプします。あまり多様すると可読性の低いプログラムになってしまうことがあります。

sub concat {
    my($a, $b) = @_;
    if (!$a) { goto ERROR }
    if (!$b) { goto ERROR }
    return $a . $b;
ERROR:
    return "ERROR";
}
try-catch文

Perl 5.34 で try {...} catch {...} が導入されました。try { ... } の中で die を呼ぶと残りの処理をスキップし、catch { ... } の処理に移ります。

use feature qw(try);
no warnings "experimental::try";
try {
    my $err = func();
    if ($err) { die "ERROR!!!" }
} catch($e) {
    print "catch: $e";
}

die は try {...} から呼び出したサブルーチンの中で発生したものであっても補足します。

sub func() {
    die "ERROR!!";
}
try {
    func();
} catch($e) {
    print "catch: $e";
}
switch/given/when文

switch文を模倣するために下記のような書き方をします。

my $foo = "AAA";
for ($foo) {
    if (/AAA/) { print "AAA\n"; last }
    if (/BBB/) { print "BBB\n"; last }
    { print "Other\n"; last }
}

v5.10.1 以降では実験的機能ですが下記の書き方ができます。ただし given / when is experimental 警告が出ます。

use v5.10.1;
my $foo = "AAA";
given ($foo) {
    when (/AAA/) { print "AAA\n" }
    when (/BBB/) { print "BBB\n" }
    default      { print "Other\n" }
}

v5.14 以降ではさらに実験的な機能ですが下記の書き方ができます。v5.10.1~v5.14 でも動作しますがバグがあるようです。ただし、given, when の構文はまだ不安定な状態で使用するのは控えた方がよさそうです。

use v5.14;
my $foo = "CCC";
given ($foo) {
    print "AAA\n" when /AAA/;
    print "BBB\n" when /BBB/;
    default { print "Other\n" }
}
省略文("...")

Perl 5.12 から未実装の箇所に ... 文を記述することが可能となりました。プログラムのパースは成功しますが、実行時に Unimplemented at filename line n. の警告が表示されます。use v5.12 が必要です。

use v5.12;
print "Hello\n";
...;
print "Bye\n";

入出力

ファイル読み込み

ファイルからの読み込みは次のようにします。ここで、IN のような変数をファイルハンドルと呼びます。ファイルハンドルは IN でも HEHE でも好きな名前で構いませんが通常大文字にします。ファイルを読み書きする際は、このファイルハンドルを指定します。

open(IN, "file.txt");
while ($line = <IN>) {
    print $line;
}
close(IN);

変数 $line を省略した場合は、省略時の暗黙の変数 $_ が使用されます。前記の例は次のように記述することもできます。

open(IN, "file.txt");
while (<IN>) {
    print;
}
close(IN);

これは、次の記述と同じ意味を持ちます。

open(IN, "file.txt");
while ($_ = <IN>) {
    print $_;
}
close(IN);

もっと簡単に次のように記述することもできます。

open(IN, "file.txt");
print <IN>;
close(IN);

次のようにファイルの各行を配列に一括して読み込むこともできます。

open(IN, "file.txt");
my @lines = <IN>;
foreach my $line (@lines) {
    print $line;
}
close(IN);

ファイルを開けない時にエラー終了させるには次のようにします。

open(IN, "file.txt") or die "Can't open file\n";

ファイルハンドルを省略すると、標準入力、またはコマンドラインで指定したファイルをひとつづつオープンし、それを順次読み込むことができます。

while (<>) {
    print;
}

ファイル指定の場合は次のような処理と同等になります。

for (my $i = 0; $i <= $#ARGV; $i++) {
    open(IN, $ARGV[$i]);
    while (<IN>) {
        print;
    }
    close(IN);
}

IN などのファイルハンドルはグローバル変数の様に扱われてしまうため、Perl 5.6 からはファイルハンドルとして my で宣言するローカル変数を使用できるようになりました。

open(my $in, "file.txt");
while (my $line = readline($in)) {
    print $line;
}
close($in);
ファイル書き込み

ファイルに書き込むには次のようにします。

open(OUT, "> file.txt");
print(OUT "ABC\n");
close(OUT);

ファイルに追加書き込みするには次のようにします。

open(OUT, ">> file.txt");
print(OUT "こんにちわ\n");
close(OUT);

ファイルを読み書きモードで開くには次のようにします。

open(FH, "+< file.txt");      # 読み書きモード (ファイルが存在しなければエラー/ファイルの先頭から上書き)
open(FH, "+> file.txt");      # 読み書きモード (ファイルが存在しなければ作成/ファイルサイズを0にする)
open(FH, "+>> file.txt");     # 読み書きモード (ファイルが存在しなければ作成/ファイルの末尾から追記)

最近では ">" などのモードを第二引数として指定できるようになりました。ファイルハンドルも my 変数を使用した方がベターです。

open(my $fh, ">", "file.txt");
open(my $fh, ">>", "file.txt");
open(my $fh, "+<", "file.txt");
open(my $fh, "+>", "file.txt");
open(my $fh, "+>>", "file.txt");
外部コマンド実行

出力結果を外部コマンドに渡すこともできます。次の例ではprint文の出力を sort に渡してソートします。

open(OUT, "| /usr/bin/sort");
print OUT "BBB\n";
print OUT "CCC\n";
print OUT "AAA\n";
close(OUT);

外部コマンドの出力結果を読み取ることもできます。次の例では、file.txt の中身を nkf コマンドでEUCに変換したデータを読み込みます。

open(IN, "/usr/bin/df |");
while (<IN>) {
    print;
}
close(IN);

単に外部コマンドを実行するだけであれば、次のようにします。

system("/usr/bin/df");
標準入出力

次の3つのファイルハンドルがあらかじめオープンされています。

STDIN     標準入力
STDOUT    標準出力
STDERR    標準エラー出力

標準入力から各行を読み込むには次のようにします。

while (<STDIN>) {
    print;
}

STDIN を省略すると、コマンド引数で何も指定しなかった場合は標準入力から、コマンド引数でファイルのリストを指定した場合は、そのファイルを順次読み取ることができます。

while (<>) {
    print;
}
改行コード

改行コードは、Linux などの UNIX系システムでは LF(\n)、Windowsでは CR LF(\r\n)、Mac は Mac OS 9 までは CR(\r) でしたが、Mac OS X 以降は LF(\n) のように異なります。入力ファイルの改行コードの差異に関わらず、動作環境に合わせた改行コードに変換するには下記の様にします。

while (my $line = readline($in)) {
    $line =~ s/[\r\n]*$//;
    print "$line\n";
}
テキストモード・バイナリモード

前述の例で Windows では $_\n ではなく $_\r\n にすべきだと思うかもしれませんが、Windows は STDOUT やファイルへの出力は \n を \r\n に自動変換して書き込みます。これを テキストモード と呼びます。この自動変換を行わないようにするには binmode() を用いて バイナリモード にします。

open(my $in, "<", "infile.txt");
open(my $out, ">", "outfile.txt");
binmode($out);         # 出力をバイナリモードにする(Windowsで\nを\r\nに変換しない)
while (my $line = readline($in)) {
    $line =~ s/[\r\n]*$//;
    print $out "$line\n";
}
close($in);
close($out);
バイナリ読み込み

次のようなコードでファイルをバイナリデータとして読み込む事ができます。

open(my $in, "binary.dat");         # ファイルを開く
binmode($in);                       # バイナリモードで読み書きする
my $size = -s "binary.dat";         # ファイルサイズを得る
sysread($in, my $buf, $size);       # ファイルサイズ分バイナリモードで読み込む
close($in);                         # ファイルをクローズする
my @data = unpack("C*", $buf);      # バイナリデータを数値配列に変換する
for my $x (@data) {
    printf("%02x ", $x);            # 1バイトずつ16進数で表示する
}
print "\n";

サブルーチン

サブルーチン(sub)

次のようにしてサブルーチンを宣言することができます。

sub add {
    return($_[0] + $_[1]);
}

サブルーチンは宣言しただけでは実行されませんが、次のように呼び出す事によって実行されます。

print add(3, 5);

サブルーチンに渡された引数は特殊配列変数 @_ に格納され、$_[0], $_[1], $_[2], ...で参照することができます。下記の様にすると、第一引数を $x、第二引数を $y の名前で受け取れるようになります。

sub add {
    my ($x, $y) = @_;
    return $x + $y;
}
print add(3, 5) . "\n";

return(...) は ... をサブルーチンの戻り値として返し、サブルーチンを終了します。

sub add {
    my ($x, $y) = @_;
    return $x + $y;
}

return() を省略した場合は、サブルーチンの最後の文(statement)の結果がサブルーチンの戻り値となります。

sub add {
    my ($x, $y) = @_;
    $x + $y;
}
変数のスコープ

my で宣言した変数はそのブロック内でのみ有効です。{...} の外部で定義した $foo と内部で定義した $foo は別の変数として扱われます。

my $foo = "ABC";         # グローバル変数
{
    my $foo = "XYZ";     # ローカル変数 ({...}内のみで有効)
    print "$foo\n";      # => XYZ (ローカル変数)
}
print "$foo\n";          # => ABC (グローバル変数)
参照渡し

サブルーチンに複数の配列を渡す場合、下記の様に渡すと (1, 2, 3, 4, 5, 6) というひとつの配列に結合されてしまいます。

sub printList {
    foreach my $x (@_) { print "$x "; }    # => 1, 2, 3, 4, 5, 6
    print "\n";
}
my @a = (1, 2, 3);
my @b = (4, 5, 6);
printList(@a, @b);

複数の配列として渡すには参照渡しという仕組みを用います。呼び出し側は \@変数名 で配列への参照(ポインタ)を渡します。受け取り側は参照(ポインタ)を $変数名 で受け取り、@$変数名 で元の配列を参照します。

sub printList {
    my ($list1, $list2) = @_;
    foreach my $x (@$list1) { print "$x "; }    # => 1, 2, 3
    print "\n";
    foreach my $x (@$list2) { print "$x "; }    # => 4, 5, 6
    print "\n";
}
my @a = (1, 2, 3);
my @b = (4, 5, 6);
printList(\@a, \@b);
プロトタイプ

プロトタイプを用いてサブルーチンの引数の型を指定することができます。$ は引数はスカラー値であることを示します。

sub double($) {
    my ($x) = @_;
    return $x * 2;
}
printf("%d\n", double(5));          # => 10

$$ は第1引数はスカラー値、第2引数もスカラー値であることを示します。

sub add($$) {
    my ($x, $y) = @_;
    return $x + $y;
}
printf("%d\n", add(3, 5));          # => 10

@ は引数がリストであることを示します。

sub total(@) {
    my (@list) = @_;
    my $total = 0;
    foreach my $n (@list) { $total += $n }
    return $total;
}
printf("%d\n", total(1, 2, 3, 4, 5));   # => 15

% は引数がハッシュであることを示します。

sub print_hash(%) {
    my (%hash) = @_;
    while (my ($key, $value) = each(%hash)) { print "$key = $value\n" }
}
print_hash(R=>"Red", G=>"Green", B=>"Blue");

\$, \@, \% はスカラー値、リスト、ハッシュのリファレンスであることを示します。

sub mypush(\@$) {
    my ($ref_of_arr, $elm) = @_;
    @$ref_of_arr[$#$ref_of_arr + 1] = $elm;
}

sub mypop(\@) {
    my ($ref_of_arr) = @_;
    splice(@$ref_of_arr, @$ref_of_arr - 1, 1);
}

my @arr = (1, 2, 3, 4, 5);
mypush(@arr, 6);
say @arr;                       # => 123456
say mypop(@arr);                # => 6
say @arr;                       # => 12345

セミコロン(;)は以降の引数が省略可能であることを示します。

sub mysubstr($$;$) {
    my ($expr, $offset, $length) = @_;
    my @strarr = split(//, $expr);
    if (defined($length)) {
        return join("", @strarr[$offset..$offset + $length - 1]);
    } else {
        return join("", @strarr[$offset..length($expr) - 1]);
    }
}
say mysubstr("ABCDEFG", 2, 3);       # => CDE
say mysubstr("ABCDEFG", 2);          # => CDEFG

アンパサンド(&)はブロックを受け取ります。

sub do_twice(&) {
    my ($block) = @_;
    &$block;
    &$block;
}
do_twice { print "Do!\n" };        # Do!が2回表示される
sub mygrep (&@) {
    my ($code) = shift;
    my @result;
    foreach (@_) { push(@result, $_) if (&$code) }
    return @result;
}
print mygrep { $_ % 2 == 0 } (1, 2, 3, 4, 5, 6);   # => 2, 4, 6
sub mytry (&@) {
    my ($mytry, $mycatch) = @_;
    eval { &$mytry };
    if ($@) {
        local $_ = $@;
        &$mycatch;
    }
}
sub mycatch (&) { $_[0] }
mytry {
    die "mytry";
} mycatch {
    print "Catch!\n";
};     # セミコロン(;)を忘れないように

アンダーバー(_)は暗黙の引数 $_ を受け取ります。

sub myprint(_) {
    my ($val) = @_;
    print $val;
    print "\n";
}
for (1..5) { myprint }             # => 1, 2, 3, 4, 5

アスタリスク(*)は 型グロブ を受け取ります。ファイルハンドルを受け取る際などに利用されます。

sub myreadline(*) {
    my ($in) = @_;
    return readline($in);
}
open(IN, "file.txt");
while (my $line = myreadline(IN)) { print $line }
close(IN);

ライブラリ

ライブラリ

別ファイルにライブラリとしてサブルーチンなどを定義することができます。下記の内容のファイルを ~/lib/mylib.pl として保存してください。ファイルの末尾に 1; など真と見なされる値を記述する必要があります。

mylib.pl
sub hello {
    print "Hello!\n";
}
1;

環境変数 PERL5LILB にライブラリファイルを配置するディレクトリを指定してください。

$ export PERL5LIB=~/lib

reuqire を用いてライブラリファイルを読み込み、ライブラリ内のサブルーチンを呼び出すことができます。

require "mylib.pl";
hello();
パッケージ(package)

サブルーチン名の重複を避けるために、パッケージ名を指定することができます。~/lib/mylib.pl に package 定義を追加します。

package mylib;
sub hello {
    print "Hello!\n";
}
1;

呼び出し側もパッケージ名をつけて呼び出します。

require "mylib.pl";
mylib::hello();

古い Perl では :: の代わりに ' を使用していました。

require "mylib.pl";
mycalc'hello();

モジュール

モジュール

Perl5 ではライブラリの代わりに モジュール がサポートされました。ライブラリとモジュールの差異には下記などがあります。

下記にモジュールのサンプルを示します。

MyMod.pm
package MyMod;
our $VERSION = '1.00';
sub hello {
    print "Hello!\n";
}
1;
use MyMod;
MyMod::hello();
インポート(import)

モジュールが読み込まれると import サブルーチンが自動的に呼び出されます。

package MyMod;
sub import { ... }
エクスポート(@EXPORT)

下記の様にして変数やサブルーチンをエクスポートすることができます。

package MyMod;
use Exporter ();
our @ISA = qw(Exporter);
our @EXPORT = qw(hello);
sub hello {
    print "Hello!!\n";
}
1;

エクスポートされたシンボルはモジュール名無しで呼び出すことができます。

use MyMod;
hello();
エクスポート許可(@EXPORT_OK)

下記の様にしてインポート要求があるもののみをエクスポートすることができます。

package MyMod;
use Exporter ();
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(hello);
sub hello {
    print "Hello!!\n";
}
1;

インポート要求は use 文の末尾にリストで指定します。

use MyMod qw(hello);
hello();

どのシンボルをインポートするかは下記の規則に従います。

use MyMod;             # @EXPORT に含まれるものをすべてインポートする
use MyMod ();          # 何もインポートしない
use MyMod ("a", "b");  # @EXPORT_OK に含まれるもののうち a と b のみをインポートする
use MyMod qw(a b);     # MyMod ("a", "b")と同じ
エクスポートのグルーピング(%EXPORT_TAGS)

use Math(sin cos tan ...) のようにシンボルを列挙しなくてもすむように、シンボルをタグ名でグルーピングすることができます。

package MyMod;
use Exporter ();
our @ISA = qw(Exporter);
our @EXPORT = qw(a1 a2 a3);
our @EXPORT_OK = qw(b1 b2 b3);
our %EXPORT_TAGS = (
    Tag1 => [qw(a1 b1)],
    Tag2 => [qw(a2 b2)],
    Tag3 => [qw(a3 b3)],
);
sub a1 { print "A1\n" }
sub a2 { print "A2\n" }
sub a3 { print "A3\n" }
sub b1 { print "B1\n" }
sub b2 { print "B2\n" }
sub b3 { print "B3\n" }
1;

インポート例を下記に示します。:DEFAULT は @EXPORT されたシンボル (a1, a2, a3) を意味します。:Tag1 はタグ名 Tag1 に指定されたシンボル (a1, b1) を意味します。b3 は @EXPORT_OK されたシンボル (b3) を意味します。!b1 は b1 をインポート対象から取り除きます。下記の例では、(a1, a2, a3) + (a1, b1) + (b3) - (b1) で、(a1, a2, a3, b3) がインポートされます。

use MyMod qw(:DEFAULT :Tag1 b3 !b1);
モジュールのネスト(::)

MyMod.pm を Some ディレクトリの下に Some/MyMod.pm として配置すると、Some::MyMod モジュールとして参照できるようになります。

use Some::MyMod;

リファレンス(\)

リファレンス(\)

Perl5 からC言語のポインタに相当するリファレンスがサポートされました。\$foo, \@foo, \%foo はそれぞれの変数へのリファレンスを示します。リファレンスに $, @, % をつけるとリファレンス先の値を得る(デリファレンスする)ことができます。

# スカラーのリファレンス
my $num1 = 123;
my $num_ref = \$num1;      # リファレンスを得る
my $num2 = $$num_ref;      # デリファレンスする
print "$num2\n";

# 配列のリファレンス
my @arr1 = ("A", "B", "C");
my $arr_ref = \@arr1;      # リファレンスを得る
my @arr2 = @$arr_ref;      # デリファレンスする
print @arr2;
print "\n";

# ハッシュのリファレンス
my %hash1 = (height => 123, width => 456);
my $hash_ref = \%hash1;    # リファレンスを得る
my %hash2 = %$hash_ref;    # デリファレンスする
print $hash2{"height"} . ", " . $hash2{"width"} . "\n";

[...] は配列へのリファレンスを返します。

my $foo = [1, 2, 3];
printf("%d\n", $foo->[0]);

{...} はハッシュへのリファレンスを返します。

my $foo = {name => "Yamada", age => 26};
print $foo->{"name"} . "\n";             # => Yamada
リファレンスの参照

-> を用いてリファレンスから直接リファレンス先の値を参照することもできます。

my @arr1 = ("A", "B", "C");
my $arr_ref = \@arr1;
print $arr_ref->[0] . "\n";

my %hash1 = (height => 123, width => 456);
my $hash_ref = \%hash1;
print $hash_ref->{"height"} . "\n";

$foo[n]->[m] は $foo[n][m] と書くことができます。

my @foo = ([[1, 2], [3, 4]], [[5, 6], [7, 8]]);
printf("%d\n", $foo[1]->[1]->[1]);     # => 8
printf("%d\n", $foo[1][1][1]);         # => 8
複雑なリファレンス

配列の配列は、配列の要素に配列リファレンスを加えます。

my @foo = ([1, 2, 3], [4, 5, 6]);
foreach my $f (@foo) {
    printf("%d, %d, %d\n", $f->[0], $f->[1], $f->[2]);
}

ハッシュの配列は、配列の要素にハッシュリファレンスを加えます。

my @foo = (
    {name => "Yamada", age =>26},
    {name => "Suzuki", age =>32}
);
foreach my $f (@foo) {
    printf("%s(%d)\n", $f->{"name"}, $f->{"age"});
}

ハッシュのハッシュは、ハッシュの要素にハッシュリファレンスを加えます。

my %foo = (
    name => {first_name => "Taro", last_name => "Yamada"},
    addr => {prefecture => "Tokyo", city => "Minato-ku"}
);
printf("%s\n", $foo{"name"}{"first_name"});
printf("%s\n", $foo{"addr"}{"prefecture"});

ハッシュリファレンスを要素に持つハッシュのリファレンスも使用できます。

my $foo = {
    name => {first_name => "Taro", last_name => "Yamada"},
    addr => {prefecture => "Tokyo", city => "Minato-ku"}
};
printf("%s\n", $foo->{"name"}{"first_name"});
printf("%s\n", $foo->{"addr"}{"prefecture"});

その他

コメント(#)

ハッシュ記号(#)から行末まではコメントとして無視されます。

$foo = 5;    # ここから行末までは無視されます
正規表現(/.../)

/ABC/ は ABC という文字を含んでいるかという 正規表現 を表します。=~ は正規表現にマッチするか否かを判断する演算子です。

my $foo = "ABCDEFG";
if ($foo =~ /ABC/) {          # 文字列$fooが文字列ABCを含んでいたら
    print "含んでいる\n";
}

演算子 !~ は正規表現にマッチしなければ真という演算子です。

my $foo = "ABCDEFG";
if ($foo !~ /ABC/) {          # 文字列$fooが文字列ABCを含んでいなければ
    print "含んでいない\n";
}

正規表現には下記の表現があります。詳細は「とほほの正規表現入門」を参照してください。

A           Aという文字
ABC         ABCという文字列
A+          1個以上連続したA(A, AA, AAA, ...)
A*          0個以上連続したA(  , A, AA, AAA, ...)
A?          0または1つのA(  , A)
A{3}        3個のA
A{3,}       3個以上のA
A{,5}       5個以下のA
A{3,5}      3個以上5個以下のA
.           1つの任意文字(A, B, C, ...) (\nを除く)
^ABC        ABCで始まっていたら
ABC$        ABCで終わっていたら
[ABC]       A,B,Cのいずれか1文字
[A-Z]       A~Zまでのいずれか1文字
[A-Za-z0-9] A~Z, a~z, 0-9までのいずれか1文字
[^ABC]      A,B,C以外の文字
[^A-Z]      A~Z以外の文字
AA|BB|CC    AAまたはBBまたはCC
\w          英数文字かアンダーバー(_)。[a-zA-Z0-9_]と同様
\W          \w以外の文字
\d          数値文字。[0-9]と同等
\D          \d以外の文字
\s          空白文字。[\ \t\r\n\f]と同等
\S          \s以外の文字
\b          単語の区切り
\B          \b以外の文字
\n          エスケープシーケンス文字の\n
\nnn        文字コード(8進数)
\xnn        文字コード(16進数)
(ABC)       ABCという文字列。カッコ部分は後で参照可能です。

正規表現中では下記の文字や、/.../ の場合は / は特殊な文字(メタ文字)として扱われます。

+ * ? . ( ) [ ] { } | \ ^ $

メタ文字自身を表わすには \/, \+, \*, \?, ...のようにバックスラッシュ(\)でエスケープします。

if ($foo =~ /^http:\/\//) { ... }    # http:// で始まっていれば

先頭に m をつけると、/.../ の代わりに m|...| や m(...) など、/.../ 以外の記号を使用することができます。この場合 / はメタ文字ではなくなります。

if ($foo =~ m|http://|) { ... }

括弧(...)で囲んだ部分は、正規表現内では \1, \2, \3, ...として参照できます。1, 2, 3, ...は、正規表現中で括弧が現れる順番を示します。

if ($foo =~ /^(...)\1/) { ... }    # 最初の3文字をもう一度繰り返す(ABCABCなど)

正規表現の外ではマッチの結果を次のように参照できます。

$1, $2, $3, ...      (  )の部分にマッチした文字列
$+                   最後にマッチした(  )部分
$&                   マッチした部分の文字列
$`                   マッチした部分から前側の文字列
$'                   マッチした部分から後側の文字列

たとえば、次のように使用することができます。

my $time = "23:59:59";
my ($hour, $min, $sec);
if ($time =~ /^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$/) {
    $hour = $1;    # => 23
    $min = $2;     # => 59
    $sec = $3;     # => 59
}

/.../ の後に g をつけると正規表現マッチングを繰り返し行います。下記の例では1回目のループでABCに、2回目でDEFに、3回目でGHIにマッチします。

my $foo = "ABC DEF GHI";
while ($foo =~ /([A-Z]+)/g) {
    print "$1\n";                # => ABC DEF GHI
}
ヒアドキュメント(<<)

ヒアドキュメント を用いて複数行の文字列を表現することができます。下記の例は 3行の Hello world! を表示します。EOD は END_OF_DATA でも OWARI でもどんな文字列でも使用できますが、(1)<<の後にスペースをいれてはならない、(2)最初のEND_OF_DATAの後ろにはセミコロン(;)が必要、(3)最後のEND_OF_DATAの後ろには改行が必要、など注意点も多いので気を付けてください。

print <<EOD;
Hello World!
Hello World!
Hello World!
EOD

次の例では、変数 $msg に 3行のメッセージを代入しています。

my $msg = <<EOD;
This is Japan.
That is America.
That is England.
EOD
print $msg;

最初の EOD を EOD としたり "EOD" のようにダブルクォート(")で囲む場合は文字列の中で変数が展開されます。下記の例では Hello Tanaka! が3行表示されます。

my $name = "Tanaka";
print <<"EOD";
Hello $name!
Hello $name!
Hello $name!
EOD

EOD をシングルクォート(')で囲む場合は変数の展開は行われません。下記の例では Hello $name! が3行表示されます。

my $name = "Tanaka";
print <<'EOD';
Hello $name!
Hello $name!
Hello $name!
EOD
コマンド実行(`...`)

`...` に囲まれた文字は変数展開の後外部コマンドとして実行され、その結果を返します。下記の例は外部コマンド /bin/ls -lF dir を実行し、その結果を読み取っています。

my $dir = "/usr";
my @files = `ls -lF $dir`;
foreach my $file (@files) {
    print $file;
}
日本語の取り扱い

日本語には、シフトJIS、EUC、JIS(ISO-2022-JP)、UTF-8 などいくつかの文字コードがあります。Windows はまだ Shift_JIS 系と UTF-8 が混在していますが、UTF-8 を使用するケースが増えてきました。Perl で UTF-8 を扱うには下記のおまじないを行います。

use utf8;

標準入出力で日本語を扱う場合は下記を追加します。

use utf8;
binmode STDIN, ":encoding(utf-8)";
binmode STDOUT, ":encoding(utf-8)";
binmode STDERR, ":encoding(utf-8)";
print "あいうえお\n";

日本語のファイルを読み書きするにはモードにエンコーディングルールを指定します。次の例では UTF-8 のファイルを読み込み、Shift_JIS のファイルに書き込んでいます。文字コードには utf-8, Shift_JIS, ISO-2022-JP, EUC-JP などを指定できます。

use utf8;
binmode STDIN, ":encoding(utf-8)";
binmode STDOUT, ":encoding(utf-8)";
binmode STDERR, ":encoding(utf-8)";

open(my $in, "<:encoding(utf-8)", "file.txt");
open(my $out, ">:encoding(Shift_JIS)", "out.txt");
while (my $line = readline($in)) {
    print $out $line;
}
close($in);
close($out);

文字コードを変換するには Encode モジュールを使用します。

use Encode;
$str = Encode::decode("utf-8", $str);   # 外部コード文字列(UTF-8)を内部コード文字列にデコード
$str = Encode::encode("utf-8", $str);   # 内部コード文字列を外部コード文字列(UTF-8)にエンコード
Perlのバージョン指定

use v... を指定すると最低限必要とする perl のバージョンを指定することができます。

use v5.34;

v5.12 以上を指定すると use strict が自動的に宣言されます。v5.36 以上を指定すると use warnings も自動的に宣言されます。

use v5.12      # 暗黙的に use strict が宣言される
use v5.36      # 暗黙的に use warnings も宣言される
バージョン文字列

v で始まる v1.2 や、1.2.3 のように複数のピリオドを持つ値はバージョン文字列と呼ばれます。v1.2 は "\x{1}\x{2}"、1.2.3 は "\x{1}\x{2}\x{3}" と等価です。数字は 0~65533 が使用できるようです。文字列の "v1.2" と "v1.12" を比較すると "v1.2" の方が大きくなってしまいますが、バージョン文字列の v1.2 と v1.12 を比較すると v1.12 の方が大きいと判断されます。

if (v1.2 eq "\x{1}\x{2}")       { print "OK\n" } else { print "NG\n" }    # => OK
if (1.2.3 eq "\x{1}\x{2}\x{3}") { print "OK\n" } else { print "NG\n" }    # => OK
if ("v1.12" gt "v1.2")          { print "OK\n" } else { print "NG\n" }    # => NGになってしまう
if (v1.12 gt v1.2)              { print "OK\n" } else { print "NG\n" }    # => OK
組み込みドキュメント(=...)

=string で始まる行を見つけると =cut で始まる行までを組み込みドキュメントとして読み飛ばします。perlpod(Perl Plain Old Documentation) に従った記述を行っておくと perldoc コマンドでマニュアルとして参照することができます。

=head1 NAME
  add : Addition

=head1 SYNOPSIS
  add(3, 5)

=head1 DESCRIPTION
  Returns the sum of the first and second arguments.
=cut
sub add { $_[0] + $_[1] }
$ perldoc add.pl
行指示子(# line)

# line lineno filename の行があると、次の行を filenamelineno 行目とみなします。下記を実行すると syntax error at foo.pl line 100, at EOF エラーとなります。

# line 100 "foo.pl"
@foo = (1, 2
BEGIN・ENDブロック

BEGIN ブロックはプログラムの最初、END ブロックはプログラムの最後に一度だけ実行される処理を記述します。

my $lineno = 1;
BEGIN {
    print "----BEGIN----\n";
}
while (<>) {
    printf("%d: %s", $lineno++, $_);
}
END {
    print "----END----\n";
}
do {...} 中のnext,last文

do {...} はループ処理ではないため、この中で next や last を使用する場合は注意が必要です。

# next を使用する場合は do {{...}} の様に中括弧を二重にする
my $foo = 1;
do {{
    if ($foo == 2) { next }
    print "$foo\n";
}} while ($foo++ < 5);
# last を使用する場合は { do {...} } の様に do文を中括弧で囲む
my $foo = 1;
{
    do {
        if ($foo == 4) { last }
        print "$foo\n";
    } while ($foo++ < 5);
}
# next と last 両方を使用する場合は LABEL { do {{...}} } の様に両方対応してラベルもつける
my $foo = 1;
LOOP: {
    do {{
        if ($foo == 2) { next }
        if ($foo == 4) { last LOOP }
        print "$foo\n";
    }} while ($foo++ < 5);
}

Copyright (C) 1997-2023 杜甫々
初版:1997年2月3日、最終更新:2023年10月8日
http://www.tohoho-web.com/perl/basic.html