运行存储在变量中的shell命令



我正在尝试在容器内运行一个带有重定向的命令,该重定向应该在容器内工作,该命令应该存储在一个变量中。这是我在容器中运行的命令

consul kv export > /consul/data/backup.json

这是我从docker主机上尝试的命令

docker exec f0a57e0592e5 consul kv export > /consul/data/backup.json

这不起作用,因为重定向发生在主机上,而不是在容器内

为了工作,我不得不使用shell在容器内执行重定向

docker exec f0a57e0592e5 sh -c 'consul kv export > /consul/data/backup.json'

在这里工作之前,我需要的是能够将所有这些命令作为变量传递

command="docker exec f0a57e0592e5 sh -c 'consul kv export > /consul/data/backup.json'"

但当执行$command时,我收到kv: 1: Syntax error: Unterminated quoted string

为什么

引用bash手动

在将拆分为标记后,在命令行上执行扩展。执行的扩展有七种:

  • 支架展开
  • 波浪形展开
  • 参数与变量展开
  • 命令替换
  • 算术展开
  • 分词
  • 文件名扩展

所以键入这个

command="docker exec f0a57e0592e5 sh -c 'consul kv export > /consul/data/backup.json'"

做你想做的事。双引号可防止随地吐痰。所以它只是command=something,没有其他令牌。到目前为止还不错。

现在,当你输入

$command

$command扩展为

docker exec f0a57e0592e5 sh -c 'consul kv export > /consul/data/backup.json'

这条生产线是用膨胀链的剩余部分处理的。但不是重新启动整个评估链。

因此,在变量替换之后是命令替换($(...)`...`(。这些都没有。

然后对CCD_ 11。也没有。

然后是单词纵切。因此,我们吐出单词,即dockerexecf0a57e0592e5sh-c'consulkvexport>/consul/data/backup.json'这里的引号不被解释为防止拆分。现在找引号已经太迟了。这发生在";代币";第一阶段。这是在引用之前完成的。所以这些引号在这里只是字符。

所以CCD_ 22是用所述自变量来执行的。包括一个'consul自变量。这不是你所期望的。

实验

如果你有

command="printf (%s)"

然后

$command 1 2 3

给出(1)(2)(3)

$command "1 2 3"

给出(1 2 3)

我想,不出所料。因为"在第一阶段就存在,令牌分裂,并被解释为防止后来的单词分裂。

但试试这个变体

command="printf (%s) 1 2"
$command 3 4 5

还是没什么奇怪的→(1)(2)(3)(4)(5)

command="printf (%s) '1 2'"
$command '3 4 5'

('1)(2')(3 4 5)

这可能很奇怪。但这很正常。看看会发生什么:

  • $command '3 4 5'标记$command(变量引用(3 4 5(字符串——通过标记化发现其中的空格是文字空间,因为被引号包围。我用表示(
  • 没有大括号,没有可展开的波浪号
  • 我们替换变量,所以现在要评估的行是printf (%s) '1 2' 3 4 5
  • $(...)$((...)可扩展
  • 分为单词:printf(%s)'12'3 4 5。请注意,在此阶段的'只是字符。对他们来说,影响标记化已经太晚了,就像3 4 5周围的人所做的那样。因此,12之间的空间确实分割了单词,与345之间的空间相反,由于'的存在,这些空间通过标记化转换为文字空间
  • exec
  • ('1)(2')(3 4 5)

正是我们得到的。当我们了解我们的命令到底发生了什么时,即使是最奇怪的结果也是完全可以预测的。

解决方案

你可以使用臭名昭著的evalcommand="eval docker exec f0a57e0592e5 sh -c 'consul kv export > /consul/data/backup.json'"

这迫使扩展链从头开始,包括标记化。

但是,和往常一样,必须小心处理eval,尤其是在其中包含用户输入部分的情况下。这里的情况似乎并非如此。因为如果链中的任何东西扩展到; rm -fr /,你就知道会发生什么。。。

或者您可以尝试创建一个脚本/path/bin/myscript

#!/bin/bash
docker exec f0a57e0592e5 sh -c 'consul kv export > /consul/data/backup.json'

然后有command=/path/bin/myscript

所以$command只是执行该脚本(这是受控的eval。因为启动脚本是强制评估其内容(。

或者,根据上下文,与函数或别名相同。

最新更新