Как я могу проверить состояние первой программы в конвейере в системе Perl ()? - PullRequest
2 голосов
/ 26 августа 2009
perl -e 'system ("crontab1 -l");print $?'

возвращает -1, как и ожидалось (программа crontab1 не существует)

perl -e 'system ("crontab1 -l|grep blah");print $?'

возвращает 256.

Как проверить состояние первой (или обеих) программ?

Ответы [ 7 ]

4 голосов
/ 26 августа 2009

Если вы допускаете использование чего-то другого, кроме system, есть более простые решения. Например, метод results в IPC :: Run возвращает все коды выхода цепочки.

4 голосов
/ 26 августа 2009

Вы получаете статус завершения всей команды, как и вы должны. Если вы хотите получить статус выхода отдельно, вам придется запускать команды отдельно.

#!/usr/bin/perl -e
system("crontab1 -l > /tmp/junk.txt"); print $?;
system("grep blah /tmp/junk.txt"); print $?;

в качестве примера.

3 голосов
/ 26 августа 2009

Помните, вы должны использовать $? >> 8 для получения кода выхода, а не $?

perl -e 'system("set -o pipefail;false | true");print $?>>8,"\n"'

1

Это ("pipefail") работает, только если ваша оболочка bash 3. Cygwin и linux поставляются вместе с ней; не уверен насчет mac.

Вы должны знать, что 256 - это возврат ошибки. 0 - это то, что вы получите в случае успеха:

perl -e 'system("true");print $?>>8,"\n"'

0

Я не знал, что система вернула -1 для одной команды, которая не найдена, но $? >> 8 в этом случае все равно должно быть ненулевым.

2 голосов
/ 09 февраля 2015

[ Это составлено как ответ на другой вопрос , который был закрыт как дубликат этого. ]

Выполнение команды оболочки требует выполнения оболочки. С этой целью

system($shell_command)

эквивалентно

system('/bin/sh', '-c', $shell_command)

Таким образом, все ваши примеры запускают одну программу (/bin/sh). Если вы хотите получить статус выхода нескольких детей, вам нужно иметь несколько детей!

use IPC::Open3 qw( open3 );

open(local *CHILD1_STDIN, '<', '/dev/null')
   or die $!;

pipe(local *CHILD2_STDIN, local *CHILD1_STDOUT)
   or die $!;

my $child1_pid = open3(
   '<&CHILD1_STDIN',
   '>&CHILD1_STDOUT',
   '>&STDERR',
   'prog1', 'arg1', 'arg2',
);

my $child2_pid = open3(
   '<&CHILD2_STDIN',
   '>&STDOUT',
   '>&STDERR',
   'prog2', 'arg1', 'arg2',
);

my @pipe_status = map { waitpid($_, 0) } $child1_pid, $child2_pid;

IPC :: Open3 - довольно низкий уровень. IPC :: Run3 и / или IPC :: Run могут сделать это проще. [Обновление: действительно, IPC :: Run делает ].

1 голос
/ 26 августа 2009

Как проверить состояние первой (или обеих) программ?

Нет, по крайней мере, такого способа, как вы построили вещи. Возможно, вам придется самостоятельно управлять подпроцессами с помощью fork (), exec () и waitpid (), если вы должны знать эти вещи.

Вот примерно то, что происходит в вашем фрагменте кода.

  1. perl s system () порождает оболочку и perl * wait () * s для завершения этого подпроцесса.

  2. Оболочка устанавливает конвейер:

    1. subhell * exec () * s grep на конце чтения канала
    2. подоболочка не может найти crontab1 где-либо в $PATH и * exit () * s 127 (в моей системе, где 127 - оболочка, указывающая на невозможность найти программу для запуска).
  3. grep определяет конец файла на его входе и, ничего не сопоставив, * exit () * s 1.

  4. Оболочка * exit () * s с кодом выхода последнего процесса в конвейере, который снова равен 1.

  5. perl определяет код выхода оболочки, равный 1, который кодируется в $? как 256.
    (256 >> 8 == 1)

1 голос
/ 26 августа 2009

Операционная система возвращает состояние завершения только для последней выполненной программы, и если ОС не возвращает ее, Perl не может сообщить об этом.

Я не знаю никакого способа получить код завершения, возвращенный более ранними программами в конвейере, кроме как запускать каждую по отдельности и использовать временные файлы вместо каналов.

1 голос
/ 26 августа 2009

Если вы хотите проверить статус, не помещайте их все в одну систему. Откройте канал чтения для первой программы, чтобы получить ее вывод, затем откройте другой канал для другой программы.

...