我正在使用Python的subprocess
模块来调用某些Linux命令行函数。该文档将shell=True
参数解释为
如果 shell 是
True
,则指定的命令将通过shell执行
有两个例子,从描述性的角度来看对我来说是相同的(即他们都调用了一些命令行命令),但其中一个使用shell=True
而另一个不使用
>>> subprocess.call(["ls", "-l"])
0
>>> subprocess.call("exit 1", shell=True)
1
我的问题是:
shell=False
相比,使用shell=True
运行命令的做法是什么? subprocess.call
和check_call
以及check_output
都必须通过shell执行参数。换句话说,它怎么可能不通过shell执行参数?获得一些例子也很有帮助:
shell=True
无法完成的事情
shell=False
以及为什么无法完成这些工作。shell=True
还是False
以及无关紧要的事情都无关紧要答案 0 :(得分:10)
UNIX程序通过以下三个调用或其衍生物/等价物相互启动:
fork()
- 创建自己的新副本。exec()
- 用不同的程序替换自己(如果你是副本,请这样做!)。wait()
- 等待另一个进程完成(可选,如果没有在后台运行)。因此,使用shell=False
,您就是这样做的(如下面的Python语法伪代码 - 如果不是wait()
的阻塞调用,则排除subprocess.call()
:
pid = fork()
if pid == 0: # we're the child process, not the parent
execlp("ls", "ls", "-l", NUL);
else:
retval = wait(pid) # we're the parent; wait for the child to exit & get its exit status
而使用shell=True
,您可以这样做:
pid = fork()
if pid == 0:
execlp("sh", "sh", "-c", "ls -l", NUL);
else:
retval = wait(pid)
请注意,对于shell=False
,我们执行的命令为ls
,而对于shell=True
,我们执行的命令为sh
。
也就是说:
subprocess.Popen(foo, shell=True)
与:
完全相同subprocess.Popen(
["sh", "-c"] + ([foo] if isinstance(foo, basestring) else foo),
shell=False)
也就是说,执行/bin/sh
的副本,并指示/bin/sh
的副本将字符串解析为参数列表并自行执行ls -l
。
那么,为什么会你使用shell=True
?
您正在调用内置的shell。
例如,exit
命令实际上是shell本身的一部分,而不是外部命令。也就是说,这是fairly small set of commands,它们很少在shell实例的上下文中有用,该实例仅在单个subprocess.call()
调用期间存在。
你有一些shell构造(即重定向)的代码,如果没有它就难以模拟。
例如,如果您的命令是cat one two >three
,则语法>three
是重定向:它不是cat
的参数,而是指令运行命令stdout=open('three', 'w')
时设置['cat', 'one', 'two']
的shell。如果您不想自己处理重定向和管道,则需要使用shell来执行此操作。
一个稍微棘手的案例是cat foo bar | baz
。要在没有shell的情况下执行此操作,您需要自己启动管道的两端:p1 = Popen(['cat', 'foo', 'bar'], stdout=PIPE), p2=Popen(['baz'], stdin=p1.stdout)
。
你不会对安全漏洞感到厌恶。
...好吧,那是小位太强,但不是太多。使用shell=True
很危险。你不能这样做:Popen('cat -- %s' % (filename,), shell=True)
没有shell注入漏洞:如果你的代码是用含有filename
的{{1}}调用的,那么你的日子就会非常糟糕。另一方面,$(rm -rf ~)
对所有可能的文件名都是安全的:文件名纯粹是数据,不是由shell或其他任何东西解析为源代码。
可以在shell中编写安全脚本,但是你需要注意它。请考虑以下事项:
['cat', '--', filename]
该代码是安全的(同样安全 - 就像让用户读取他们想要的任何文件那样安全),因为它从你的脚本代码传递你的文件名 - 但是这是安全的,因为传递给shell的字符串是固定的并且是硬编码的,参数化的内容是外部变量(filenames = ['file1', 'file2'] # these can be user-provided
subprocess.Popen(['cat -- "$@" | baz', '_'] + filenames, shell=True)
列表)。即便如此,它只是“安全”到了某一点 - 像Shellshock这样的bug会在shell初始化时触发它会对其产生影响。
答案 1 :(得分:2)
我的印象是subprocess.call和check_call以及check_output都必须通过shell执行参数。
不,子进程完全能够直接启动程序(通过操作系统调用)。它不需要shell
shell = False
无法用shell = True完成的事情
您可以将shell=False
用于任何只使用某些指定参数运行某些可执行文件的命令。
如果您的命令使用shell功能,则必须使用shell=True
。这包括管道,|
或重定向,或包含与;
或&&
或||
等结合的复合语句。
因此,可以使用shell=False
作为grep string file
之类的命令。但是,由于grep string file | xargs something
需要|
,shell=True
之类的命令会出现。
因为shell具有python程序员并不总是直观的强大功能,所以除非你真的需要shell功能,否则使用shell=False
被认为是更好的做法。例如,管道并不是真正需要的,因为它们也可以使用子流程'PIPE功能完成。