如何强制声明 -f 尊重分号

如何强制声明 -f 尊重分号

鉴于函数

hello () { echo "one"; echo "two"; }

在我正在使用的脚本中cmd="$(declare -f hello); hello",我尝试使用执行命令bash -c $cmd,但是由于 declared -f 删除;声明中的最后一个行为而失败,有没有办法防止这种情况发生?

细节

对于hello () { echo "one"; echo "two"; } declare -f 结果

hello () {
    echo "one";
    echo "two"
}

代替:

hello () {
    echo "one";
    echo "two";
}

答案1

$cmd它只会失败,因为在扩展变量时没有正确引用它。

declare -p“尊重”分号——它会在它们所在的位置输出它们必要的重建与解析的原始源代码语义相同的源代码。

但是行尾不需要分号;并且的输出declare -p每个命令都在单独的行中,因此它也可以根本没有分号并且功能上保持相同。

(一旦函数被解析,它的原始来源就会丢失——内存中的表示没有分号也没有换行符,所有这些都由“declare”重建。)

你需要做的是保留这些换行符通过在扩展名周围使用双引号"$cmd"

$ cmd="$(declare -f hello); hello"
$ bash -c "$cmd"
one
two

如果没有引号,变量的内容将在空格处分割:

$ echo $cmd
hello () { echo one; echo two }; hello

答案2

由于变量周围缺少双引号,bash -c $cmd导致该命令相当于:

bash -c hello '()' …

其中是hello命令字符串,()你无意中给这个确切的名字bash表示来自 unquoted 扩展的其余参数$cmd。换句话说, 的内容$cmd被拆分为多个参数。

重要的是,该命令与bash -c 'hello () …'您希望的非常不同,其中引号中的整个字符串是命令字符串。

你新近被叫来bash试图执行hello 仅此而已,而对于它$0是,并且()所有剩余的参数({,,,,以及来自未引用的扩展)成为位置参数(,,...)shell无论如何都不会使用它们,它们echo"one";echo"two"}$cmd$1$2完全不相关

错误是:

(): line 1: hello: command not found

(很遗憾你没有在问题中透露这一点)。你可以看到 shell 确实命名了自己()。你可以从

bash -c hello '()' arbitrary garbage which is also irrelevant

如果您设法强制declare -f将分号放在您想要的位置,则不会有任何区别,因为分号属于不相关的参数;shell 代码仍然是唯一的,hello并且错误仍然是相同的。

一个解决方案是使用双引号括住变量:

bash -c "$cmd"

它是你应该(几乎)总是做的事情

上述命令相当于bash -c 'hello () …'您想要解释为 shell 代码的所有内容都是命令字符串。


笔记另一个答案(当我写这篇文章的时候修订 1) 也建议使用双引号,但理由是错误的。该答案专注于保留换行符,您可以通过不在 中包含换行符来实现这一点IFS。处理换行符是不够的,请尝试以下代码:

hello () { echo "one"; echo "two"; }
cmd="$(declare -f hello); hello"
IFS=' '
bash -c $cmd

代码保留换行符,但它仍然$cmd在空格上分割,因此命令字符串再次是唯一的hello,并且错误是相同的。

是的,我们确实想保留换行符,但是首先我们希望将整个扩展$cmd为一个单一的论点,命令字符串。双引号是执行此操作的正确方法,它还可以保留换行符。我的观点是:如果的内容在和$cmd之间被拆分,那么分号(或缺少分号)或换行符(或缺少换行符)无论如何都是无关紧要的。拆分是问题所在。另一个答案解释并解决了一个甚至没有发生的问题(拆分破坏了你的代码hello()较早)。除非我们先解决真正的问题,否则解决这个问题毫无意义。另一个答案中的解决方案“碰巧”也解决了真正的问题,事实上它是解决真正问题的正确方法。这使得答案“有效”并且看起来正确,但它不是一个好答案。

它是有点就像这样:想象一下,你试图用锤子敲一颗螺丝,但你一直敲打手指。你认为如果你不敲打手指,你最终会把螺丝钉敲进去。你认为头更大的螺丝会有帮助(因为更容易敲打头)。然后有人告诉你用螺丝刀代替锤子,理由是使用螺丝刀时不要撞到手指。这个解决方案非常有效:你不需要更换螺丝,你不再需要敲击手指,你可以轻松地拧螺丝。但使用螺丝刀的真正原因与敲击手指的能力无关:螺丝刀适合螺丝,而锤子不适合。

相关内容