2017-07-27 10 views
1

子プロセスをPerl5〜fork()にしようとしています。子プロセスは、STDINを名前付きパイプにリダイレクトする別のプログラムexec()とログファイルにSTDOUTSTDERRをリダイレクトする必要があります。親プロセスはループ内で実行を継続し、waitpidを使用し、$?をチェックして、ゼロ以外の終了ステータスで終了する場合に子プロセスを再起動します。exec()内でSTDOUTとSTDERRをリダイレクトするシェルなし

exec()機能のためのPerlのドキュメントは言う:LISTに複数の引数がある場合

、これは、リスト内の引数でexecvpの(3)を呼び出します。 LISTに要素が1つしかない場合は、引数にシェルメタキャラクタがあるかどうかチェックされ、存在する場合は引数全体がシステムのコマンドシェルに渡されます(Unixプラットフォームでは/bin/sh -cですが、他のプラットフォームで異なります)。引き数にシェルメタ文字がない場合は、単語に分割されてexecvpに直接渡されます。より効率的です。例:これはかなりクールなサウンド、私はこれらの例に示すように、中間のシェルなしで私の外部プログラムを実行したいと思います

exec '/bin/echo', 'Your arguments are: ', @ARGV; 
exec "sort $outfile | uniq"; 

。残念ながら、これを出力リダイレクション(/bin/foo > /tmp/stdoutのように)と組み合わせることはできません。言い換えれば

、これは動作しません:

exec ('/bin/ls', '/etc', '>/tmp/stdout'); 

だから、私の質問は:どのように私はシェルを使用せずに、私のサブコマンドのSTD*ファイルをリダイレクトするのですか?

答えて

5

<>のリダイレクションは、シェルの機能です。このため、この用途では機能しません。あなたは、本質的に/bin/lsを呼び出し、echoでコマンドを交換する際、容易に見ることが>/tmp/stdoutとしてだけで別の引数、渡している:

exec ('/bin/echo', '/etc', '>/tmp/stdout'); 

プリント:

/etc >/tmp/stdout 

通常、シェル(/bin/sh)が解析されているだろうしコマンドは、リダイレクトの試みを検出し、適切なファイルを開き、/bin/echoに入る引数リストを整理しました。

しかし - プログラムがexec()(またはsystem())で開始し、その呼び出し元のプロセスのSTDINSTDOUTSTDERRファイルを継承します。だから、これを処理する適切な方法は、

  • にある最終的には、プログラムを起動するexec()を呼び出して、ご希望のログファイルを指し、それらを再開き、
  • 、それぞれ特別なファイルハンドルを閉じます。

    close STDOUT; 
    open (STDOUT, '>', '/tmp/stdout'); 
    exec ('/bin/ls', '/etc'); 
    

    ...または、perldocが推奨する間接オブジェクト構文を使用して::

    close STDOUT; 
    open (STDOUT, '>', '/tmp/stdout'); 
    exec { '/bin/ls' } ('ls', '/etc'); 
    

    (実際には、応じて、これは正常に動作し、上記のあなたの例のコードを書き換え

この最終的な構文は、Windowsでシェルをインスタンス化するのを避ける唯一の信頼できる方法です)。

4

follo wing shellコマンドは、/bin/ls/etcで起動し、そのSTDOUTをリダイレクトする引数をシェルに伝えます。一方

/bin/ls /etc >/tmp/stdout 

、次のPerl文は/etcと引数の>/tmp/stdoutで、/bin/lsと現在のプログラムを置き換えるためにPerlを伝えます。

exec('/bin/ls', '/etc', '>/tmp/stdout'); 

あなたはPerlにSTDOUTをまったくリダイレ​​クトするように指示しませんでした。 execは新しいプロセスを開始しないので、子プロセスのfd 1を変更すると、lsが同じプロセスで実行されることを覚えておいてください。

むしろこの1つのだけ問題を修正するよりも(グレッグ・ケネディが行ったように)が、そのままあなたの他の問題を残して(例えばlsからエラーとしてlsを起動できないことを誤報)、私はそれらすべてを修正するためにあなたが表示されます:

これにより、コードの数百行が節約されましたが、open3は依然としてかなり低いレベルです。 (2つのパイプを扱う必要がある場合は、問題に遭遇します。)代わりに、IPC::Run3(より簡単)またはIPC::Run(より柔軟性)をお勧めします。

use IPC::Run3 qw(run3); 
run3([ '/bin/ls', '/etc' ], \undef, \my $stdout); 

または

use IPC::Run qw(run); 
run([ '/bin/ls', '/etc' ], \undef, \my $stdout); 
関連する問題