【问题标题】:Use command substitution with backticks inside subprocess.call()在 subprocess.call() 中使用带有反引号的命令替换
【发布时间】:2017-01-09 17:03:35
【问题描述】:

只要我安装了适当的程序 - fortune 在 Ubuntu 16.04 中默认不安装 - 我可以使用以下命令让我的计算机的语音到文本功能告诉我重新开始工作,并且然后给我读一封来自fortune的励志名言:

spd-say "get back to work and remember `fortune`"

这里我使用带有反引号的命令替换将调用fortune 命令的结果添加到要读取的文本字符串中。在 Python 中,我可以使用 subprocess 来执行此操作:

subprocess.call(['spd-say', 'get back to work'])

但是,用反引号替换命令似乎在这里不起作用:

subprocess.call(['spd-say', 'get back to work and remember `fortune`'])

在这种情况下,我会听到:“回去工作,记住财富”。我认为这是因为subprocess 传递的是单引号字符串,而这种命令替换只适用于双引号字符串。

我尝试了各种解决方案,包括使用双引号,单引号内的双引号,但我认为它仍然是传递带有双引号的单引号字符串而不是双引号字符串。

我也尝试将整个命令编写为单个字符串,并使用shlex.split() 将其分解:

import shlex
command = 'spd-say "get back to work and remember `fortune`"'
command_args = shlex.split(command)

但这给了我:

['spd-say', 'get back to work and remember `fortune`']

同样,没有双引号。

有没有办法强制将单个参数用双引号括起来(如果这确实是问题),或者以上述方式使用subprocess.call()fortune 命令的输出通过管道传输到spd-say

【问题讨论】:

  • 顺便说一句——我强烈建议您避免使用shlex.split(),而是对您的列表进行硬编码:作为人类,您确切地知道您希望如何解析它们,所以您不妨以这种形式通过它们(另外,这意味着读者只需要知道 one 引用语法——Python 语法——VS 两种,Python 和 shell)。前一种方法很容易通过执行类似shlex.split("your-command --file=%s" % filename) 的操作遇到麻烦,如果文件名包含空格,它可以扩展为多个参数,而['your-command', '--file=%s' % filename],这是明确的。

标签: python python-2.7 subprocess


【解决方案1】:

命令替换的扩展是一个shell操作。没有shell=Truesubprocess 操作中没有涉及到 的 shell。这通常是非常理想的行为:如果您将文件名作为输入传递,您不希望包含 $(rm -rf $HOME) 的文件名(是的,所有这些字符在典型 UNIX 系统上的文件名中都是合法的)导致内容被删除.

但是,如果您确定要进行命令替换,则需要进行 shell 评估:

subprocess.call('spd-say "get back to work and remember $(fortune)"', shell=True)

...相当于下面的,没有 shell=True:

subprocess.call(['sh', '-c', 'spd-say "get back to work and remember $(fortune)"'])

顺便说一句,如果您使用shell=True 传递一个数组,则只有第一个参数会被解析为脚本(因此会执行命令替换和类似的扩展);其他参数是可以由脚本本身解释的文字参数:

subprocess.call(
  ['spd-say "$1 $(fortune)"',                     # this is a script
   "_",                                           # this is a literal for $0 in the script
   "get back to work, don't $(rm), and remember"  # this is a literal for $1 in the script
  ], shell=True)

$(rm) 未在此处执行,因为只有第一个列表元素 ('spd-say "$1 $(fortune)"') 中的内容被视为脚本文本。

【讨论】:

  • 感谢您的回答!我知道与使用 shell=True 相关的安全风险,您在回答中给出了一个很好的例子。但是,我想知道,在一个像上面这样的简单程序中(为了论证,如果我的脚本只做一个 subprocess.call() 调用),是否仍然存在滥用或利用的危险?我唯一能想到的是,如果fortune 命令被替换为恶意内容,那么当$(fortune) 运行时会发生一些不好的事情,但这似乎不太可能......
  • 不包括像 shellshock 这样的情况,在这些情况下,shell 执行会产生其他副作用,主要风险是当您将不受信任/未经审计的内容替换为脚本参数本身时。如果字符串spd-say "get back to work and remember `fortune`" 在您的软件中是硬编码的,那您就很好了;相比之下,如果您连接从用户、文件系统或其他不太受信任的来源获得的东西来生成它,那么您就处于危险之中。
  • 这与 SQL 注入实际上非常相似——如果您通过连接或替换值来生成 SQL,那么您将面临注入攻击的风险;如果唯一的动态值在参数中带外传递,并且您的查询没有对这些值做任何不合理/不安全的事情,那么您可能没问题。在这里,问题是您将值替换为第一个参数 - 脚本本身 - 还是后续参数(不被解释为脚本文本)。
  • 谢谢!都非常有帮助!
猜你喜欢
  • 2018-11-17
  • 1970-01-01
  • 2014-01-21
  • 2015-01-11
  • 2011-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-29
相关资源
最近更新 更多