【问题标题】:Why do I have to type ctrl-d twice? [duplicate]为什么我必须输入 ctrl-d 两次? [复制]
【发布时间】:2011-11-14 05:06:15
【问题描述】:

为了我自己的乐趣,我编写了一个 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输入输入 baz输入 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)' fooCtrl DCtrl DbarEnter Ctrl DCtrl D'foobar\n' $ python pype.py '("%r\n" % (l,) for l in sys.stdin)' fooCtrl VCtrl D^DbarEnter Ctrl DCtrl D'foo\x04bar\n' $

【问题讨论】:

  • 为我工作(OS X 10.5、Python 2.5 和 3.2)。
  • 根据我的经验,您所描述的行为正是我所期望的——违反预期的行为正是您所期望的。所描述的行为是我在 Linux 和 Mac OS 机器中一直得到的。 (除了双 Ctrl D - 我只需要输入一次(如果在新的空行上)或两次(如果当前行有一些内容)。
  • @brandizzi:括号中的那位正是我所说的。我还对在处理任何输入之前输入多行这一事实感到困惑,但这是次要的问题。
  • @TokenMacGuy 我想知道你为什么也得到它,特别是因为我无法重现它。你的操作系统是什么?另外,你用的是什么外壳?
  • 看起来这是某些 python 版本的已知问题;我将不得不解决它。

标签: python eof tty


【解决方案1】:

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

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

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

因此,如果您有输入数据,则在非空行中按两次 Ctrl-D 或在空行上按一次,即之前按 Enter。 p>

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

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

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

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

【讨论】:

  • 感谢您使用 iter(...) 的建议。这正是我获得我想要的单 Ctrl-D 行为所需要的。
【解决方案2】:

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

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

【讨论】:

  • Control-V Control-D 确实 给我一个\x04,但 Control-D Control-D 没有。额外的控制权在哪里?
【解决方案3】:

我不能确切地说为什么额外的 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 正在生成的生成器。

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

Generator Class reference (section 9.10)

【讨论】:

  • Generator - 返回迭代器的函数。它看起来像一个普通函数,只是它包含用于生成一系列可在 for 循环中使用的值的 yield 语句,或者可以使用 next() 函数一次检索一个值。每个 yield 会暂时暂停处理,记住位置执行状态(包括局部变量和挂起的 try 语句)。当生成器恢复时,它会从中断处继续(与每次调用都重新开始的函数相反)。
  • 我知道发电机是什么。 eval 不是一个。
  • 对不起,我的意思是 eval(sys.argv[1], _g) 返回的东西是一个生成器。尝试:sys.stdout.write(eval(sys.argv[1], _g)),写调用引发:TypeError: argument 1 must be string or read-only character buffer, not generator
猜你喜欢
  • 1970-01-01
  • 2023-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多