17

为了我自己的乐趣,我编写了一个 python 脚本,它允许我将 python 用于 bash 单行;提供一个 python 生成器表达式;并且脚本对其进行迭代。这是脚本:

DEFAULT_MODULES = ['os', 're', 'sys']

_g = {}
for m in DEFAULT_MODULES:
    _g[m] = __import__(m)

import sys
sys.stdout.writelines(eval(sys.argv[1], _g))

以下是您可以如何使用它的方法。

$ groups | python pype.py '(l.upper() for l in sys.stdin)'
DBORNSIDE
$ 

对于预期用途,它可以完美运行!

但是当我不使用管道输入它并直接调用它时,例如:[强调显示我输入的内容]

$ python pype.py '("%r\n" % (l,) for l in sys.stdin)'
 foo Enter
bar Enter
bazEnter
Ctrl DCtrl D 'foo\n'
'谷仓'
'巴兹\n'
$

为了停止接受输入并产生任何输出,我必须输入Enter- Ctrl D-Ctrl DCtrl D- Ctrl D- Ctrl D。这违反了我的期望,即每行都应按输入进行处理,并且Ctrl D随时键入都会结束脚本。我的理解差距在哪里?

编辑:我已经更新了交互式示例,以表明我没有看到 wim 在他的回答中描述的引用,以及更多示例。

$ python pype.py '("%r\n" % (l,) for l in sys.stdin)'
 foo Ctrl DCtrl DbarEnter
Ctrl DCtrl D 'foobar\n'
$ python pype.py '("%r\n" % (l,) for l in sys.stdin)'
fooCtrl VCtrl D ^D barEnter
Ctrl DCtrl D 'foo\x04bar\n'
$
4

3 回答 3

12

Ctrl-D不一定被识别为EOF,而是“终止当前read()呼叫”。

如果您有一个空行(或只是按Ctrl-D)并按Ctrl-D,您read()将立即终止并返回 0 个读取字节。这是EOF的标志。

如果您在一行中有数据并按Ctrl-D,您将read()终止输入的任何内容,当然没有终止换行符 ( '\n')。

因此,如果您有输入数据,您可以Ctrl-D在非空行中按两次或在空行上按一次,即使用Enterbefore。

这一切都适用于正常的操作系统界面,可以通过 Python 访问os.read()

Python 文件对象以及文件迭代器将第一个识别为当前调用的 EOF 视为终止read(),因为它们认为不再存在任何内容。下一个read()调用再次尝试并且需要另一个调用Ctrl-D才能真正返回 0 字节。原因是文件对象总是尝试返回请求的字节数,并且如果操作系统返回的字节数少于请求的字节数read(),则会尝试填充。read()

与此相反file.readline()iter(file)使用内部read()函数读取因而总是有额外的这个特殊要求Ctrl-D

我总是iter(file.readline, '')习惯从文件中逐行读取。

于 2011-09-10T07:45:07.177 回答
3

Ctrl+D被终端设备识别,终端通过生成文件结尾来响应它。也许这会有所帮助,来自维基百科(强调我的):

在 UNIX 和 AmigaDOS 中,击键到 EOF 的转换是由终端驱动程序执行的,因此程序不需要将终端与其他输入文件区分开来。默认情况下,驱动程序将行首的 Control-D 字符转换为文件结束指示符。要将实际的 Control-D (ASCII 04) 字符插入输入流,用户在其前面加上一个“引号”命令字符(通常是 Control-V,尽管在某些系统上您可以通过键入 Control-D 两次来实现此效果)。

于 2011-09-10T02:32:01.603 回答
0

我不能确切地说为什么额外的 CTRL+D (尽管其他答案做得很好),但这将使输入仅在单个 之后打印CTRL+D,但您仍然需要CTRL+D第二次退出脚本

#!/usr/bin/python
DEFAULT_MODULES = ['os', 're', 'sys']

_g = {}
for m in DEFAULT_MODULES:
    _g[m] = __import__(m)

import sys
for x in eval(sys.argv[1], _g):
    print x,

输出:

[ root@host ~ ]$ ./test.py '(l.upper() for l in sys.stdin)'
abc
def(ENTER, CTRL+D)
ABC
DEF
qwerty(ENTER, CTRL+D)
QWERTY
[ root@host ~ ]$

编辑:

eval在这种情况下正在返回一个生成器,因此第一个 EOF (CTRL+D) 可能会结束对 sys.stdin 的读取,而第二个 EOF 可能会停止正在生成的生成器eval

生成器- 返回迭代器的函数。它看起来像一个普通函数,只是它包含用于生成一系列可在 for 循环中使用的值的 yield 语句,或者可以使用 next() 函数一次检索一个值。每个 yield 会暂时暂停处理,记住位置执行状态(包括局部变量和挂起的 try 语句)。当生成器恢复时,它会从它停止的地方开始(与每次调用都重新开始的函数相反)。

生成器类参考(第 9.10 节)

于 2011-09-10T02:46:36.437 回答