2017-10-31 16 views
1

一時的にstdoutをメモリ変数にリダイレクトしたいとします。プリントは私の変数に正しくリダイレ​​クトされますが、パイプの出力ではありません(私の例ではbc)。何が起こっている?STDOUTがパイプ出力をキャッチしない変数にリダイレクトされました

#!/usr/bin/perl 

my $stdout_sink; 
open(my $orig_stdout, ">&STDOUT") || die $!; 
close STDOUT; 
open(STDOUT, ">", \$stdout_sink) || die $!; 

# produce some output in different ways 
print "before bc\n"; # ok 
open my $fh, "| bc"; 
print $fh "2+2\n"; # not ok 
close $fh; 

close STDOUT; 
open(STDOUT, ">&", $orig_stdout) || die $!; 
print "$stdout_sink"; 

実際の出力に含まは次のようになります。

before bc 

予想される出力:

before bc 
4 
+0

'bc'はforkされ、独立したSTDOUTを持つ別のプロセスとして実行されます。 ['IPC :: Run3'](https:// metacpan)のようなものを使って実行する必要があります。org/pod/IPC :: Run3)を使用して結果を取得します。 – Borodin

+0

これが亀裂を通って落ちた場合のリマインダは、[誰かが私の質問に答えるとどうすればいいですか?](http://stackoverflow.com/help/someone-answers) – zdim

答えて

1

これは...できません。

パイプオープンの標準出力とsystem呼び出しがファイル記述子1に書き込まれます。通常、PerlのSTDOUTファイルハンドルはファイル記述子1に関連付けられていますが、それは操作できます。

この例では、systemコールはファイルfooに書き込むファイルハンドルSTDOUTに書き込みを行います。

close STDOUT;    # closes file descriptor 1 
open STDOUT, '>', 'foo'; # reopens STDOUT as file descriptor 1 
system("echo bar"); 
close STDOUT; 
print STDERR "foo: ",`cat foo`; 
# result: "foo: bar" 

しかし、この例では、system呼び出しはBAZファイルハンドルに書き込みます。

close STDOUT;    # closes file descriptor 1 
open BAZ, '>', 'baz';  # attaches fd 1 to handle BAZ 
open STDOUT, '>', 'foo'; # opens new file descriptor, maybe fd 3 
system("echo bar"); 
close STDOUT; 
print STDERR "foo: ",`cat foo`; 
print STDERR "baz: ",`cat baz`; 
# result: "foo: baz: bar" 

メモリ内ファイルハンドルは実際のファイルハンドルではありません。それにfilenoを呼び出すと、(通常、OSに依存するかもしれませんが)負の数になります。

open STDOUT, '>', \$scalar; 
print STDERR fileno(STDOUT);  # -1 

パイプで開いたファイルとシステムコールは、このファイルハンドルに書き込むことができません。

パイプで開いた出力をファイルに書き込んだ後、そのファイルをメモリ内の変数にコピーするなど、より複雑な回避策が必要になります。

2

を実際にファイルハンドルではない変数にリダイレクトできない理由については、mob's answerの詳細な説明があります。

代わりに、標準ストリームを変数にリダイレクトできる外部プログラムを実行するためのモジュールを使用できます。その後、リダイレクトされた出力と望むように文字列を組み合わせることができます。

ここで私はプリントがデフォルトでどこに行くか(指定したファイルハンドルなし)を操作するためにselectを使用IPC::Run3

use warnings; 
use strict; 
use feature 'say'; 

use IPC::Run3; 

open my $fh, ">", \my $so_1;  
my $old = select $fh;   # make $fh the default for output, 
say "To select-ed default"; # so prints end up in $so_1 

run3 ["ls", "-l", "./"], undef, \my $so_2; # output goes to $so_2 

select $old;     # restore STDOUT as default for output 
print $so_1, $so_2; 

と例。

run3は同じもの($so_1)にリダイレクトできないため、そこにあるものを上書きすることができます。モジュールは、このリダイレクションに一時ファイルを使用します(mobも回答に示されています)。

いくつかのオプションは、ほとんどすべてのコードから出力をリダイレクトすることができ、シンプルでクリーンなインターフェイスと非常に強力な、丸められた、より複雑なIPC::Runです。

関連する問題