为什么我必须按 ctrl-d 两次?
为了我自己的娱乐,我编写了一个 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)' fooEnter barEnter bazEnter Ctrl DCtrl D'foo\n' 'bar\n' 'baz\n' $
为了停止接受输入并产生任何输出,我必须键入Enter - Ctrl D - Ctrl D 或 Ctrl 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' $
For my own amusement, I've cooked up a python script that allows me to use python for bash one-liners; Supply a python generator expression; and the script iterates over it. Here's the script:
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))
And here's how you might use it.
$ groups | python pype.py '(l.upper() for l in sys.stdin)'
DBORNSIDE
$
For the intended use, it works perfectly!
But when I don't feed it with pipe and just invoke it directly, for instance: [emphasis added to show what I type]
$ python pype.py '("%r\n" % (l,) for l in sys.stdin)' fooEnter barEnter bazEnter Ctrl DCtrl D'foo\n' 'bar\n' 'baz\n' $
In order to stop accepting input and produce any output, I have to type either Enter - Ctrl D - Ctrl D or Ctrl D - Ctrl D - Ctrl D. This violates my expectations, that each line should be processed as entered, and that typing Ctrl D at any time will end the script. Where is the gap in my understanding?
EDIT: I've updated the interactive example to show that I'm not seeing the quoting wim describes in his answer, and some more examples too.
$ 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' $
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
Ctrl-D 不一定被识别为 EOF,而是“终止当前
read()
调用”。如果您有一个空行(或刚刚按下 Ctrl-D)并按下 Ctrl-D,您的
read()
将立即终止并返回 0读取字节。这是 EOF 的标志。如果一行中有数据并按 Ctrl-D,则
read()
将以键入的任何内容终止,当然没有终止换行符 (' \n'
)。因此,如果您有输入数据,请在非空行上按两次 Ctrl-D 或在空行上按一次,即之前按 Enter 。
这一切都适用于普通操作系统接口,可以通过 os.read() 从 Python 访问。
Python 文件对象以及文件迭代器将第一个 EOF 视为当前 read() 调用的终止,因为它们认为不再有任何内容了。下一个
read()
调用会再次尝试,并且需要另一个 Ctrl-D 才能真正返回 0 字节。原因是文件对象read()
总是尝试返回请求的字节数,如果操作系统read()
返回的字节数少于请求的字节数,则尝试填充。与
file.readline()
相反,iter(file)
使用内部read()
函数进行读取,因此始终有此特殊要求额外的 Ctrl-D。我总是使用 iter(file.readline, '') 来从文件中逐行读取。
Ctrl-D is recognized not necessarily as EOF, but as "terminate current
read()
call".If you have an empty line (or just pressed Ctrl-D) and press Ctrl-D, your
read()
terminates immediately and returns 0 read bytes. And this is a sign for EOF.If you have data in a line and press Ctrl-D, your
read()
terminates with whatever there has been typed, of course without a terminating newline ('\n'
).So if you have input data, you press Ctrl-D twice of a non-empty line or once on a empty one, i.e. with Enter before.
This all holds for the normal OS interface, accessible from Python via
os.read()
.Python file objects, and also file iterators, treat the first EOF recognized as termination for the current
read()
call, as they suppose there is nothing any longer. A nextread()
call tries again and needs another Ctrl-D in order to really return 0 bytes. The reason is that a file objectread()
always tries to return as many bytes as requested and tries to fill up if a OSread()
returns less than requested.As opposite to
file.readline()
,iter(file)
uses the internalread()
functions to read and thus always has this special requirement of the extra Ctrl-D.I always use
iter(file.readline, '')
to read line-wise from a file.Ctrl+D被终端设备识别,终端通过生成文件结束来响应它。也许这会有所帮助,来自维基百科(强调我的):
Ctrl+D is recognized by the terminal device, terminal responds to it by generating an end of file. Perhaps this will help, from Wikipedia (emphasis mine):
我不能确切地说为什么额外的 CTRL+D (尽管另一个答案在这方面做得很好),但这将使输入在仅一个 CTRL+D 之后打印,但您仍然需要第二次 CTRL+D 退出脚本
输出:
编辑:
eval
在这种情况下返回一个生成器,因此可能是第一个 EOF (CTRL +D) 结束阅读sys.stdin,第二个停止 eval 正在生成的生成器。生成器 - 返回迭代器的函数。它看起来像一个普通函数,只不过它包含用于生成一系列可在 for 循环中使用的值的yield 语句,或者可以使用 next() 函数一次检索一个值。每个yield都会暂时挂起处理,记住位置执行状态(包括局部变量和挂起的try语句)。当生成器恢复时,它会从上次停止的地方继续执行(与每次调用时都重新开始的函数相反)。
生成器类参考(第 9.10 节)
I can't say exactly why the extra CTRL+D (the other answer does a very good job of that though), but this will make it so the input is printed after only a single CTRL+D, but you still need to CTRL+D a second time to exit the script
Output:
Edit:
eval
is returning a generator in this case, so possible the first EOF (CTRL+D) ends the reading of sys.stdin, and the second stops the generator thateval
is producing.Generator - A function which returns an iterator. It looks like a normal function except that it contains yield statements for producing a series a values usable in a for-loop or that can be retrieved one at a time with the next() function. Each yield temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator resumes, it picks-up where it left-off (in contrast to functions which start fresh on every invocation).
Generator Class reference (section 9.10)