Python子脚本消耗了所有的stdin

在bash脚本中运行python脚本时,我发现了raw_input / readline的某些奇怪行为.

简而言之,当一次将所有stdin(每个条目用换行分隔)传递给父脚本时,bash子脚本将仅采用其所需的stdin,而python子脚本将消耗所有stdin,不留任何内容为下一个孩子.我想出一个简单的例子来说明我的意思:

父脚本(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似乎清除了stdin中的所有其余行.使用sys.stdin.readline代替raw_input不会引发EOFError,但是接收到的输入是一个空字符串,而不是预期的’dd’.

这是怎么回事如何避免这种行为,以便最后一个子脚本收到预期的输入?

编辑:可以肯定的是,我向stdin添加了几行,结果是相同的:

./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
}

从所有人的角度来看,这看起来应该打印两行,但该栏却神秘地丢失了.

这是因为阅读线是一个谎言. 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 3:如何指定stdin编码


下一篇:使用Java扫描程序按顺序从stdin读取时引发异常