python:输入来自管道时的混乱

python: confusion when input is coming from a pipe

我的 python 脚本可以在两种模式下使用:

./foo arg file

cat file | foo arg

这就是我判断 stdin 是否为管道的方式:

if sys.stdin.isatty():
    print('not pipe\n')
else:
    print('is pipe')

它的行为符合预期:

./foo arg file
not pipe

cat file | ./foo arg
is pipe

但是,在下面的用法中它认为输入来自管道,即使管道只是 while 循环的一部分:

while read F ; do foo arg $F ; done < /tmp/zz
is pipe

我将我的脚本称为 ./foo arg file。为什么它认为输入是管道?

while read F ; do foo arg $F ; done < /tmp/zz

您正在从 /tmp/zz 重定向 while 循环的标准输入(我假设这是一个文件)。这不仅包括条件测试中的 read,它还包括循环主体中的所有语句(当然,除非它们被重定向)。

您的 python 代码仅测试标准输入是否为 tty/terminal,否则假设它是管道。但是标准输入可以来自 any 类型的可以读取的打开文件描述符; tty、管道、常规文件、套接字等

接受文件名和标准输入作为程序数据的典型方法是查看命令行参数的数量;如果它需要文件名但没有足够的参数来拥有任何参数,则改为从标准输入读取。


如果使用 bashzsh(可能还有其他扩展基本 POSIX sh 的 shell),如果您希望在这样的循环中有一个程序从脚本的原始标准输入读取,一种技术是重定向到 与 0(标准输入)不同的 描述符,并告诉 read 从中读取:

while read -r -u 3 f; do foo arg "$f" ; done 3< /tmp/zz

打开文件 /tmp/zz 作为描述符编号 3。

您的想法在标准输入未绑定到 TTY 的上下文中行不通,但您也不在管道中。

在你的具体情况下,我会简单地从 file 参数的存在与否中得出,是否从标准输入读取。