Я пошел через документацию open3и вот часть, которую я не мог понять:
Если вы попытаетесь прочитать из дочернего модуля записи stdout и их stderr писатель, у вас будут проблемы с блокировкой, а значит, вы захотите используйте select() или IO::Select, что означает, что вам лучше всего использовать sysread() вместо readline() для обычных вещей.
Это очень опасно, так как вы можете заблокировать навсегда. Предполагается, что это собирается поговорить с чем-то вроде bc, и писать ему, и читать от него. Это предположительно безопасно, потому что вы «знаете», что такие команды, как bc будет читать строку за раз и выводить строку за раз. Программы однако сортировка, которая сначала читает весь входной поток, вполне может вызвать взаимоблокировку.
Итак, я попробовал open3
в надежде узнать его получше. Вот первая попытка:
sub hung_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $pid = open3(my $in, my $out, my $err = gensym(), $cmd);
print "[PID]: $pid\n";
waitpid($pid, 0);
if(<$err>) {
print "[ERROR] : $_" while(<$err>);
die;
}
print "[OUTPUT]: $_" while (<$out>);
}
Интересно отметить, что здесь я должен инициализировать $err
.
Во всяком случае, это просто зависает, когда я execute("sort $some_file");
учитывая, что $some_file
является текстовым файлом, содержащим более 4096 символов (ограничения для моей машины) .
Затем я просмотрел этотFAQ, и ниже была моя новая версия execute:
sub good_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $in = gensym();
#---------------------------------------------------
# using $in, $out doesn't work. it expects a glob?
local *OUT = IO::File->new_tmpfile;
local *ERR = IO::File->new_tmpfile;
my $pid = open3($in, ">&OUT", ">&ERR", $cmd);
print "[PID]: $pid\n";
waitpid($pid, 0);
seek $_, 0, 0 for \*OUT, \*ERR;
if() {
print "[ERROR] : $_" while();
die;
}
print "[OUTPUT]: $_" while ();
}
Команда sort
теперь выполняется нормально, но я не могу понять, почему.
[Обновление]Прочитав ответ @tchrist, я прочитал IO::Select
и, погуглив еще, нашел эту версию execute
:
sub good_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $pid = open3(my $in, my $out, my $err = gensym(), $cmd);
print "[PID]: $pid\n";
my $sel = new IO::Select;
$sel->add($out, $err);
while(my @fhs = $sel->can_read) {
foreach my $fh (@fhs) {
my $line = <$fh>;
unless(defined $line) {
$sel->remove($fh);
next;
}
if($fh == $out) {
print "[OUTPUT]: $line";
}elsif($fh == $err) {
print "[ERROR] : $line";
}else{
die "[ERROR]: This should never execute!";
}
}
}
waitpid($pid, 0);
}
Это работает нормально, и теперь некоторые вещи стали яснее. Но общая картина все еще немного туманна.
Итак, мои вопросы:
hung_execute
?good_execute
работает из-за >&
в вызове open3. Но почему и как?good_execute
не работал, когда я использовал лексические переменные ( my $out
вместо OUT
) для файловых дескрипторов. Это дало эту ошибку: open3: open(GLOB(0x610920), >&main::OUT) failed: Invalid arguments
. Почему так?