bash脚本执行命令带有双引号,单引号和空格

时间:2019-12-12 23:55:25

标签: bash shell

我看过有关此问题的类似文章,但是无法弄清楚如何使执行的代码具有正确的格式,该格式必须为foo --bar "a='b'"。我对此的最佳尝试是

#!/bin/bash -x

bar='--bar ''"''a='"'"'b'"'"'"'
cmd=(foo $bar)
echo ${cmd[@]}
eval ${cmd[@]}

此输出对于echo是正确的,但对于eval是错误的

+ bar='--bar "a='\''b'\''"'
+ cmd=(foo $bar)
+ echo foo --bar '"a='\''b'\''"'
foo --bar "a='b'"
+ eval foo --bar '"a='\''b'\''"'
++ foo --bar 'a='\''b'\'''

使用该选项执行命令的正确方法是什么?

2 个答案:

答案 0 :(得分:4)

如果必须存储命令片段,请使用函数或数组,而不要使用字符串。

根据BashFAQ #50的最佳做法代码示例:

#!/usr/bin/env bash
bar=( --bar a="b" )
cmd=(foo "${bar[@]}" )
printf '%q ' "${cmd[@]}" && echo  # print code equivalent to the command we're about to run
"${cmd[@]}"                       # actually run this code

奖金:您的调试输出无法证明您的想法。

"a='b'"'a='\''b'\'''是引用完全相同的字符串的两种不同方式。

要证明这一点:

printf '%s\n' "a='b'" | md5sum -
printf '%s\n' 'a='\''b'\''' | md5sum -

...作为输出发出:

7f183df5823cf51ec42a3d4d913595d7  -
7f183df5823cf51ec42a3d4d913595d7  -

...因此,在代码中解析echo $fooeval $foo的参数之间没有什么不同。

为什么这是真的?因为语法引号不是实际运行的命令的一部分;在外壳程序使用它们确定如何逐个字符解释命令行后,它们将被外壳程序删除。

因此,让我们分解一下set -x向您显示的内容:

'a='\''b'\'''

...由以下串联在一起的文字字符串组成:

  • a=(在单引号上下文中,以单引号引起来并以该引号结尾)
  • '(在无引号的上下文中,由其前面的反斜杠转义)
  • b(在单引号上下文中,以单引号引起来并以该引号结尾)
  • '(在未引用的上下文中)

...其他所有内容都是 syntactic ,对shell有意义,但从未传递给程序foo

答案 1 :(得分:0)

如果要进行与echo ${cmd[@]}中相同的扩展,只需运行以下命令即可:

${cmd[@]}

它将执行:

+ foo --bar '"a='\''b'\''"'

请注意,由于*未加引号,因此将根据filename expansion进行扩展。