Python 如何获取终端实时输出 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
oIMOo
V2EX    Python

Python 如何获取终端实时输出

  •  
  •   oIMOo 2020-02-06 19:14:06 +08:00 7093 次点击
    这是一个创建于 2073 天前的主题,其中的信息可能已经有所发展或是发生改变。

    各位好。

    提问前,我已经 Google 了一圈,因为种种原因,我并没有找到答案。然后才来提问。

    首先理想状态是这样的:

    • 执行 A 文件
    • 执行过程中,终端会显示提示,输入 token
    • 按照提示,自动输入 token 后,程序继续执行,并定期显示结果

    问题 (1): 同一个文件可以做到交互吗?

    不能的话,就退而求其次,使用两个文件。

    • 通过文件 B 执行文件 A
    • B 获取 A 的提示 (此时 A 并没有运行结束,等待值传入)
    • B 根据提示给 A 传参
    • B 获取 A 后续输出并显示

    问题 ( 2 ): 我会基本的获取某命令输出。但由于上面获取输出时,A 并没有执行完成。 尝试了 subprocess.Popen 和 os.popen 都获取不到,也可能时我方法不对? 求如何获取?

    附:A.py 文件实例:

    x = input("Value x: ") print("x = ", x) 

    目的: B 要获取这个 “Value x: ”,并根据获取到的结果再传入指定的值,完成 input()。

    感谢!


    目前的辣鸡方法:

    执行 A 并使用 tee 输出到 C,然后读 C 进行处理……

    第 1 条附言    2020-02-06 22:56:55 +08:00
    目前唯一的办法还是通过第三个文件,见 #5 链接。

    这是不需要第三个文件,根据大家集思广益的版本 : https://www.heypasteit.com/clip/0IUD2I
    但依然会阻塞在 read (line 12),求大佬支招。
    第 2 条附言    2020-02-07 16:37:33 +08:00
    经历了众多网友的指导和帮助,也经历了各种网络搜索,我开始觉得,是不是该“推卸责任”了?

    细读 subprocess 文档的过程中,这句话 “The run() function was added in Python 3.5” 让我想起,曾经和同事因为 package 版本不同而结果不同的坑。

    pip list 查看 subprocess,subprocess 并不在其中,居然是 python 自带的。

    检查本机 python 版本:
    pytrhon --version > Python 3.6.9

    抬头看了看官网文档的 python 版本:3.6.10 ,还有 3.7,3.8 和 3.9 (dev)

    我这个更新狂人怎么会不是最新版本呢? apt search python3 看看 >
    python3/bionic-updates,now 3.6.7-1~18.04 amd64 [installed]
    interactive high-level object-oriented language (default python3 version)
    默认版本……

    手动安装 python3.8,创建 virtualenv 并指定 3.8 版本。
    测试代码,一切正常了!!!

    好了,我现在去研究研究到底 subprocess 在 3.6.9 和 3.8 里面有什么区别……
    如果大家有了解和兴趣可以回复在下面。

    再次感谢所有朋友的帮助!!!
    29 条回复    2020-02-07 16:42:33 +08:00
    wuwukai007
        1
    wuwukai007  
       2020-02-06 19:19:30 +08:00
    同一个文件可以吧,处理完后调用 os.system("clear")不就好了
    Cooky
        2
    Cooky  
       2020-02-06 19:21:44 +08:00 via Android
    搜一下 expect 模块
    subprocess 可以实现,但是我忘了(
    CallMeReznov
        3
    CallMeReznov  
       2020-02-06 19:36:19 +08:00
    搜索 LS 的关键字 就可以了
    Death
        4
    Death  
       2020-02-06 19:46:24 +08:00 via Android
    pwntools 能做到
    oIMOo
        5
    oIMOo  
    OP
       2020-02-06 20:20:59 +08:00
    @Cooky #2 @CallMeReznov #3

    output = subprocess.Popen(['python', './A.py'], stdout=subprocess.PIPE ).communicate()[0]
    print(output)
    我搜到大部分都是这个,然而第一行就阻塞了- -

    来源于 https://stackoverflow.com/questions/4408377/how-can-i-get-terminal-output-in-python

    用一个人发了另一种办法,写到临时文件,确实比 tee 好一点- -
    fzinfz
        6
    fzinfz  
       2020-02-06 20:48:44 +08:00
    muzuiget
        7
    muzuiget  
       2020-02-06 21:31:23 +08:00
    这不就是普通的管道处理吗?难道楼主写输出时没有 flush ?
    omph
        8
    omph  
       2020-02-06 21:33:13 +08:00
    [Pexpect 模块使用说明]( https://www.jianshu.com/p/cfd163200d12)
    mxalbert1996
        9
    mxalbert1996  
       2020-02-06 21:35:09 +08:00 via Android
    不就是 Popen 然后 communicate 吗? Popen 怎么可能阻塞? communicate 带个 timeout 然后循环就好。
    oIMOo
        10
    oIMOo  
    OP
       2020-02-06 21:40:40 +08:00
    @Cooky #2
    @CallMeReznov #3
    @muzuiget #7

    https://www.heypasteit.com/clip/0IUD26

    上面连接是代码,很尴尬的卡在了 read 那一行(带注释)。
    如果去掉这一行,A 就可以顺利接受发过去的 888,并结束运行。


    @fzinfz #6
    我看同一个帖子,带 Popen 里添加 bufsize=1 看起来是一个效果,我就加上了。
    不过目前好像不是这个的原因- -
    oIMOo
        11
    oIMOo  
    OP
       2020-02-06 21:41:39 +08:00
    @mxalbert1996 #9
    感谢回复,我暂时没用到 communicate,但是试过 wait。
    我的代码在 #10.
    卡在了 read 那一行……
    leiz
        12
    leiz  
       2020-02-06 21:51:21 +08:00
    试试
    python -u
    oIMOo
        13
    oIMOo  
    OP
       2020-02-06 21:57:44 +08:00
    @mxalbert1996 #9
    我去读了下文档 docs[dot]python[dot]org[slash]2[slash]library[slash]subprocess[dot]html
    并且尝试了一下,如果把带注释的 read 行替换成 communicate(timeout=1.0), 会直接因为 A 文件的 input 报错
    ```
    x value: Traceback (most recent call last):
    File "./A[dot]py", line 2, in <module>
    x = input("x value: ")
    EOFError: EOF when reading a line
    lxy42
        14
    lxy42  
       2020-02-06 22:12:54 +08:00
    阻塞的原因是 A.py 需要 input, 因此在 B.py 使用 subprocess 执行 A.py 需要写数据到 stdin, 例如 subprocess.Popen.communicate 的 input 参数.
    oIMOo
        15
    oIMOo  
    OP
       2020-02-06 22:13:57 +08:00
    @omph #8
    感谢,第一次看到这个包~
    不过以下代码,什么都没有发生,哭……

    cmd = "python ./tmp.py"
    process = pexpect.spawn(cmd, logfile=sys.stdout)
    process.logfile_read = sys.stdout
    oIMOo
        16
    oIMOo  
    OP
       2020-02-06 22:16:39 +08:00
    @lxy42 #14

    根据错误提示,我也感觉是这个原因。
    但是我的 input 的参数,需要根据 A 中 input 语句来进行判断。
    比如说 x value 我给它 666, 说 y value 我给它 888.

    这么一来,好像就不能用 communite,而是单独用 read 和 write。
    但是呢…… 就卡住了- -

    ---

    我把 input 改成 print(提示) 和 input() 试试……
    lxy42
        17
    lxy42  
       2020-02-06 22:34:34 +08:00
    @oIMOo subprocess 不太适合这种两个进程频繁通信的情况, 可以考虑使用 RPC.
    chibupang
        18
    chibupang  
       2020-02-06 22:48:41 +08:00 via iPhone
    A 在 input 之后输出提示到 tmp.txt ,B 读取 tmp.txt ,获得 A 的输出。
    chibupang
        19
    chibupang  
       2020-02-06 22:49:41 +08:00 via iPhone
    相当于记录 A 的日志,B 读取 A 的日志。
    oIMOo
        20
    oIMOo  
    OP
       2020-02-06 22:51:50 +08:00
    @chibupang #18
    这个是最初的办法,我最最开始用的 tee 命令。
    后来经过提示,发现了 #5 的临时文件方法。

    不过如果有不需要提三个文件的办法,那就最好了。
    chibupang
        21
    chibupang  
       2020-02-06 23:02:46 +08:00 via iPhone
    那可以考虑使用 socket 通信,A 在 input 的时候并不是真的 input,而是将提示发送给 B,B 接收到提示后,可以从键盘输入,也可以自动设置 token,并把结果返回,这样 A 的输入就完全交给 B 去实现了。
    (不知道你的具体场景是什么。
    oIMOo
        22
    oIMOo  
    OP
       2020-02-06 23:09:44 +08:00
    @chibupang #21
    访问一个 API: 需要通过 API 给出的链接 -> 登陆 -> 获取 PIN -> 在终端里输入。
    每次(每个 session )都会生成新链接,然后重新获取 PIN。

    中间部分,登陆 + 获取 PIN,我是没问题的。
    然而怎么拿到链接卡住了……
    简化下来就是主题里 A.py 的场景。
    chibupang
        23
    chibupang  
       2020-02-06 23:21:14 +08:00 via iPhone
    a@oIMOo #22 根据你的描述,A 应该是可以得到每次的新链接,那么只需要把这个链接发送给 B,B 去访问这个链接,完成登录,返回 PIN 给 A。
    oIMOo
        24
    oIMOo  
    OP
       2020-02-06 23:27:30 +08:00
    @chibupang #23
    对的,但是你看我 append 的代码,我抓不到这个链接……
    应该说我抓不到 所有 仍在运行的输出……
    omph
        25
    omph  
       2020-02-07 09:55:13 +08:00
    我试了下,expect 模块应该可以吧,只用 expect 脚本试了下

    ```
    #!/bin/expect -f

    set timeout 5;
    spawn python A.py;
    expect {
    "*Value x:*" { send 3\r; exp_continue; }
    eof { exit 0; }
    }
    ``
    noclin
        26
    noclin  
       2020-02-07 12:01:09 +08:00
    test1.py
    ```
    print(input("input1\n"))

    print(input("input2\n"))
    ```

    test2.py
    ```
    import subprocess

    p = subprocess.Popen(["python", "test1.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True,
    bufsize=1)

    while p.poll() is None:
    line = p.stdout.readline()
    print("[out]", line.encode())
    if line == "input1\n":
    p.stdin.write("hello\n")
    p.stdin.flush()
    elif line == "input2\n":
    p.stdin.write("world\n")
    p.stdin.flush()
    ```
    试一下这样?要注意输出必须有\n 不然会卡在 read
    noclin
        27
    noclin  
       2020-02-07 15:45:10 +08:00
    再附加一条,没有换行存在阻塞的情况,要按单个字符读取,存入暂存区自己判断即可
    ```
    import subprocess

    p = subprocess.Popen(["python", "a.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True,
    bufsize=1)

    stash = ""
    while p.poll() is None:
    c = p.stdout.read(1)
    stash += c
    print(c, end='')
    if stash == "Value x: ":
    print()
    print("[bingo]", stash)
    stash = ""
    p.stdin.write("hello\n")
    ```


    输出

    ```
    Value x:
    [bingo] Value x:
    x = hello
    ```
    oIMOo
        28
    oIMOo  
    OP
       2020-02-07 16:28:12 +08:00
    @noclin #27
    我电脑有毒,依然不行…… 为了找到哪里阻塞,我试了一个只有四行的版本:

    import subprocess
    p = subprocess.Popen(['python', './A[dot]py'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True, bufsize=1)
    c = p.stdout.read(1)
    print(c, end='')

    然而还是不行,我就开始检查环境之旅…… 发现是 python 的问题……
    目前已解决,我写在 append 里面去。
    oIMOo
        29
    oIMOo  
    OP
       2020-02-07 16:42:33 +08:00
    感谢所有朋友的帮助,问题已解决,原因在 append 中。
    @wuwukai007 #1 @Cooky #2 @CallMeReznov #3 @fzinfz #6 @muzuiget #7 @mxalbert1996 #9 @leiz #12 @lxy42 #14 @noclin #26

    @omph #8
    感谢,一直在研究 subprocess,我也看一下 Pexpect

    @chibupang #18
    socket 不太适合我这个场景( A[dot]py 在实际中不能变更),但是也非常感谢!

    @Death #4
    pwntools 当年学安全的时候用过,现在全都忘光了哈哈哈,我再去捡起来。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3004 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 13:30 PVG 21:30 LAX 06:30 JFK 09:30
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86