Как восстановить STDIN из сценария perl, который был вызван из сценария csh? - PullRequest
1 голос
/ 28 сентября 2010

У меня ужасная структура вложенных сценариев, которую я поддерживаю, которая периодически принимает пользовательский ввод с клавиатуры, и я пытаюсь написать сценарий, который автоматизирует этот ввод. По сути, структура скрипта выглядит следующим образом:

  • cmd1.csh занимает 3 строки ввода и затем вызывает cmd2.csh, затем выходит обычно
  • cmd2.csh дважды вызывает cmd3.pl а затем занимает 1 строку ввода, затем выходит нормально
  • cmd3.pl занимает 1 строку вход и выход нормально.

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

Я пробовал несколько вариантов следующего сценария Perl, но, возможно, я просто не использую волшебную комбинацию кавычек и экранированных символов:

$cmd = "echo 'a\\
b\\
c\\
d\\
e\\
f' | ~/pltest/cmd1.csh";

system_tcsh

sub system_tcsh
{
   @args = ("tcsh", "-c", shift);
   return system(@args);
}

Кажется, проблема в том, что как только cmd3.pl вызывается один раз и читается d, входные данные не читаются ни вторым вызовом cmd3.pl, ни cmd2.csh. Помещение множественного чтения в cmd3.pl работает нормально, и больше элементов считывается из STDIN, но после того, как первый вызов cmd3.pl завершается, STDIN становится пустым (или в каком-то неиспользуемом состоянии).

Что происходит с STDIN, когда скрипт perl возвращается и есть ли способ имитировать этот тип ввода, используя perl или что-то еще?

Заранее спасибо.

Ответы [ 5 ]

3 голосов
/ 30 сентября 2010

Я создал условия для воспроизведения проблемы, но все прошло хорошо:

caller.pl:

#! /usr/bin/perl

$cmd = "echo 'a\\
b\\
c\\
d\\
e\\
f' | ~/doc/Answers/src/pltest/cmd1.csh";

sub system_tcsh
{
   @args = ("tcsh", "-c", $cmd);
   return system(@args);
}

system_tcsh

cmd1.csh:

#! /bin/csh
echo "${0}(cmd1) $argv[*]"
set line1 = $<
set line2 = $<
set line3 = $<
setenv lineno  3
echo "cmd1: read: $line1 $line2 $line3"
./cmd2.csh

cmd2.csh:

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
./cmd3.csh
./cmd3.csh

set line6 = $<

echo "    ${0}: Read: $line6"

cmd3.csh:

#! /bin/csh
# cmd3
set line = $<
echo "        ${0}: Read: '$line'"

Пробный запуск:

frayser@gentoo ~/doc/Answers/src/pltest $ ./caller.pl 
/export/home/frayser/doc/Answers/src/pltest/cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.csh: Read: 'd'
        ./cmd3.csh: Read: 'e'
    ./cmd2.csh: Read: f

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

-

UPDATE
Это обновление, которое воспроизводит проблему Perl STDIN и предлагает обходной путь.

Замена cmd3 на версию Perl дает сообщаемый проблемный результат:

cmd3.pl, который заменяет cmd3.csh:

#! /usr/bin/perl
# cmd3

$_=<STDIN>;
chomp();
print "        $0: Read: '$_'\n";

Результат: использование cmd3.pl . После прочтения «d» ввод больше недоступен.

./caller.pl    
./cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.pl: Read: 'd'
        ./cmd3.pl: Read: ''
    ./cmd2.csh: Read: 

Чтобы исправить эту ситуацию, cmd2 заменен на отправку только 1 строки на cmd3 . Команда line (1) легко делает это:

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
line | ./cmd3.pl
line | ./cmd3.pl

set line6 = $<

echo "    ${0}: Read: $line6"

Это cmd2.csh оптимизировано, чтобы избежать накладных расходов при выполнении команды line .

#! /bin/csh
echo "    ${0}(cmd2) $argv[*] now running..."
set x = $<
echo $x | ./cmd3.pl

set y = $<
echo $y | ./cmd3.pl

set line6 = $<

echo "    ${0}: Read: $line6"

Вот вывод с обновленным cmd2.csh . Функциональность теперь та же, что и при использовании Perl , как и при использовании csh в качестве окончательного сценария: без потерь в stdin.

./cmd1.csh(cmd1) 
cmd1: read: a b c
    ./cmd2.csh(cmd2)  now running...
        ./cmd3.pl: Read: 'd'
        ./cmd3.pl: Read: 'e'
    ./cmd2.csh: Read: f
1 голос
/ 28 сентября 2010

Для начала я бы использовал Ожидайте для ввода сценария, возможно, он более надежен и доступен в большинстве систем Unix.

За исключением того, что в зависимости от вашей версии echo многиеразрешить использование "\ n" в строках в кавычках, хотя это должно быть функционально эквивалентно.Что-то вроде:

$cmd = '/bin/echo -e "a\nb\nc\nd\ne\nf" | ~/pltest/cmd1.csh'

(Это работает на моей Linux-системе .. man echo, чтобы увидеть, на что способна ваша локальная версия.)

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

0 голосов
/ 05 октября 2010

Этот ответ о жадном вводе perl от Грега Бэкона , кажется, является способом решения этой проблемы. Конечно, это означает, что мне нужно изменить самый внутренний Perl-скрипт, но пока это лучшее решение, если я могу получить разрешение на его изменение. Это не самое надежное решение - вероятно, лучше использовать Expect pm, но, за исключением этого, это должно сработать.

0 голосов
/ 04 октября 2010

@ just jon: К сожалению, функция wait недоступна для меня, и я пытался найти способ с помощью имеющихся у меня инструментов (поскольку наш процесс запроса не совсем целесообразен).Спасибо за ответ, хотя!Скрипт perl просто выполняет, или <>, ни один из которых, кажется, не работает должным образом.

@ user268396: Кажется, это тоже не работает, и я думаю, это функциональный эквивалент, так как каналпринудительно вводит данные в STDIN независимо от того, откуда поступают данные.

@ Frayser: Вы пробовали третий скрипт (cmd3.csh) как скрипт perl?Вот в чем заключается моя проблема.

0 голосов
/ 28 сентября 2010

И что произойдет, если вы сделаете:

echo 'a\
b\
c\
d\
e\
f' > tmpfile
cat tmpfile | ~/pltest/cmd1.csh

Если это сработает, вы можете организовать свой Perl-скрипт вокруг предварительной выборки ввода, сохранить его во временном файле и затем выполнить вышеописанный трюк cat /path/to/tempfile | /path/to/cmd1.csh.

...