Python子进程命令作为列表而不是字符串
我需要使用Python中的subprocess模块通过重定向stdout来创建一些新文件。由于存在安全漏洞,我不想使用Python子进程命令作为列表而不是字符串,python,subprocess,Python,Subprocess,我需要使用Python中的subprocess模块通过重定向stdout来创建一些新文件。由于存在安全漏洞,我不想使用shell=True 我编写了一些测试命令来解决这个问题,我发现这是可行的: import subprocess as sp filer = open("testFile.txt", 'w') sp.call(["ls", "-lh"], stdout=filer) filer.close() 但是,当我将命令作为一个长字符串而不是列表传递时,它找不到文件。所以当我写这篇文章时
shell=True
我编写了一些测试命令来解决这个问题,我发现这是可行的:
import subprocess as sp
filer = open("testFile.txt", 'w')
sp.call(["ls", "-lh"], stdout=filer)
filer.close()
但是,当我将命令作为一个长字符串而不是列表传递时,它找不到文件。所以当我写这篇文章时:
import subprocess as sp
filer = open("testFile.txt", 'w')
sp.call("ls -lh", stdout=filer)
filer.close()
我收到了这个错误:
Traceback (most recent call last):
File "./testSubprocess.py", line 16, in <module>
sp.call(command2, stdout=filer)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 524, in call
return Popen(*popenargs, **kwargs).wait()
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 711, in __init__
errread, errwrite)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1308, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
回溯(最近一次呼叫最后一次):
文件“/testSubprocess.py”,第16行,在
sp.call(command2,stdout=filer)
文件“/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py”,第524行,在调用中
返回Popen(*popenargs,**kwargs)。等待()
文件“/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py”,第711行,在__
错误读取,错误写入)
文件“/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py”,第1308行,在执行子进程中
引发子对象异常
OSError:[Errno 2]没有这样的文件或目录
为什么将参数作为字符串或列表传递很重要?这是因为参数被解释为可执行文件名。如果将
“ls-lh”
放入shell中,则情况相同
luk32:~/projects/tests$ "ls -lh"
bash: ls -lh: command not found
有一个用于此的实用程序称为
但我认为你不需要它。只需假设使用列表是正确的方法,并且该工具支持从不推荐的
shell=True
模式进行转换。如果希望字符串像在shell中一样进行分割,请使用:
顺便说一句,我在这里的时候,让我说明一下。没有它,例如,如果您添加了无效参数,您将得到空输出。您可能会想知道为什么文件管理器的输出是空的
with open("testFile.txt", 'w') as filer:
sp.check_call(shlex.split("ls -lh0"), stdout=filer)
使用check\u call
您会得到一个错误,该错误会定位问题并阻止后续代码执行:
Traceback (most recent call last):
File "go.py", line 6, in <module>
sp.check_call(shlex.split("ls -lh0"), stdout=filer)
File "/usr/lib/python2.7/subprocess.py", line 540, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['ls', '-lh0']' returned non-zero exit status 2
回溯(最近一次呼叫最后一次):
文件“go.py”,第6行,在
sp.check_调用(shlex.split(“ls-lh0”),stdout=filer)
文件“/usr/lib/python2.7/subprocess.py”,第540行,在check_调用中
引发被调用的进程错误(retcode,cmd)
subprocess.CalledProcessError:命令'['ls','-lh0']'返回非零退出状态2
这是因为通话的方式:
使用shell=True
,通过shell执行调用,并将命令作为一个字符串发送给shell
使用shell=False
,通过execv()
和相关函数直接执行调用。这些函数输出一系列参数
如果只传递一个字符串,则它将被视为仅包含可执行文件名且不带参数的调用的缩写。但是(可能)在您的系统上没有名为ls-lh的可执行文件
确切地说,在subprocess.py
的深处,会发生以下情况:
if isinstance(args, types.StringTypes):
args = [args]
else:
args = list(args)
因此,传递的每个字符串都被转换为一个包含一个元素的列表
if shell:
args = ["/bin/sh", "-c"] + args
这个我不知道:显然,这允许向被调用的shell传递额外的参数尽管以这种方式记录,但不要使用它,因为它会造成太多混乱
如果shell=False
,我们将在下面进一步说明
if env is None:
os.execvp(executable, args)
else:
os.execvpe(executable, args, env)
它只是获取一个列表并将其用于调用。根据subprocess.py中的注释:
在UNIX上,shell=False(默认值):在本例中,Popen类
使用os.execvp()执行子程序。args通常应该
这是一个序列。字符串将被视为具有该字符串的序列
作为唯一项(要执行的程序)
在UNIX上,shell=True:如果args是字符串,则指定
要通过shell执行的命令字符串。如果args是一个序列,
第一项指定命令字符串和任何其他项
将被视为附加的shell参数
在Windows上:Popen类使用CreateProcess()执行子进程
对字符串进行操作的程序。如果args是一个序列,它将是
使用list2cmdline方法转换为字符串。请注意
并非所有MS Windows应用程序对命令行的解释都相同
方式:list2cmdline是为使用相同
规则作为MS C运行时
在UNIX子进程中,调用('ls-l')将失败,而在Windows中它将成功。导致问题的是os.execvp()。问题是整个字符串作为参数传递。如果您执行subprocess.call('free'),它将在UNIX中成功。一个小贴士:不要同时提示列表参数和shell=True
的可能性——这是一个常见的错误,它甚至出现在subprocess
模块的PyMOTW页面中。@J.F.Sebastian您是对的,但我对发生的事情非常感兴趣,因此对它进行了研究。Python bug tracker上存在一个问题:。吸取了教训,新的API(如asyncio
)使用了两个不同的名称
if shell:
args = ["/bin/sh", "-c"] + args
if env is None:
os.execvp(executable, args)
else:
os.execvpe(executable, args, env)