Python的subprocess.Popen与shell = True和真正的shell之间的区别

时间:2018-05-15 15:36:22

标签: python bash subprocess

当我打开命令行时,我希望它在当前活动窗口所在的同一工作目录中启动。 因此,我编写了一个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

为什么会有所作为?

解决方案: 感谢Charles Duffy的评论,我发现了这个问题。 在我的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)

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

subprocess.Popen(cmd, shell=True)

1 个答案:

答案 0 :(得分:4)

不要使用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=True 100%等同于运行sh -c '...command...',其中您的命令的文字文字位于...command...;这正是它在实践中的作用

请注意,我上面删除了stderr=subprocess.PIPE。您可以重新添加,但前提是您已将代码实际读取内容写入stderr,方法是调用communicate()