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 · 6681 次点击
    这是一个创建于 1781 天前的主题,其中的信息可能已经有所发展或是发生改变。

    各位好。

    提问前,我已经 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   ·   实用小工具   ·   1521 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 16:41 · PVG 00:41 · LAX 08:41 · JFK 11:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.