代码之家  ›  专栏  ›  技术社区  ›  Michael Kristofik

为什么我必须按两次ctrl+d来关闭stdin?

  •  12
  • Michael Kristofik  · 技术社区  · 14 年前

    我有下面的python脚本,它读取数字并在输入不是数字时输出错误。

    import fileinput
    import sys
    for line in (txt.strip() for txt in fileinput.input()):
        if not line.isdigit():
            sys.stderr.write("ERROR: not a number: %s\n" % line)
    

    如果我从stdin获得输入,我必须按 Ctrl键 + D 两次 结束程序。为什么?

    我只需要按 Ctrl键 + D 有一次我自己运行python解释器。

    bash $ python test.py
    1
    2
    foo
    4
    5
    <Ctrl+D>
    ERROR: not a number: foo
    <Ctrl+D>
    bash $
    
    5 回复  |  直到 9 年前
        1
  •  14
  •   Jason Orendorff    9 年前

    在python 3中,这是由于在python的标准I/O库中有一个bug导致的。该bug已在python 3.3中修复。


    在Unix终端中,输入ctrl+d并不会关闭进程的s t d in。但是,输入enter或ctrl+d确实会导致OS read system call立即返回。所以:

    >>>sys.stdin.read(100)
    XYZZY(我在这里按Enter键)
    (按一次ctrl+d)
    'XyZy\n’
    >>>
    < /代码> 
    
    

    sys.stdin.read(100)is deleged tosys.stdin.buffer.read,which calls the system read()in a loop until either it accumulates the full requested amount of data;or the system read()returns 0 bytes;or an error occurs.(docs)(source)中。

    在第一行后按Enter键导致系统read()返回6个字节。sys.stdin.buffer.readcalled read()again to try to get more input.然后我按了ctrl+d,导致read()返回0个字节。此时,sys.stdin.buffer.read.放弃并返回之前收集的6个字节。

    请注意,进程中的stdin上仍然有我的终端,我仍然可以键入内容。

    >>>sys.stdin.read()(注意我仍然可以向python键入stuff)
    XYZZY(按Enter键)
    (再次按ctrl+d)
    'XyZy\n’
    < /代码> 
    
    <好的。这是最初提出这个问题时被打断的部分。它现在起作用了。但在python 3.3之前,有一个bug。

    这个bug有点复杂——基本上问题是两个独立的层在做相同的工作。bufferedreader.read()was written to callself.raw.read()repeated until it returned 0 bytes.然而,raw方法,fileio.read(),执行了一个循环,直到其自身的零字节。因此,当您第一次使用此bug在Python中按ctrl+d时,它将导致fileio.read()to return 6 bytes tobufferedreader.read(),which will then immediately callself.raw.read()again.第二个ctrl+d会导致返回0个字节,然后bufferedreader.read().would finally exit.

    不幸的是,这个解释比我以前的解释要长得多,但它有正确的优点。虫子就是这样…

    ,键入ctrl+d实际上不会关闭进程的stdin。但是输入enter或ctrl+d会导致操作系统read系统调用立即返回。所以:

    >>> sys.stdin.read(100)
    xyzzy                       (I press Enter here)
                                (I press Ctrl+D once)
    'xyzzy\n'
    >>>
    

    sys.stdin.read(100)委派给sys.stdin.buffer.read,它在循环中调用系统read(),直到它累积所请求的全部数据量;或者系统read()返回0字节;或者发生错误。(DOCS)(source)

    在第一行后按Enter键导致系统read()返回6个字节。sys.stdin.buffer.read读取再次调用read()以尝试获取更多输入。然后我按了ctrl+d,导致read()返回0个字节。在这一点上,sys.stdin.buffer.read读取放弃并返回之前收集的6个字节。

    请注意,这个过程仍然在stdin上有我的终端,并且我仍然可以输入东西。

    >>> sys.stdin.read()        (note I can still type stuff to python)
    xyzzy                       (I press Enter)
                                (Press Ctrl+D again)
    'xyzzy\n'
    

    好啊。这是最初提出这个问题时被打断的部分。它现在起作用了。但在Python3.3之前,有一个bug。

    这个bug有点复杂——基本上问题是两个独立的层在做相同的工作。BufferedReader.read()是为了打电话self.raw.read()重复,直到返回0字节。然而,原始方法,FileIO.read(),执行循环,直到其自身的字节为零。所以当你第一次用这个bug在一个python中按下ctrl+d时,它会导致文件IO.read()返回6字节到BufferedReader.read()。,然后立即调用原始的()再一次。第二个ctrl+d会导致那个返回0字节,然后BufferedReader.read()。最终会退出。

    不幸的是,这个解释比我以前的解释要长得多,但它有正确的优点。虫子就是这样…

        2
  •  9
  •   Alok Singhal    14 年前

    这很可能与以下的Python问题有关:

    • 5505 : sys.stdin.read() 在Windows上的第一个EOF之后不返回,并且
    • 1633941 : for line in sys.stdin: 第一次没有注意到EOF。
        3
  •  4
  •   Community holdenweb    7 年前

    我在回答这个问题时写了一个解释。

    How to capture Control+D signal?

    简而言之,终端的control-d只会使终端刷新输入。这使得 read 系统调用返回。第一次返回非零值(如果您键入了什么)。第二次,它返回0,这是“文件结束”的代码。

        4
  •  0
  •   jathanism    14 年前

    第一次它认为它是输入,第二次它是为保持!

    只有当输入来自tty时才会发生这种情况。很可能是由于终端设置的原因,在输入换行符(回车)之前,将缓冲字符。

        5
  •  0
  •   Tony    11 年前

    使用“for line in file:”从文件中读取行的形式,python使用隐藏的预读缓冲区(请参见 http://docs.python.org/2.7/library/stdtypes.html#file-objects 在file.next函数中)。首先,这解释了为什么在读取每个输入行时写入输出的程序在按ctrl-d之前不显示输出。其次,为了给用户一些缓冲控制权,按ctrl-d将输入缓冲区刷新为应用程序代码。当输入缓冲区为空时按ctrl-d将被视为eof。

    把这个结合起来回答原来的问题。在输入一些输入之后,第一个ctrl-d(单独在一行上)会将输入刷新到应用程序代码中。既然缓冲区是空的,那么第二个ctrl-d将充当文件尾(eof)。

    file.readline() 不会表现出这种行为。