Python脚本与Linux shell的交互

4 投票
2 回答
7272 浏览
提问于 2025-04-17 20:55

我有一个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

这段代码可以执行一个命令(比如说添加用户的命令adduser newuser02),就像在终端里直接输入一样,包括交互的部分。这一点很好。

现在,我想在Python脚本里记录下屏幕上出现的所有内容。但是我似乎无法让这一部分正常工作。

我尝试了各种方法来使用subprocess.PIPE,但这通常会搞乱交互,比如不输出提示信息。

我也尝试了不同的方法来直接改变sys.stdout的行为,但因为subprocess直接写入sys.stdout.fileno(),所以这些方法都没有效果。

2 个回答

0

这个应该可以用

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

Popen 可能不太适合用在需要互动的程序中,主要是因为它有一些 缓冲问题,还有一些程序是直接和终端进行读写的,比如用来获取密码。你可以看看这个链接 问:为什么不直接用管道 (popen())?

如果你想模拟 script 工具 的功能,可以使用 pty.spawn(),具体的代码示例可以参考 从 Python 子进程复制终端输出 或者 记录 Python 子进程的语法错误和未捕获的异常并打印到终端

#!/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()

如果你想分别读取标准输出和标准错误,可以使用 teed_call(),具体可以参考 Python 子进程将子进程的输出获取到文件和终端?

撰写回答