如何检查标准输入(stdin)是否有数据?

88
在Python中,如何检查sys.stdin是否有数据?我发现os.isatty(0)不仅可以检查stdin是否连接到TTY设备,还可以检查是否有可用的数据。但是如果有人使用以下代码:
sys.stdin = cStringIO.StringIO("ddd")

之后使用os.isatty(0),它仍然返回True。我需要做什么才能检查stdin是否有数据?


3
你想要实现什么目标? - shahjapan
这是因为 os.isatty(0) 检查与文件描述符(fd)0相关联的文件是否为TTY。当您更改 sys.stdin 变量时,您并没有更改与 fd0 相关联的文件。fd0 仍然指向原始标准输入(stdin)(在您的情况下是TTY)。 - Cristian Ciupitu
2
请查看这个答案,它是否与您的问题相关? - Muhammad Alkarouri
我无法理解从cStringIO.StringIO("ddd")读取数据会如何阻塞;除了当然到达EOF时,它总是有可用的数据。 - Cristian Ciupitu
https://dev59.com/4W865IYBdhLWcg3wi_I2 - Jared Goguen
显示剩余2条评论
7个回答

94

在Unix系统中,您可以执行以下操作:

import sys
import select

if select.select([sys.stdin, ], [], [], 0.0)[0]:
    print("Have data!")
else:
    print("No data")

在Windows上,select模块只能与套接字一起使用,因此您需要使用另一种机制。


2
@mlzboy:仅由操作系统打开的“真实”文件具有文件描述符号(顺便说一下,在Unix中,套接字或管道也是文件)。调用操作系统的select函数的select仅能使用这些文件。 StringIO是仅为Python解释器所知的虚拟文件,因此它没有文件描述符号,您无法在其上使用 select - Cristian Ciupitu
2
@mlzboy 管道具有有限的缓冲区大小。在没有任何读取管道的情况下写入管道是危险的,因为如果您尝试写入超过缓冲区大小的数据,它将会阻塞(死锁)。 - Aryeh Leib Taurog
1
当我在由cron执行的脚本中使用此方法时,它对我失败了。然而,在cron中,sys.stdin.isatty()对我有效。 - Dave
1
在 Jenkins 中无法工作,返回 <_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'> 而不是 [] - GuySoft
这个方法仅测试sys.stdin是否有立即可用的数据。我认为即使它连接到文件(例如,如果它连接到NFS上的文件并且底层网络连接不稳定),也不能严格保证。select用于多路复用,即从多个源/目标中选择准备好读/写的内容。 - musiphil
显示剩余3条评论

94

我一直在使用

if not sys.stdin.isatty()

这里有一个例子:

import sys

def main():
    if not sys.stdin.isatty():
        print "not sys.stdin.isatty"
    else:
        print "is  sys.stdin.isatty"

跑步

$ echo "asdf" | stdin.py
not sys.stdin.isatty

sys.stdin.isatty()如果 stdin 没有连接到一个交互式输入设备(如 tty),则返回 false。

isatty(...)
    isatty() -> true or false. True if the file is connected to a tty device.

6
谢谢!这比使用“select”容易得多。 - Niklas R
谢谢。这解决了我的问题:在Linux上,使用echo 'maybe pipe' | stdin.py +maybe_args。 melancholynaturegirl的解决方案在Windows上可行吗? - Alexx Roche
看起来是这样的...C:\Users\naturegirl\Desktop>C:\Python27\python.exe isatty.py sys.stdin.isattyC:\Users\naturegirl\Desktop> C:\Users\naturegirl\Desktop>echo "foo" | C:\Python27\python.exe isatty.py not sys.stdin.isatty - Aaron
36
这不是一个好主意。如果stdin正在从文件中传输,会怎样呢?isatty()告诉你stdin是否直接来自终端,而不是告诉你是否还有更多数据可以从stdin读取。 - Lathan
2
由于另一篇帖子中提到了grep,我要提到less使用isatty。我来这里是想做类似于less的事情:如果输入参数给出文件路径,则从文件中读取输入,否则从stdin中读取输入。另一种选择是使用“-”作为stdin的别名,并接受文件参数。 - ws_e_c421
显示剩余2条评论

