Python 子脚本消耗所有标准输入

标签 python linux bash stdin

在 bash 脚本中运行 python 脚本时,我发现 raw_input/readline 有一些奇怪的行为。

简而言之,当一次将所有标准输入(每个条目由一个新行分隔)传递给父脚本时,bash 子脚本将采用它们需要的标准输入,而 python子脚本将消耗 所有 标准输入,不为下一个子脚本留下任何东西。我想出了一个简单的例子来证明我的意思:

父脚本(parent.sh)

#!/bin/bash

./child.sh
./child.sh
./child.py
./child.py

Bash 子脚本 (child.sh)

#!/bin/bash

read -a INPUT
echo "sh: got input: ${INPUT}"

Python子脚本(child.py)

#!/usr/bin/python -B

import sys

INPUT = raw_input()
print "py: got input: {}".format(INPUT)

预期结果

./parent.sh <<< $'aa\nbb\ncc\ndd'
>> sh: got input: aa
>> sh: got input: bb
>> py: got input: cc
>> py: got input: dd

实际结果

./parent.sh <<< $'aa\nbb\ncc\ndd\n'
>> sh: got input: aa
>> sh: got input: bb
>> py: got input: cc
>> Traceback (most recent call last):
>>   File "./child.py", line 5, in <module>
>>     INPUT = raw_input()
>> EOFError: EOF when reading a line

raw_input 似乎清除了标准输入中的所有剩余行。使用 sys.stdin.readline 而不是 raw_input 不会引发 EOFError,但是收到的输入是一个空字符串,而不是预期的“dd”。

这里发生了什么?我怎样才能避免这种行为,以便最后一个子脚本接收到预期的输入?

编辑:为了确定,我在标准输入中添加了几行,结果是一样的:

./parent.sh <<< $'aa\nbb\ncc\ndd\nff\nee\n'
>> sh: got input: aa
>> sh: got input: bb
>> py: got input: cc
>> Traceback (most recent call last):
>>   File "./child.py", line 5, in <module>
>>     INPUT = raw_input()
>> EOFError: EOF when reading a line

最佳答案

下面是演示相同问题的更简单方法:

printf "%s\n" foo bar | {
    head -n 1
    head -n 1
}

从各方面来看,这看起来应该打印两行,但是 bar 神秘地不见了。

发生这种情况是因为阅读台词是一个谎言。 UNIX 编程模型不支持它。

相反,基本上所有工具所做的都是消耗整个缓冲区,分割出第一行,并将缓冲区的其余部分留给下一次调用。这适用于 head、Python raw_input()、C fgets()、Java BufferedReader.readLine()以及几乎所有其他内容。

由于 UNIX 将整个缓冲区计为已消耗,无论程序实际最终使用了多少,程序退出时缓冲区的其余部分将被丢弃。

然而,

bash 解决了这个问题:它逐字节读取直到到达换行符。这是非常低效的,但它允许 read 仅使用流中的一行,将其余部分留给下一个过程。

您可以通过打开一个原始的、无缓冲的阅读器在 Python 中做同样的事情:

import sys
import os
f = os.fdopen(sys.stdin.fileno(), 'rb', 0)
line=f.readline()[:-1]
print "Python read: ", line

我们可以用同样的方式测试:

printf "%s\n" foo bar | {
    python myscript
    python myscript
}

打印

Python read: foo
Python read: bar

关于Python 子脚本消耗所有标准输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31151726/

相关文章:

python - 如何将 0/1 转换为高效签名?

Python 内置函数 "compile"。它是干什么用的?

python - 在 Python 中打印格式化字符串的问题

c - 超时功能

java - Appium Doctor - 无法在 MacOS 10.12 上将 $JAVA_HOME/bin 设置为 PATH 变量

python - 多线程性能开销

linux - 与 gfortran 链接 : _edata: invalid version 21 (max 4)

linux - 带 cron 的 Gsettings

linux - 从某一行开始在文件中搜索字符串 否

node.js - bash,nohup sh 文件启动 Node , Node 在注销时挂起?