【问题标题】:Interaction between Python script and linux shellPython脚本与linux shell的交互
【发布时间】:2023-04-10 10:08:01
【问题描述】:

我有一个 Python 脚本,它需要通过命令行与用户交互,同时记录输出的任何内容。

我目前有这个:

# lots of code

popen = subprocess.Popen(
    args,
    shell=True,
    stdin=sys.stdin,
    stdout=sys.stdout,
    stderr=sys.stdout,
    executable='/bin/bash')

popen.communicate()

# more code

这会执行一个 shell 命令(例如 adduser newuser02),就像在终端中键入它时一样,包括交互行为。这很好。

现在,我想从 Python 脚本中记录屏幕上出现的所有内容。但我似乎无法使那部分工作。

我尝试了各种使用 subprocess.PIPE 的方法,但这通常会破坏交互性,比如不输出提示字符串。

我也尝试了各种方法来直接改变 sys.stdout 的行为,但是当子进程直接写入 sys.stdout.fileno() 时,这一切都无济于事。

【问题讨论】:

  • 附带说明,您使用shell=True 有什么原因吗?尤其是当您要启动的东西本身就是/bin/bash 时?您基本上是在告诉 Python 启动 bash 的副本以告诉它启动 bash 的另一个副本(大概,因为您没有显示整个代码)告诉它启动 adduser 的副本。你可能甚至不需要一个 shell,但我无法想象你为什么需要两个。
  • @abarnert: executable 不会启动新的bash 进程:它告诉subprocess 模块使用它的值而不是/bin/sh 如果shell=Trueargs 可能是一些带有 bash-isms 的 shell 命令,例如 'use_tty &>/dev/null'。据我了解,意图是:script -c '/bin/bash -c "$args"' log。尽管作为my answer shows,您甚至不需要一个额外的 bash 进程来在这里运行命令。

标签: python linux shell subprocess prompt


【解决方案1】:

Popen 可能不太适合交互式程序,因为buffering issuessome programs write/read directly from a terminal 例如用于检索密码。见Q: Why not just use a pipe (popen())?

如果您想模拟script utility,那么您可以使用pty.spawn(),请参阅Duplicating terminal output from a Python subprocesslog syntax errors and uncaught exceptions for a python subprocess and print them to the terminal 中的代码示例:

#!/usr/bin/env python
import os
import pty
import sys

with open('log', 'ab') as file:
    def read(fd):
        data = os.read(fd, 1024)
        file.write(data)
        file.flush()
        return data

    pty.spawn([sys.executable, "test.py"], read)

或者您可以使用pexpect 以获得更大的灵活性:

import sys
import pexpect # $ pip install pexpect

with open('log', 'ab') as fout:
    p = pexpect.spawn("python test.py")
    p.logfile = fout # or .logfile_read
    p.interact()

如果您的子进程不缓冲其输出(或不干扰交互性)并将其输出打印到其标准输出或标准错误,那么您可以尝试subprocess

#!/usr/bin/env python
import sys
from subprocess import Popen, PIPE, STDOUT

with open('log','ab') as file:
    p = Popen([sys.executable, '-u', 'test.py'],
              stdout=PIPE, stderr=STDOUT,
              close_fds=True,
              bufsize=0)
    for c in iter(lambda: p.stdout.read(1), ''):
        for f in [sys.stdout, file]:
            f.write(c)
            f.flush()
    p.stdout.close()
    rc = p.wait()

要分别读取 stdout/stderr,您可以使用 teed_call() from Python subprocess get children's output to file and terminal?

【讨论】:

    【解决方案2】:

    这应该可以工作

    import subprocess
    f = open('file.txt','w')
    cmd = ['echo','hello','world']
    subprocess.call(cmd, stdout=f)
    

    【讨论】:

    • 这不会在控制台中显示输出。这也不适用于需要用户输入的命令,例如“adduser”
    • 您的问题表明您希望将输出记录到文件中。您希望它同时在文件和控制台中登录吗?
    • 是的,我的想法是在 python 脚本的末尾,我有一个完整的日志,记录了发生的所有事情,包括脚本本身显式生成的任何日志。
    • @user480441: adduser 可能希望直接从终端输入。否则由call() 启动的子进程应该继承其父标准输入,即,您应该能够提供输入,例如,尝试subprocess.call('python')
    猜你喜欢
    • 2016-05-29
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-28
    • 2019-02-27
    • 1970-01-01
    • 2011-08-31
    相关资源
    最近更新 更多