Проблема с файловым дескриптором в perl - PullRequest
2 голосов
/ 13 августа 2010

Я пытаюсь запустить bp_genbank2gff3.pl (пакет bioperl) из другого сценария perl, в качестве аргумента которого используется genbank.

Это не работает (выходные файлы не создаются):

   my $command = "bp_genbank2gff3.pl -y -o /tmp $ARGV[0]";

   open( my $command_out, "-|", $command );
   close $command_out;

но это делает

   open( my $command_out, "-|", $command );
   sleep 3; # why do I need to sleep?
   close $command_out;

Почему?

Я думал, что close должен блокироваться до выполнения команды:

Закрытиелюбой переданный файловый дескриптор заставляет родительский процесс ожидать завершения дочернего процесса ... (см. http://perldoc.perl.org/functions/open.html).

Редактировать

Я добавил это как последнюю строку:

say "ret=$ret, \$?=$?, \$!=$!";

и в обоих случаях распечатка:

ret=, $?=13, $!=

(что означает, что close не удалось в обоих случаях, верно?)

Ответы [ 2 ]

5 голосов
/ 13 августа 2010

$? = 13 означает, что ваш дочерний процесс был прерван сигналом SIGPIPE.Ваша внешняя программа (bp_genbank2gff3.pl) попыталась записать какой-либо вывод в канал для вашей perl программы.Но программа perl закрыла свой конец канала, поэтому ваша ОС отправила SIGPIPE во внешнюю программу.

Используя sleep в течение 3 секунд, вы позволяете своей программе работать в течение 3 секунд, прежде чемОС убивает его, так что это позволит вашей программе что-то сделать.Обратите внимание, что каналы имеют ограниченную емкость, поэтому, если ваш родительский сценарий perl не читает из канала и если внешняя программа много пишет в стандартный вывод, операции записи внешней программы в конечном итоге блокируются, и вы, возможно, на самом деле неполучите 3 секунды усилий от вашей внешней программы.

Обходной путь - прочитать выходные данные внешней программы, даже если вы просто собираетесь их выбросить.

open( my $command_out, "-|", $command );
my @ignore_me = <$command_out>;
close $command_out;


Обновление: Если вы действительно не заботитесь о выводе команды, вы можете избежать проблем SIGPIPE, перенаправив вывод в /dev/null:

open my $command_out, "-|", "$command > /dev/null";
close $command_out;     # succeeds, no SIGPIPE

Конечно, если вы собираетесь пойти на такие неприятности, чтобы проигнорировать вывод, вы также можете просто использовать system.


Дополнительная информация : в качестве OPговорит, что закрытие переданного по конвейеру файлового дескриптора заставляет родителя ждать, пока ребенок завершит работу (используя waitpid или что-то подобное).Но до начинает ждать, закрывает конец трубы.В этом случае этот конец является концом чтения канала, в который дочерний процесс записывает свой стандартный вывод.В следующий раз, когда дочерний объект пытается что-то записать в стандартный вывод, ОС обнаруживает, что конец чтения этого канала закрыт, и отправляет SIGPIPE дочернему процессу, убивая его и быстро допуская оператор close в родительском процессе..

0 голосов
/ 13 августа 2010

Я не уверен, что вы пытаетесь сделать, но system , вероятно, лучше в этом случае ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...