Получение stdin из именованного канала

Получение stdin из именованного канала

Я пытаюсь запустить python в окне терминала и перенаправить его stdin из именованного канала. Затем я пишу в именованный канал в другом терминале и эта команда выполняется на python.

Терминал 1:

mkfifo p1
python < p1

Терминал 2:

echo -n "print \"Hello World\"" > p1

Что происходит - python печатает Hello Worldи выходит. Я хочу, чтобы python продолжал работать, чтобы принять следующую команду. Как это сделать в оболочке?

решение1

Вам нужно

  • Запустите Python в интерактивном режиме, даже если его stdin не является терминалом: используйтеpython -i
  • оставьте пишущий конец канала открытым, в противном случае python обнаружит EOF и завершит работу.

Так:

python -i < p1

И в другом месте:

# open p1 on fd 3 of the shell process¹ for instance so stdout is
# left untouched in case you still want to write messages there.
exec 3> p1

# write something on fd 3. For instance with ksh/zsh's print
# builtin:
print -u3 '1j*1j'

# or for commands such as echo that can only print things
# on stdout (fd 1), duplicate that fd 3 to their fd 1:
echo '1j*1j' >&3 # short for 1>&3

# after echo (a builtin² in virtually all shells) returns, fd 1
# is restored to what it was before, but fd 3 remains open on
# the writing end of the pipe, so python still doesn't see EOF
# there.

echo normal message on stdout
echo 1+1 >&3 # more instructions for python
...
# and when done, close that file descriptor so python sees the EOF
# (assuming that fd 3 was the only file descriptor left open on
# the writing end of that pipe):
exec 3>&-

В качестве альтернативы, особенно в скриптах, можно перенаправить целую группу команд вместо того, чтобы вручную открывать и закрывать файловый дескриптор с помощью exec³.

{
  echo ... >&3
  ...
  ...
} 3> p1

¹ что fd 3 будет унаследован дочерними процессами и (за исключением ksh93, который устанавливает для него флаг close-on-exec) другими командами, выполняемыми в этих процессах, если таковые имеются.

² это, конечно, работает и для невстроенных команд. Для невстроенных команд оболочке не нужно сохранять и восстанавливать fd 1, так как перенаправление выполняется только в дочернем процессе, который разветвляется для выполнения команды. Для внешних команд, за исключением ksh93, который делает это автоматически, вы можете фактически захотеть закрыть fd 3, чтобы он не просочился к ним и фоновым процессам, которые они могут в конечном итоге породить ( cmd >&3 3>&-).

³ обратите внимание, что в этом случае ksh93 не устанавливает флаг закрытия при выполнении для этого файлового дескриптора 3.

решение2

Вы можете использовать его tail -f, чтобы держать fifo открытым после echoзаписи в него.

tail -n1 -f p1 | python

Почему это работает

pythonчитает из p1. Когда он достигает конца файла, он останавливает чтение. Это нормальное поведение для чтения файлов, даже если файл является именованным каналом. tailс -fфлагом (follow) продолжит чтение из файла после достижения его конца.

решение3

Вам необходимо отправить всю программу сразу.

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

Даже если запустить его python -u p1вместо этого (то есть без буферизации и чтения из файла p1), pythonбудет предпринята попытка прочитать весь файл, прежде чем выполнить какую-либо его часть.

Попробуйте провести этот эксперимент.

Терминал 1:

mkfifo p1
python < p1

Терминал 2:

cat > p1
print "Hello World"
print "Hello World"

Вы увидите, что можете отправить несколько строк, но python в Term 1 ничего не делает. Теперь нажмите ctrl+ D. Вся программа выполняется сразу.

Итак, подведем итог: если вы хотите, чтобы python читал из конвейера, вам нужно отправить всю программу. Вы не можете использовать python интерактивно таким образом.

решение4

Вот еще одно решение. Терминал 1:

alec@MacBook-Air ~/hello $ rm -f my.fifo; mkfifo my.fifo 
alec@MacBook-Air ~/hello $ cat my.fifo 
command1
command2
...
alec@MacBook-Air ~/hello $ 

Терминал 2:

alec@MacBook-Air ~/hello $ cat > my.fifo 
command1
command2
...
alec@MacBook-Air ~/hello $ 

Вы вводите команды в Терминале 2 и смотрите, как они отображаются в Терминале 1. Вы можете перенаправить вывод Терминала 1 в другой процесс, например cat my.fifo | node .... Когда закончите, закройте stdinТерминал 2 с помощью Ctrl-D. Это закроет my.fifoи catкоманда в Терминале 1 выйдет.

Связанный контент