You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

subprocess.Popen与os.system对比:带交互的SSH命令问题

解决subprocess执行带sudo的ssh命令时的交互问题

嘿,我碰到过类似的坑,这就帮你搞定这个交互问题!首先得理清楚为啥os.system(foo)能正常工作,subprocess却不行:os.system会直接把当前终端的标准输入、输出、错误流绑定到子进程,所以ssh弹出的sudo密码请求能直接显示在你的终端里,你输入的密码也能正常传给远程服务器。但subprocess默认不会这么做——如果没正确配置IO流,子进程的输入输出会被管道接管,导致你看不到密码提示,输入也传不进去,自然就异常了。

下面给你两种靠谱的解决方案:

方案一:直接继承终端IO流(最简单)

如果你的脚本是在终端里直接运行的,只需要让subprocess完全继承父进程的IO流就行,不用搞复杂的管道,直接这么写:

import subprocess

foo = "ssh -t -p 200 username@remoteserver -C -Y sudo <需要sudo权限的命令>"
# 关键是让subprocess继承当前终端的stdin/stdout/stderr
subprocess.run(foo, shell=True, stdin=None, stdout=None, stderr=None)

这里shell=True是必须的,因为你的命令里包含了空格、sudo参数这些shell需要解析的内容;stdin=None等参数会让subprocess直接用当前终端的IO流,和os.system的行为一致,这样就能正常看到密码提示并输入了。

方案二:用pty模块模拟终端(应对sudo的requiretty限制)

有些服务器的sudo配置里开启了requiretty选项(要求sudo必须在终端环境下运行),这时候即使你用了方案一,可能还是会报错。这时候就需要用pty模块创建一个伪终端,让ssh和sudo以为自己在真实终端里:

import os
import pty
import subprocess
import sys

def run_interactive_ssh(cmd):
    # 创建伪终端的主从两端
    master_fd, slave_fd = pty.openpty()
    # 启动子进程,把IO绑定到伪终端的slave端
    proc = subprocess.Popen(
        cmd,
        shell=True,
        stdin=slave_fd,
        stdout=slave_fd,
        stderr=slave_fd,
        close_fds=True
    )
    # 关闭slave端,避免资源泄漏
    os.close(slave_fd)
    
    try:
        # 循环读取伪终端的输出,打印到终端
        while True:
            output = os.read(master_fd, 1024)
            if not output:
                break
            sys.stdout.write(output.decode())
            sys.stdout.flush()
            # 读取用户输入,写入伪终端传给远程服务器
            user_input = sys.stdin.readline()
            os.write(master_fd, user_input.encode())
    except KeyboardInterrupt:
        # 处理Ctrl+C中断
        proc.terminate()
    finally:
        os.close(master_fd)
        proc.wait()

# 调用函数执行命令
foo = "ssh -t -p 200 username@remoteserver -C -Y sudo <需要sudo权限的命令>"
run_interactive_ssh(foo)

这个方法会模拟一个终端环境,完美解决sudo对终端的要求,同时保证输入输出的正常交互。

额外提醒

  • 如果你不想用shell=True(避免shell注入风险),可以把命令拆分成列表形式,比如:
    foo = [
        "ssh", "-t", "-p", "200", "username@remoteserver",
        "-C", "-Y", "sudo", "<你的命令>", "<命令参数>"
    ]
    
    然后把shell=True去掉,直接传给subprocess就行。
  • 不要随便用capture_output=True或者stdout=subprocess.PIPE,这些会把输出捕获到内存里,导致你看不到密码提示,输入也传不进去。

内容的提问来源于stack exchange,提问作者Donald Hall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:37:26