【问题标题】:Python's subprocess does not interpret "~" as expected on cygwinPython 的子进程在 cygwin 上未按预期解释“~”
【发布时间】:2025-12-28 13:25:16
【问题描述】:

第 17.1.1.1 节。的documentation 状态:

如果 shell 为 True,指定的命令将通过 shell 执行。如果您将 Python 主要用于它在大多数系统 shell 上提供的增强控制流,并且仍然希望方便地访问其他 shell 功能,例如 shell 管道、文件名通配符、环境变量扩展和 ~ 到用户家的扩展,这将很有用目录。

然而,在 cygwin 上,bash 的输出与 Python 的子进程 的输出不同,即:

重击:

$ ls -ls ~rbarakx
total 0
0 drwxr-xr-x 1 Administrator None 0 Aug 21 17:54 bash
0 drwxr-xr-x 1 Administrator None 0 Jul 11 09:11 python

Python:

>>> subprocess.call(["ls","-ls","~rbarakx"],shell=True)
RCS  mecha.py  print_unicode.py  req.py  requests_results.html  selen.py
0

看起来好像 subprocess.call 只是在执行 ls

你能说出原因吗?

我的环境:
Python:Python 2.7.3(默认,2012 年 12 月 18 日,13:50:09)[GCC 4.5.3] 开启 cygwin
cygwin:CYGWIN_NT-6.1-WOW64 ... 1.7.22(0.268/5/3) ... i686 Cygwin
windows:Windows 7 Ultimate

【问题讨论】:

    标签: python bash shell cygwin subprocess


    【解决方案1】:

    在 Windows 上,shell (cmd.exe) 不支持将 ~ 扩展到用户的主目录。就此而言,也不是通配符扩展。在这一点上,Python 文档非常面向 UNIX/Linux。

    “但是等等!”我听到你哭了。 “我已经安装了cygwin,我有bash!”当然你知道,但你很难指望 Python 知道这一点。你在 Windows 上,所以它将使用 Windows shell。如果不是这样,期望 shell 为 cmd.exe 的 Python 脚本会非常混乱。

    【讨论】:

    【解决方案2】:

    看起来 subprocess.call 只是在执行 ls。

    你能说出原因吗?

    因为在 Unix (cygwin) 上,您的调用相当于:

    # WRONG: DON'T DO IT
    rc = subprocess.call(["/bin/sh", "-c"] + ["ls","-ls","~rbarakx"])
    

    换句话说,参数列表被传递给 shell 本身,即 ls 看不到 -ls~rbarakx 它们被认为是 /bin/sh 参数。

    你想要的是:

    rc = subprocess.call(["/bin/sh", "-c"] + ["ls -ls ~rbarakx"])
    

    或者使用shell=True:

    rc = subprocess.call("ls -ls ~rbarakx", shell=True)
    

    这是来自subprocess docs for reference的报价:

    在 Unix 上 使用 shell=True,shell 默认为 /bin/sh。如果args 是 string,字符串指定要通过shell执行的命令。 这意味着字符串必须完全按照它的格式进行格式化 在 shell 提示符下键入时。这包括,例如,引用或 反斜杠转义带有空格的文件名。 如果 args 是 序列,第一项指定命令字符串,任何 附加项将被视为 shell 的附加参数 也就是说,Popen 相当于:

    Popen(['/bin/sh', '-c', args[0], args[1], ...])
    

    重点是我的

    你也可以在没有任何 shell 的情况下执行命令:

    import os
    import subprocess
    
    rc = subprocess.call(["ls", "-ls", os.path.expanduser("~rbarakx")])
    

    【讨论】:

    • 感谢 J.F. Sebastian,现在很清楚了,确实 subprocess.call("ls -ls ~rbarakx", shell=True)subprocess.call(["ls", "-ls", os.path.expanduser("~rbarakx")]) 产生的结果与从 shell 产生的结果相同。
    【解决方案3】:

    有趣的是,当将命令作为字符串而不是元组传递时,它似乎就像在 bash 中执行它一样工作。

    subprocess.call(["ls -ls ~"], shell=True)
    

    如果您坚持使用元组,请考虑以下解决方法:

    cmd = subprocess.list2cmdline(["ls","-ls","~rbarakx"])
    subprocess.call(cmd, shell=True)
    

    或者这个:

    from os.path import expanduser
    subprocess.call(["ls","-ls", expanduser("~rbarakx")])
    

    【讨论】:

    • 感谢 Specur。只是一个小提示 - subprocess.call(["ls -ls","~rbarakx"], shell=True) 实际上并没有产生预期的结果(所有其他人都会这样做)。
    • 我明白了...哦,好吧,我已经编辑了答案以反映这一点。感谢您告知此事。
    • list2cmdline() 使用 MS C 运行时规则进行转义。在这种情况下不合适(OP在Unix(cygwin)环境中运行)如果要创建适合sh的字符串,可以使用" ".join(map(pipes.quote, args))(尽管最好直接传递args) .它不会扩展 ~