do

外部の Perl スクリプトを実行する

構文

解説

do は、後ろにブロックが続く (do BLOCK) のか、 文字列が続く (do EXPR) のかによって、その役割は大きく異なります。

do BLOCK

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 構文は、whileuntil のループと組み合わせて使うこともできます。 この場合、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 );

なお、冒頭で解説した whileuntil の組み合わせではない do BLOCK 構文は、ループとして考慮されません。そのため、 next, last, redo といったループ制御文は機能しませんので注意してください。

また、do BLOCK は関数ではありませんので、 ブロックの中で return を使うことはできません。

do EXPR

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 を実行すると、 do3 を返します。

my $res = do './stat.pl';
print $res, "\n";    # 3

do が Perl スクリプトの実行に失敗すれば、 doundef を返します。 Perl スクリプトファイルが見つからなかった場合、失敗理由は $! にセットされます。 一方で、Perl スクリプトは見つかったものの、Perl スクリプトの実行でエラーが出たのであれば、 $@ にその理由がセットされます。 いずれにせよ、do はそのような状況に陥っても例外を投げません。 そのため、すべてのエラーを捕捉したいなら、まず $@ を評価し、その後、$! を評価するようにしてください。

do './stat.pl';
if ($@) {
    print "Failed to run the script: " . $@;
}
elsif ($!) {
    print "The script was not found: " . $!;
}