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

#perl #winapi

#perl #winapi

Вопрос:

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

Например, этот скрипт на Perl работает, и я использовал его в течение многих лет во многих различных конфигурациях:

 use strict;
my @out;
@out = `psexec \\192.168.1.105 -u admin -p pass netstat -a`;

print @out;
  

Этот сценарий Perl завершается с ошибкой и, похоже, надежно приводит к зависанию psexesvc в удаленной системе:

 use IPC::Open2;

my($chld_out, $chld_in, $pid);
$pid = open2($chld_out, $chld_in, 'psexec \\192.168.1.105 -u admin -p pass netstat -a');

waitpid( $pid, 0 );
my $child_exit_status = $? >> 8;
my $answer = <$chld_out>;

print "nn answer: $answer";
  

Что для меня так странно, так это то, что у обратных ссылок, похоже, никогда не возникает никаких проблем. Все остальное делает, включая примеры на C из MSDN.

Я подозреваю, что проблема с IPC:: Open2 и примером на C (ссылка выше) связана с тем фактом, что я перенаправляю STDIN и STDOUT из командной оболочки (cmd.exe ), и дочерний процесс (psexec) делает то же самое при обмене данными с моей удаленной системой.

Кроме того, где в perldoc я могу найти подробную информацию о том, как работают обратные ссылки? Меня больше всего интересуют их «внутренности» в Windows.

Или, где в исходном коде Perl я могу просмотреть внутреннюю работу backticks (возможно, это отнимает больше времени, чем я могу прожевать, но на данный момент стоит попробовать).

ОБНОВЛЕНИЕ: следуя предложению Энди, я обнаружил, что это работает:

 use IPC::Open2;

my($chld_out, $chld_in, $pid);
$pid = open2($chld_out, $chld_in, 'psexec \\192.168.1.105 -u admin -p pass netstat -a');

my @answer = <$chld_out>;
print "nn answer: @answer";

waitpid( $pid, 0 );
my $child_exit_status = $? >> 8;
  

Комментарии:

1. Вы пробовали читать выходные данные ( my $answer = <$chld_out> ) перед вызовом waitpid ?

2. Вау, это заставляет его работать. Хотя я понятия не имею, почему!

Ответ №1:

Я очень мало знаю о том, как это работает в Windows, поэтому, возможно, кто-нибудь сможет дать более конкретный ответ, но при передаче между процессами в perl вам нужно быть осторожным, чтобы избежать нежелательных блокировок и взаимоблокировок. В perlipc обсуждаются различные проблемные сценарии.

В вашем примере немедленный вызов waitpid вызывает проблемы. Одна из возможностей заключается в том, что дочернее приложение не может завершить работу, пока что-то не прочитает выходные данные, поэтому все зависает, поскольку родительское приложение не собирается считывать выходные данные. Другая возможность заключается в том, что часть потока данных отключается как часть waitpid вызова, и это вызывает проблему с удаленным процессом.

В любом случае, было бы лучше прочитать выходные данные дочернего процесса перед вызовом waitpid .