Python的子进程之间的区别。带有 shell=True 和真实 shell 的 Popen



当我打开命令行时,我希望它在当前活动窗口所在的同一工作目录中启动。 因此,我编写了一个python脚本来找出正确的路径。它采用打开终端的命令作为参数。在此参数中,它将占位符替换为所需的目录,并执行命令

subprocess.Popen(cmd, stderr=subprocess.PIPE, shell=True)

字符串cmd正确组装为:

xterm -e 'cd '''/mnt/data/software/computer/tools/i3'''; /usr/bin/bash'

如果我在 bash 中执行此命令,它会根据需要在正确的目录中打开 xterm。 如果我从python脚本执行此命令xterm不会打开。

区别在哪里?


附加信息:

我正在使用Python 3.6.5。

echo $SHELL返回/bin/bash.

我的 i3 配置中脚本的键绑定:

bindsym $mod+Return exec "/mnt/data/software/computer/tools/i3/i3_launch_cwd.sh \"xterm -e 'cd %{cwd}; /usr/bin/bash'\""

(这个 shell 脚本只是一个简单的包装器,它将 stderr 重定向到日志文件进行调试)

在 bash 中执行以下命令后,xterm 甚至可以从 python 打开:

xrdb -merge -I$HOME ~/.Xresources

Xresources 中的相关行是

xterm*faceName: DejaVu Sans Mono Book

为什么会有所不同?


解决方案: 多亏了查尔斯·达菲的评论,我发现了问题所在。 在我的 python 脚本中,我正在使用stderr=subprocess.PIPE重定向 stderr,但我忘了阅读 stderr。 在加载设置字体的 Xresources 文件之前,xterm 会向 stderr 打印一条警告,指出无法加载字体。 XTERM有一个非常小的缓冲区。因为我的程序没有读取 stderr,所以缓冲区没有被清除,xterm 被阻止了。

取代

subprocess.Popen(cmd, stderr=subprocess.PIPE, shell=True)

p = subprocess.Popen(cmd, stderr=subprocess.PIPE, shell=True)
out, err = p.communicate()
sys.stderr.write(err)

会解决问题。 但是因为我没有对stdrr做任何事情,所以没有必要首先重定向它。 所以相反,我现在使用

subprocess.Popen(cmd, shell=True)

不要使用shell=True; 它不适合您的用例。

import subprocess
try:
from pipes import quote # Python 2
except ImportError:
from shlex import quote # Python 3
dir='/mnt/data/software/computer/tools/i3'
p = subprocess.Popen(['xterm', '-e', 'cd %s && exec bash' % quote(dir)])

但是,您可以通过让subprocess.Popen为您设置目录来简化此操作:

dir='/mnt/data/software/computer/tools/i3'
p = subprocess.Popen(['xterm', '-e', 'bash'], cwd=dir)

也就是说,shell=True100% 等同于运行sh -c '...command...',其中命令的文字文本是...command...;这正是它在实践中所做的


请注意,我删除了上面的stderr=subprocess.PIPE您可以重新添加它,但前提是您的代码实际读取写入 stderr 的内容,例如通过调用communicate().

最新更新