4

根据这里的目标:

import fileinput
for line in fileinput.input():
    do_something(line)

这也可能会很有用。


据我所知,fileinput有一个缺点,就是参数必须是文件名,但我的参数可能是一些控制命令。 - mlzboy
好的!我试图处理“对一堆行执行某些操作,可能来自stdin”的常见习语 :) - Gregg Lind

3

正如其他人所提到的,没有一种绝对可靠的方法可以知道数据是否会从stdin中可用,因为UNIX不允许它(更普遍地说,它无法猜测stdin连接到的任何程序的未来行为)。

即使可能没有任何内容(这就是grep等工具所做的),也始终等待stdin,或者要求用户提供一个-参数。


1
所以,你通过引用同一个问题来回答问题?什么鬼?? - ForceBru
该进程的过程是什么 - 等待标准输入并读取直到EOF,然后处理数据?也就是说,grep等程序如何知道何时处理数据以及何时退出? - Matt
1
@Matt,上游数据源负责发送EOF。例如:printf "" | catwhile read l; do true; done; echo finishing(在第二种情况下,您必须手动输入EOF/^D才能通过)。 - n.caillou
请注意,如果您在命令行上只输入例如“cat”,它将无限期地等待输入。在上面的示例中,printf发送EOF。 - n.caillou
@JeremyFriesner 内容可能需要一段时间后才能获得。 - n.caillou
显示剩余4条评论

0
我找到了一种使用concurrent.futures库中的future的方法。 这是一个GUI程序,当你还在等待标准输入时,它将允许你滚动并点击等待按钮。每当程序看到从STDIN得到一行信息,它就会在多行框中打印出来。但是它不会锁定脚本,也不会使用户无法访问GUI界面,因为它正在检查新的行是否从stdin输入。(请注意,我在Python 3.10.5中测试过这个程序)
import concurrent.futures
import PySimpleGUI as sg
import sys

def readSTDIN():
    return sys.stdin.readline()

window = sg.Window("Test Window", [[sg.Multiline('', key="MULTI", size=(40, 10))],
                                   [sg.B("QUIT", bind_return_key=True, focus=True),
                                    sg.B("Wait")]], finalize=True, keep_on_top=True)
for arg in sys.argv[1:]:
    window["MULTI"].print(f"ARG={arg}")
with concurrent.futures.ThreadPoolExecutor() as pool:
    futureResult = pool.submit(readSTDIN)
    while True:
        event, values = window.read(timeout=500)
        if event != "__TIMEOUT__":
            window['MULTI'].print(f"Received event:{event}")
        if event in ('QUIT', sg.WIN_CLOSED):
            print("Quit was pressed")
            window.close()
            break
        if futureResult.done(): # flag that the "future" task has a value ready;
                                #therefore process the line from STDIN
            x = futureResult.result()
            window["MULTI"].print(f"STDIN:{x}")
            futureResult = pool.submit(readSTDIN) #add a future for next line of STDIN

-1
看到上面很多答案,几乎所有的答案都需要sys、select、os等。然而,我有一个非常简单的想法,关于如何持续从stdin获取输入数据。 我们可以使用try、except块来实现。 例如,
while(1):
  try:
    inp = input()
  except:
     break

上面的循环将会运行,我们会不断地检查输入并将其存储在变量“inp”中,如果标准输入中没有输入,那么try语句将会抛出错误,但是在except块中有一个break语句,因此while循环将被终止。

3
但是这个程序会等待输入并暂停执行。你无法根据标准输入是否为空来执行某些操作。 - SmarthBansal

-2

使用内置模块,可以通过以下代码实现,就像Gregg已经提出的想法一样:

import fileinput
isStdin = True
for line in fileinput.input:
    # check if it is not stdin
    if not fileinput.isstdin():
        isStdin = False
        break
    # continue to read stdin
    print(line)
fileinput.close()

fileinput.input 是一个不可迭代的函数,当没有数据时,它会永久阻塞。 - Nate Scarlet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接