从命名管道获取 stdin

从命名管道获取 stdin

我想做的是在终端窗口中运行 python 并从命名管道重定向它的标准输入。然后我写入另一个终端中的命名管道,并在 python 上执行该命令。

1号航站楼:

mkfifo p1
python < p1

2 号航站楼:

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

发生的情况是 - python 打印Hello World并退出。我想做的是保持 python 运行以接受下一个命令。我如何在 shell 中执行此操作?

答案1

你需要

  • 即使它的标准输入不是终端,也以交互方式运行 python:使用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³ 手动打开和关闭 fd。

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

¹ fd 3 将由子进程继承,并且(除了在 ksh93 中设置 close-on-exec 标志)在这些进程中执行的其他命令(如果有)。

² 当然,这也适用于非内置命令。对于非内置程序,shell 不需要保存和恢复 fd 1,因为重定向仅在分叉来执行命令的子进程中执行。对于外部命令,除了自动执行此操作的 ksh93 之外,您实际上可能希望关闭 fd 3,这样它就不会泄漏给它们,并且它们最终可能会生成后台进程 ( cmd >&3 3>&-)。

³ 请注意,在这种情况下,ksh93 不会在该 fd 3 上设置 close-on-exec 标志。

答案2

您可以使用它在写入tail -f后保持 fifo 打开。echo

tail -n1 -f p1 | python

为什么这有效

python正在阅读来自p1.当到达文件末尾时,它将停止读取。这是文件读取的正常行为,即使该文件是命名管道。tail带有-f(follow) 标志的文件将在到达文件末尾后继续读取。

答案3

您需要一次发送整个程序。

当您调用 run 时,python < p1shell 会在调用 python 之前等待输入。也就是说,python 甚至没有开始执行根本不直到整个数据流被 shell 读取,然后将其全部传递给python.

即使改为运行python -u p1(即,无缓冲并从 file 读取p1),python也会在执行任何文件之前尝试读取整个文件。

试试这个实验。

1号航站楼:

mkfifo p1
python < p1

2 号航站楼:

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

您将看到您可以发送多行,但第 1 学期中的 python 不执行任何操作。现在按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使用 Ctrl-D 关闭终端 2。这将关闭my.fifo并且cat终端 1 中的命令将退出。

相关内容