web-dev-qa-db-ja.com

Perlスクリプト内からPerlスクリプトを実行するにはどうすればよいですか?

別のPerlスクリプトを実行する必要があるPerlスクリプトがあります。この2番目のスクリプトはコマンドラインで直接実行できますが、最初のプログラム内から実行する必要があります。スタンドアロンで実行するときに通常渡されるいくつかのパラメーターを渡す必要があります(最初のスクリプトは定期的に実行され、特定のシステム条件の下で2番目のスクリプトを実行します)。

Googleの予備検索では、バックティックまたはsystem()コールの使用が提案されています。それを実行する他の方法はありますか? (私たちが話しているPerlだから:P)はいプログラムは直接呼び出されました)?

(編集:ああ、nowSOは関連する質問を提案します。 この質問 は近いです2番目のプログラムは実行に1時間以上かかる可能性が高いため(I/Oのロット)、1回限りの呼び出しがこれに適しているかどうかはわかりません)

24
cbowns

現在のPerlインタープリターの場所は、特殊変数$^Xにあります。これは、Perlがパスにない場合、または複数のPerlバージョンが利用可能であるが、全体で同じバージョンを使用していることを確認する場合に重要です。

他のPerlプログラムを含む外部コマンドを実行する場合、それらが実際に実行されたかどうかを判断することは非常に困難です。 $?を調べると永続的な精神的傷跡が残る可能性があるため、 IPC :: System :: Simple (CPANから入手可能)を使用することを好みます。

use strict;
use warnings;
use IPC::System::Simple qw(system capture);

# Run a command, wait until it finishes, and make sure it works.
# Output from this program goes directly to STDOUT, and it can take input
# from your STDIN if required.
system($^X, "yourscript.pl", @ARGS);

# Run a command, wait until it finishes, and make sure it works.
# The output of this command is captured into $results.
my $results = capture($^X, "yourscript.pl", @ARGS);

上記の両方の例で、外部プログラムに渡したい引数は@ARGSに入ります。上記の両方の例では、シェルも回避されます。これにより、速度がわずかに向上し、シェルのメタキャラクターが関係する不要な相互作用が回避されます。上記のコードは、2番目のプログラムが成功を示すゼロの終了値を返すことも想定しています。そうでない場合は、許容終了値の追加の最初の引数を指定できます。

 # Both of these commands allow an exit value of 0, 1 or 2 to be considered
 # a successful execution of the command.

 system( [0,1,2], $^X, "yourscript.pl", @ARGS );
 # OR
 capture( [0,1,2, $^X, "yourscript.pl", @ARGS );

実行時間の長いプロセスがあり、そのデータを処理したい場合while生成中の場合、おそらくパイプで開く必要があります。または、CPANからのより重い重量の1つIPCモジュール。

そうは言っても、Perlから別のPerlプログラムを呼び出す必要があるときはいつでも、モジュールを使用する方が良い選択かどうかを検討することができます。別のプログラムを起動すると、起動コストとプロセス間でデータを移動するためのI/Oコストの両方の面でかなりのオーバーヘッドがかかります。また、エラー処理の難易度も大幅に向上します。外部プログラムをモジュールに変換できる場合、全体的な設計が簡素化されることがあります。

ではごきげんよう、

ポール

30
pjf

do itとすることができます。

{
    local @ARGV = qw<param1 param2 param3>;
    do '/home/buddy/myscript.pl';
}

Perlの別のコピーをロードするオーバーヘッドを防ぎます。

32
Axeman

あなたはすでにあなたの質問に対する良い答えを手に入れましたが、異なる視点をとる可能性が常にあります。最初のスクリプトから実行したいスクリプトをリファクタリングすることを検討すべきでしょう。機能をモジュールに変換します。最初のスクリプトと2番目のスクリプトのモジュールを使用します。

12
innaM

これを行うにはいくつかの方法が考えられます。最初の2つについては既に述べたので、それらについては詳しく説明しません。

  1. バックティック:$ retVal = `` Perl somePerlScript.pl;
  2. system()呼び出し
  3. 評価する

Evalは、他のファイルを文字列(または文字列のリスト)に丸lurみし、文字列を「評価」することで実現できます。ここにサンプルがあります:

#!/usr/bin/Perl
open PERLFILE, "<somePerlScript.pl";
undef $/;   # this allows me to Slurp the file, ignoring newlines
my $program = <PERLFILE>;
eval $program;

4。行う:

「somePerlScript.pl」を実行
11
Colin

コマンドの出力をキャプチャする必要がある場合は、バックティックを使用します。

コマンドの出力をキャプチャする必要がない場合は、systemを使用します。

TMTOWTDI:他の方法もありますが、それらは最も簡単で最も可能性の高い2つの方法です。

6

外部スクリプトを非同期で呼び出す必要がある場合(終了するのを待たずに起動したいだけの場合):

# On Unix systems, either of these will execute and just carry-on
# You can't collect output that way
`myscript.pl &`;
system ('myscript.pl &');    

# On Windows systems the equivalent would be
`start myscript.pl`;
system ('start myscript.pl');

# If you just want to execute another script and terminate the current one
exec ('myscript.pl');
6
Renaud Bompuis

プロセス間通信のいくつかのオプションについては、 perlipc のドキュメントを参照してください。

最初のスクリプトが2番目のスクリプトの環境を設定するだけの場合、execを探している可能性があります。

5
brian d foy
#!/usr/bin/Perl
use strict;

open(OUTPUT, "date|") or die "Failed to create process: $!\n";

while (<OUTPUT>)
{
  print;
}

close(OUTPUT);

print "Process exited with value " . ($? >> 8) . "\n";

これにより、プロセスdateが開始され、コマンドの出力が出力ファイルハンドルにパイプされ、一度に1行を処理できます。コマンドが終了したら、出力ファイルハンドルを閉じて、プロセスの戻り値を取得できます。 dateを必要なものに置き換えます。

1
Robert Gamble

編集を簡単にするために、このようなことをして非サブルーチンを外部ファイルにオフロードしたかったのです。実際にこれをサブルーチンにしました。この方法の利点は、外部ファイル内のこれらの「my」変数がメイン名前空間で宣言されることです。 「do」を使用すると、明らかにメイン名前空間に移行しません。以下のプレゼンテーションにはエラー処理が含まれていないことに注意してください

sub getcode($) {
  my @list;
  my $filename = shift;
  open (INFILE, "< $filename");
  @list = <INFILE>;
  close (INFILE);
  return \@list;
}

# and to use it:

my $codelist = [];
$codelist = getcode('sourcefile.pl');
eval join ("", @$codelist);
0
Robert