Python subprocess.Popen在命令行参数中失败

1 投票
3 回答
2732 浏览
提问于 2025-04-18 12:28

在使用subprocess.Popen()时遇到困难 - 为什么第一个和第三个都能正常工作,而第二个却找不到任何多个文件或目录?错误信息是:

>ls: Zugriff auf * nicht möglich: Datei oder Verzeichnis nicht gefunden

英文翻译:

File not found or directory: access to * not possible

这里是代码。

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import subprocess

args = []
args.append ('ls')
args.append ('-al')

# First does work
cmd1 = subprocess.Popen(args)
cmd1.wait()

# Second does NOT work
args.append ('*')
cmd2 = subprocess.Popen(args)
cmd2.wait()


# Third does work
shellcmd = "ls -al *"
cmd3 = subprocess.Popen(shellcmd, shell=True )
cmd3.wait()

3 个回答

0

这不是直接回答你问题的内容,但你也可以试试使用Python的一个库,叫sh

举个例子:

from sh import ls

print ls("-al")

这里有更多例子的链接

1

这个问题是因为出现了shell 通配符匹配

简单来说,命令中的*ls -al *中会被你的命令行解释器(shell)处理,匹配所有可用的文件。当你在没有使用shell=True这个选项的情况下运行子进程时,Python无法自己理解*,因此就会显示错误信息ls: cannot access *: No such file or directory

而当你使用shell=True运行命令时,Python实际上是把控制权交给了命令行解释器,这样就能正确显示输出了。

另外,执行包含来自不可信来源的未经处理的输入的命令会让程序面临shell注入的风险,这是一种严重的安全漏洞,可能导致任意命令的执行,因此使用时要小心(请查看这里的警告)。


编辑 1

这里的问题是shell通配符匹配和Popen处理args的方式造成的。

根据subprocess模块

class subprocess.Popen

args应该是程序参数的一个序列,或者是一个单独的字符串。

如果shell is True,建议将args作为一个字符串传递,而不是作为一个序列。

要理解shell通配符匹配和Popen处理args的方式是这里的问题,可以比较以下输出。注意在两种情况下,当shell=True时,由于传入的是一个list而不是string,所以只执行了ls,这与推荐的做法相悖。

subprocess.Popen(['ls'])                         #works
subprocess.Popen('ls')                           #works
subprocess.Popen(['ls', '-al'])                  #works
subprocess.Popen(['ls -al'])                     #doesn't work raises OSError since not a single command
subprocess.Popen('ls -al')                       #doesn't work raises OSError since not a single command
subprocess.Popen(['ls -al'], shell=True)         #works since in shell mode
subprocess.Popen('ls -al', shell=True)           #works since in shell mode & string is single command
subprocess.Popen(['ls', '-al'], shell=True)      #output corresponds to ls only, list passed instead of string, against recommendation
subprocess.Popen(['ls', '-al', '*'])             #doesn't work because of shell globbing for *
subprocess.Popen(['ls -al *'])                   #doesn't work raises OSError since not a single commandfor *
subprocess.Popen('ls -al *')                     #doesn't work raises OSError since not a single commandvalid arg
subprocess.Popen(['ls', '-al', '*'], shell=True) #output corresponds to ls only, list passed instead of string, against recommendation
subprocess.Popen(['ls -al *'], shell=True)       #works
subprocess.Popen('ls -al *', shell=True)         #works
1

这是因为默认情况下,subprocess.Popen() 并不会让命令通过 shell 来解释,所以像 "*" 这样的符号不会被展开成需要的文件列表。你可以尝试在调用时加上 shell=True 作为最后一个参数。

另外,请注意文档中的警告,不要相信用户输入的内容会以这种方式被处理。

撰写回答