为什么我必须输入ctrl-d两次?
为了自己开心,我写了一个Python脚本,这个脚本让我可以用Python来处理一些简单的命令行操作。你只需要提供一个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 都应该结束这个脚本。我的理解哪里出了问题呢?
补充说明:我更新了互动示例,显示我没有看到他在回答中提到的引号问题,还有一些其他的示例。
$ 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' $
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
在这种情况下返回的是一个生成器,所以第一次按 CTRL+D 可能结束了对 sys.stdin 的读取,而第二次则停止了 eval
生成的生成器。
生成器 - 这是一个返回迭代器的函数。它看起来和普通函数差不多,但里面有 yield 语句,用来生成一系列可以在 for 循环中使用的值,或者可以通过 next() 函数一个一个获取。每次 yield 暂时暂停处理,记住执行状态的位置(包括局部变量和待处理的 try 语句)。当生成器恢复时,它会从暂停的地方继续(这和每次调用时都重新开始的普通函数不同)。
Ctrl+D 是终端设备识别的一个组合键,按下它后,终端会生成一个“文件结束”的信号。也许下面的内容能帮你理解,这段话来自维基百科(我强调的部分):
在 UNIX 和 AmigaDOS 系统中,终端驱动程序会将这个按键转换为文件结束信号,所以程序不需要区分终端和其他输入文件。默认情况下,驱动程序会把行首的 Control-D 字符转换为文件结束的指示符。如果你想在输入流中插入一个真正的 Control-D(ASCII 04)字符,用户需要在它前面加上一个“引用”命令字符(通常是 Control-V,不过在某些系统上你可以通过连续按两次 Control-D 来实现这个效果)。
Ctrl-D 并不总是被当作文件结束符(EOF),而是被视为“结束当前的 read()
调用”。
如果你在输入框里什么都没输入(或者只是按了 Ctrl-D),再按一次 Ctrl-D,你的 read()
就会立刻结束,并返回0个读取的字节。这时候就表示文件结束了。
如果你在一行里输入了一些内容,然后按 Ctrl-D,你的 read()
会结束,并返回你输入的内容,当然不会有换行符('\n'
)。
所以,如果你有输入数据,按两次 Ctrl-D 在非空行上,或者在空行上按一次 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, '')
来逐行读取文件。