subprocess.call()如何使用shell = False?

dkv*_*dkv 10 python linux bash shell subprocess

我正在使用Python的subprocess模块调用一些Linux命令行函数.文档解释了这个shell=True论点

如果是shellTrue,则将通过shell执行指定的命令

有两个例子,从描述性的角度来看对我来说是相同的(即它们都调用了一些命令行命令),但是其中一个使用shell=True而另一个没有

>>> subprocess.call(["ls", "-l"])
0

>>> subprocess.call("exit 1", shell=True)
1
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  • shell=Falsedo做什么运行命令shell=True
  • 我的印象是subprocess.call,check_call并且check_output所有人都必须通过shell执行参数.换句话说,它怎么可能通过shell执行参数?

得到一些例子也很有帮助:

  • 无法完成shell=True可以完成的事情 shell=False以及为什么无法完成.
  • 反之亦然(虽然似乎没有这样的例子)
  • 天下为公这不要紧是否shell=TrueFalse为什么不要紧

Cha*_*ffy 15

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
Run Code Online (Sandbox Code Playgroud)

shell=True你用,你这样做:

pid = fork()
if pid == 0:
  execlp("sh", "sh", "-c", "ls -l", NUL);
else:
  retval = wait(pid)
Run Code Online (Sandbox Code Playgroud)

请注意,shell=False我们执行的命令是ls,而shell=True我们执行的命令是sh.


也就是说:

subprocess.Popen(foo, shell=True)
Run Code Online (Sandbox Code Playgroud)

与以下内容完全相同:

subprocess.Popen(
  ["sh", "-c"] + ([foo] if isinstance(foo, basestring) else foo),
  shell=False)
Run Code Online (Sandbox Code Playgroud)

也就是说,您执行一个副本/bin/sh,并指示该副本/bin/sh将字符串解析为参数列表并执行ls -l自身.


那么,为什么使用shell=True

  • 你正在调用一个内置的shell.

    例如,该exit命令实际上是shell本身的一部分,而不是外部命令.也就是说,这是一组相当小的命令,并且它们很少在shell实例的上下文中有用,该实例仅在单个subprocess.call()调用期间存在.

  • 你有一些shell构造(即重定向)的代码,没有它就很难模拟.

    例如,如果您的命令是cat one two >three,则语法>three重定向:它不是参数cat,而是stdout=open('three', 'w')在运行命令时设置shell 的指令['cat', 'one', 'two'].如果您不想自己处理重定向和管道,则需要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包含调用$(rm -rf ~),那你将度过一个非常糟糕的一天.另一方面,['cat', '--', filename]对所有可能的文件名都是安全的:文件名纯粹是数据,不是由shell或其他任何东西解析为源代码.

    可能的壳编写脚本的安全,但你必须要小心了.考虑以下:

    filenames = ['file1', 'file2'] # these can be user-provided
    subprocess.Popen(['cat -- "$@" | baz', '_'] + filenames, shell=True)
    
    Run Code Online (Sandbox Code Playgroud)

    该代码是安全的(以及-为让用户阅读他们想要的任何文件作为安全永远是),因为它是通过你的文件名外的带外脚本代码-但它是安全的,只是因为该字符串被传递到shell是固定和硬编码的,参数化内容是外部变量(filenames列表).即便如此,它只是在某一点上是"安全的" - 像Shellshock这样的bug 会在shell初始化时触发它会像其他任何东西一样影响它.