Python subprocess.Popen作为参数在shell命令上失败

时间:2014-07-08 10:08:44

标签: python shell

在subprocess.Popen()中挣扎 - 为什么First和Third按预期工作,而第二个没有找到任何多个文件或目录?错误信息是:

>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 :(得分:1)

这是因为默认情况下subprocess.Popen()没有shell解释命令,因此"*"不会扩展到所需的文件列表中。尝试添加shell=True作为呼叫的最终参数。

另请注意the warning in the documentation关于不信任以这种方式处理的用户输入。

答案 1 :(得分:1)

这是因为shell globbing而发生的。

基本上,*中的ls -al *会被您的shell扩展,以匹配所有可用文件。 当您运行没有shell=True标志的子进程时,python无法自己解析*,因此会显示错误消息ls: cannot access *: No such file or directory

当您使用shell=True运行命令时,python实际上将控件传递给shell,因此显示正确的输出。

顺便说一句,执行包含来自不受信任来源的未经过处理的输入的shell命令会使程序容易受到shell注入攻击,这是一个严重的安全漏洞,可能导致任意命令执行,因此应谨慎使用({{3} })。


编辑1

shell globbing和Popen消耗args的方式导致此问题

来自see warning here

  

class subprocess.Popen

     

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

     

如果shell is True , it is recommended to pass args as a字符串`而不是序列。

要理解shell globbing以及Popen消耗args的方式是问题,请比较以下输出。请注意,在2个案例中,当shell=True时,由于传递的输入是ls而不是list,因此只执行string

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

答案 2 :(得分:0)

不能直接回答您的问题,但您也可以尝试使用python库 sh

示例:

from sh import ls

print ls("-al")

link to more examples