调试subprocess.Popen调用

10 投票
2 回答
24045 浏览
提问于 2025-04-15 18:55

我之前一直在用 subprocess.Popen 很顺利,主要是用它来把一些程序包装在 Python 脚本里,以便格式化参数或进行一些自定义操作。

这次我在开发一个新的包装器时,照常操作,但却没有任何反应。

这是我写的简单代码:

print command
p = subprocess.Popen(command, shell = True)
result = p.communicate()[0]
print vars(p)
return result

这是输出结果:

/usr/bin/sh /tmp/run/launch.sh
{'_child_created': True, 'returncode': 0, 'stdout': None, 'stdin': None, 'pid': 21650, 'stderr': None, 'universal_newlines': False}

正如你所看到的,我的目标是创建一个 shell 脚本,设置好我需要的一切,然后执行它。我更希望使用真正的 Python 代码,但不幸的是,launch.sh 调用了第三方的 shell 脚本,我不想去尝试复制这些脚本(尽管我已经坚持要求提供 Python 接口超过一年了)。

问题是:

  • 这个 shell 脚本没有被执行(它应该会启动一个进程并输出一些小信息)
  • 没有抛出任何 Python 异常
  • p 对象中没有任何指示错误发生的迹象

我也试过 check_call,但也没有成功...

我现在不知道该怎么办,如果有人能指出我的错误或给我一些解决方案,我会非常感激...

编辑:

  • 我在 Linux(sh)上尝试运行这个
  • 脚本中需要 shell 来进行变量替换

编辑 2:

根据 badp 的建议,我调整了代码并添加了:

subprocess.Popen('ps', shell = True).communicate()

在创建进程的 p = ... 这一行后,输出结果是:

/usr/bin/sh /tmp/run/launch.sh
  PID TTY          TIME CMD
29978 pts/0    00:00:01 zsh
 1178 pts/0    00:00:01 python
 1180 pts/0    00:00:00 sh <defunct>
 1181 pts/0    00:00:00 ps
None

显然进程已经启动(尽管显示为 <defunct>),另外我还注意到我在传递参数时遇到了一些小问题...

谢谢。

2 个回答

6

试试这个:

p = subprocess.Popen(command,
                     shell = True, #is this even needed?
                     stdin = subprocess.PIPE,
                     stdout = subprocess.PIPE,
                   # stderr = subprocess.STDOUT #uncomment if reqd
                    )

在Windows上用ping命令测试过,效果很好。这个命令可以让你进行通信,可能会帮助你找出为什么脚本一开始没有被启动的原因哦 :)

5

我终于找到了我的问题的答案,感谢 badp 和他提供的调试建议。

在 Python 的 subprocess 模块 页面上提到:

executable 参数指定要执行的程序。这个参数很少需要使用:通常要执行的程序是通过 args 参数来定义的。如果 shell=True,那么 executable 参数就指定了要使用哪个 shell。在 Unix 系统中,默认的 shell 是 /bin/sh。在 Windows 系统中,默认的 shell 是由 COMSPEC 环境变量指定的。你在 Windows 上需要指定 shell=True 的唯一原因是你想执行的命令实际上是内置在 shell 中的,比如 dircopy。运行批处理文件或控制台可执行文件时并不需要 shell=True

因为我在 Linux 上并且使用了 shell=True,所以我的命令实际上是一个要由 executable 执行的参数列表,默认值是 /bin/sh。因此,实际执行的完整命令是:/bin/sh /usr/bin/sh /tmp/run/launch.sh... 结果并不是很好。

我应该使用以下任意一种:

subprocess.Popen('/tmp/run/launch.sh', shell=True)

或者

subprocess.Popen('/tmp/run/launch.sh', executable = '/usr/bin/sh', shell=True)

有点棘手的是,shell=True 只会在 Linux 上修改默认的 executable 值...

撰写回答