代码之家  ›  专栏  ›  技术社区  ›  jakun

Python子进程之间的差异。具有shell=True和真实shell的Popen

  •  0
  • jakun  · 技术社区  · 7 年前

    当我打开命令行时,我希望它在当前活动窗口所在的同一工作目录中启动。 因此,我编写了一个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。 在加载设置字体的Xresources文件之前,xterm会向stderr打印一条警告,指出无法加载字体。 xterm有一个非常小的缓冲区。因为我的程序没有读取stderr,所以缓冲区没有被清除,xterm被阻止。

    更换

    子流程。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 回复  |  直到 7 年前
        1
  •  4
  •   Charles Duffy    7 年前

    不使用 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)
    

    也就是说, 外壳=真 100%等同于运行 sh -c '...command...' ,其中命令的文本位于 ...command... ;这是 它在实际中的作用


    请注意,我已删除 stderr=subprocess.PIPE 在上面你 可以 将其添加回,但前提是您有自己的代码 实际读取 通过调用写入stderr的内容 communicate()