外部の Perl スクリプトを実行する
do は、後ろにブロックが続く (do BLOCK) のか、
文字列が続く (do EXPR) のかによって、その役割は大きく異なります。
do の後ろにブロックが続く場合、その do は関数ではありません。
ブロックの中にはコマンドをいくつか入れることができますが、
do は、最後のコマンドの実行結果を返します。
次の例は 0 ~ 99 の間のランダムな整数を抽出し、それが 3 の倍数なら 1 を、
そうでなければ 0 を返します。
my $result = do {
my $num = int( rand(100) ); # 0 ~ 99 の整数のどれか
( $num % 3 ) ? 0 : 1; # 3 の倍数なら 1
};
print $result, "\n"; # 0 または 1
この構文の使いどころとしては、if の条件式などが挙げられます。
1 行では無理だけれども数行なら何とかなる、といった状況で便利かもしれません。
if (
do {
my $num = int( rand(100) ); # 0 ~ 99 の整数のどれか
( $num % 3 ) ? 0 : 1; # 3 の倍数なら 1
}
)
{
print "Bingo!\n";
}
else {
print "Sorry.\n";
}
do BLOCK 構文は、while や until
のループと組み合わせて使うこともできます。
この場合、BLOCK の部分はループの条件式が評価される前に 1 度実行されます。
つまり、本来 while なら一度も実行されなかったはずであっても、do-while 構文なら
1 度は必ず実行されることになります。
次のコードは while によるループですが、変数 $num
の初期値が条件を満たさないため、ブロックは一度も実行されません。
my $num = 0;
while ( $num > 0 && $num < 5 ) {
print $num, "\n"; # このブロックは一度も実行されない
$num++;
}
一方、次の do-while によるループは、変数 $num
の初期値は条件を満たさないものの、その条件を評価する前に 1 度ブロックを実行するため、
以降、ループが継続します。
my $num = 0;
do {
print $num, "\n";
$num++;
} while ( $num > 0 && $num < 5 );
なお、冒頭で解説した while や until の組み合わせではない
do BLOCK 構文は、ループとして考慮されません。そのため、
next, last,
redo といったループ制御文は機能しませんので注意してください。
また、do BLOCK は関数ではありませんので、
ブロックの中で return
を使うことはできません。
do EXPR 構文は、EXPR に Perl
スクリプトのファイル名(ファイルパス)を指定することで、そのスクリプトを実行してくれます。
do './stat.pl';
この do BLOCK 構文は、実質的に次のコードと同じです。
eval `cat stat.pl`;
EXPR が / や . で始まらなければ、
@INC から指定のスクリプトファイルを検索します。
use lib '.'; # @INC にカレントディレクトリを追加
do 'stat.pl';
do は引数の Perl スクリプトの実行が成功すれば、そのスクリプトの最後の行の実行結果を返します。
./stat.pl の最後が次のようなコードだったとしましょう。
my @ary = (1, 2, 3);
scalar @ary;
このとき、do で ./stat.pl を実行すると、
do は 3 を返します。
my $res = do './stat.pl';
print $res, "\n"; # 3
do が Perl スクリプトの実行に失敗すれば、
do は undef を返します。
Perl スクリプトファイルが見つからなかった場合、失敗理由は $! にセットされます。
一方で、Perl スクリプトは見つかったものの、Perl スクリプトの実行でエラーが出たのであれば、
$@ にその理由がセットされます。
いずれにせよ、do はそのような状況に陥っても例外を投げません。
そのため、すべてのエラーを捕捉したいなら、まず $@ を評価し、その後、$!
を評価するようにしてください。
do './stat.pl';
if ($@) {
print "Failed to run the script: " . $@;
}
elsif ($!) {
print "The script was not found: " . $!;
}