
Я пытаюсь запустить 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 выйдет.