subprocess.call()如何与shell = False一起使用? [英] How does subprocess.call() work with shell=False?

查看:441
本文介绍了subprocess.call()如何与shell = False一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Python的 subprocess 模块进行调用一些Linux命令行功能.该文档将shell=True参数解释为

I am using Python's subprocess module to call some Linux command line functions. The documentation explains the shell=True argument as

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

If shell is True, the specified command will be executed through the shell

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

There are two examples, which seem the same to me from a descriptive viewpoint (i.e. both of them call some command-line command), but one of them uses shell=True and the other does not

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

>>> subprocess.call("exit 1", shell=True)
1

我的问题是:

  • shell=True相比,使用shell=False运行命令有什么作用?
  • 给我的印象是subprocess.callcheck_callcheck_output都必须通过外壳执行参数.换句话说,怎么可能通过外壳执行参数?
  • What does running the command with shell=False do, in contrast to shell=True?
  • I was under the impression that subprocess.call and check_call and check_output all must execute the argument through the shell. In other words, how can it possibly not execute the argument through the shell?

获得以下示例也将有所帮助:

It would also be helpful to get some examples of:

  • shell=True可以完成的事情 shell=False以及为何无法完成.
  • 反之亦然(尽管似乎没有这样的例子)
  • shell=TrueFalse无关紧要的东西以及为什么无关紧要
  • Things that can be done with shell=True that can't be done with shell=False and why they can't be done.
  • Vice versa (although it seems that there are no such examples)
  • Things for which it does not matter whether shell=True or False and why it doesn't matter

推荐答案

UNIX程序通过以下三个调用或它们的派生/等效项彼此启动:

UNIX programs start each other with the following three calls, or derivatives/equivalents thereto:

  • fork()-创建自己的新副本.
  • exec()-用其他程序替换自己(如果您是副本,请执行此操作!).
  • wait()-等待另一个进程完成(可选,如果未在后台运行).
  • fork() - Create a new copy of yourself.
  • exec() - Replace yourself with a different program (do this if you're the copy!).
  • wait() - Wait for another process to finish (optional, if not running in background).

因此,使用shell=False可以做到这一点(如下面的Python语法伪代码-如果不是诸如subprocess.call()之类的阻塞调用,则将wait()排除在外):

Thus, with shell=False, you do just that (as Python-syntax pseudocode below -- exclude the wait() if not a blocking invocation such as 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,您可以执行以下操作:

whereas with shell=True, you do this:

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

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

Note that with shell=False, the command we executed was ls, whereas with shell=True, the command we executed was 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本身.

That is to say, you execute a copy of /bin/sh, and direct that copy of /bin/sh to parse the string into an argument list and execute ls -l itself.

那么,为什么使用shell=True?

  • 您正在调用内置的Shell.

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

For instance, the exit command is actually part of the shell itself, rather than an external command. That said, this is a fairly small set of commands, and it's rare for them to be useful in the context of a shell instance that only exists for the duration of a single subprocess.call() invocation.

您有一些带有shell构造(即重定向)的代码,如果没有它们,将很难模拟.

例如,如果您的命令是cat one two >three,则语法>three重定向:它不是cat的参数,而是向shell设置在运行命令['cat', 'one', 'two']时.如果您不想自己处理重定向和管道,则需要一个Shell来完成.

If, for instance, your command is cat one two >three, the syntax >three is a redirection: It's not an argument to cat, but an instruction to the shell to set stdout=open('three', 'w') when running the command ['cat', 'one', 'two']. If you don't want to deal with redirections and pipelines yourself, you need a shell to do it.

一个比较棘手的案例是cat foo bar | baz.要在没有外壳的情况下执行此操作,您需要自己启动管道的两侧:p1 = Popen(['cat', 'foo', 'bar'], stdout=PIPE), p2=Popen(['baz'], stdin=p1.stdout).

A slightly trickier case is cat foo bar | baz. To do that without a shell, you need to start both sides of the pipeline yourself: p1 = Popen(['cat', 'foo', 'bar'], stdout=PIPE), p2=Popen(['baz'], stdin=p1.stdout).

您不会对安全性漏洞一窍不通.

...好吧,这有点太强大了,但幅度不大.使用shell=True是危险的.您无法执行以下操作:没有外壳注入漏洞的Popen('cat -- %s' % (filename,), shell=True):如果曾经用包含$(rm -rf ~)filename调用代码,那将是非常糟糕的一天.另一方面,['cat', '--', filename]对于所有可能的文件名都是安全的:文件名是纯数据,不被shell或其他任何东西解析为源代码.

...okay, that's a little bit too strong, but not by much. Using shell=True is dangerous. You can't do this: Popen('cat -- %s' % (filename,), shell=True) without a shell injection vulnerability: If your code were ever invoked with a filename containing $(rm -rf ~), you'd have a very bad day. On the other hand, ['cat', '--', filename] is safe with all possible filenames: The filename is purely data, not parsed as source code by a shell or anything else.

可以在外壳中编写安全脚本,但您需要注意这一点.请考虑以下内容:

It is possible to write safe scripts in shell, but you need to be careful about it. Consider the following:

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

该代码是安全的(嗯,就像让用户读取他们想要的任何文件一样安全),因为它会将您的文件名从脚本代码中带外传递出去,但是这是安全的,因为传递给外壳的字符串是固定的并经过硬编码,并且参数化的内容是外部变量(filenames列表).即便如此,它仍然是安全"的-像 Shellshock 这样的错误触发外壳初始化的事件对它的影响将与其他任何因素一样大.

That code is safe (well -- as safe as letting a user read any file they want ever is), because it's passing your filenames out-of-band from your script code -- but it's safe only because the string being passed to the shell is fixed and hardcoded, and the parameterized content is external variables (the filenames list). And even then, it's "safe" only to a point -- a bug like Shellshock that triggers on shell initialization would impact it as much as anything else.

这篇关于subprocess.call()如何与shell = False一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