Kon*_*lph 4 bash array quoting arguments
我需要将文件名数组传递给命令,并保留正确的引用。到目前为止,一切都很好。不幸的是,该命令实际上是一个子命令,又被另一个命令调用。具体来说,命令是:
\n\ngit filter-branch --index-filter \\\n \'git rm -rf --cached \xe2\x80\xb9file1\xe2\x80\xba \xe2\x80\xb9file2\xe2\x80\xba\xe2\x80\xa6\' \\\n HEAD\n
Run Code Online (Sandbox Code Playgroud)\n\n为简单起见,我\xe2\x80\x99m 将在下面用一个显示相同问题的更简单的命令替换它:
\n\nprintf \'%s\\n\' \'cmd file1 file2\xe2\x80\xa6\'\n
Run Code Online (Sandbox Code Playgroud)\n\n现在我\xe2\x80\x99已经得到了一个数组files=(\'a b\' c)
。我想要的结果是上面的命令在一行中打印,并cmd
根据需要单独引用后面的每个标记(例如,当存在\xe2\x80\x99s 空格时)。
如果我手动扩展并引用文件名,它会起作用:
\n\n$ printf \'%s\\n\' \'cmd \'\\\'\'a b\'\\\'\' c\'\n\xe2\x86\x92 cmd \'a b\' c\n
Run Code Online (Sandbox Code Playgroud)\n\n(或者,我可以混合使用单引号和双引号来达到相同的结果。)
\n\n但如果我尝试传递数组,它就不再起作用:
\n\n$ (set -x; printf \'%s\\n\' "cmd \'${files[@]}\'")\n+ printf \'%s\\n\' \'cmd \'\\\'\'a b\' \'c\'\\\'\'\'\n\xe2\x86\x92 cmd \'a b\nc\'\n
Run Code Online (Sandbox Code Playgroud)$ (set -x; printf \'%s\\n\' \'cmd \'\\\'"${files[@]}"\\\')\n+ printf \'%s\\n\' \'cmd \'\\\'\'a b\' \'c\'\\\'\'\'\n\xe2\x86\x92 cmd \'a b\nc\'\n
Run Code Online (Sandbox Code Playgroud)$ (set -x; printf \'%s\\n\' \'cmd \'"${files[@]}")\n+ printf \'%s\\n\' \'cmd a b\' c\n\xe2\x86\x92 cmd a b\nc\n
Run Code Online (Sandbox Code Playgroud)我\xe2\x80\x99m并不感到惊讶(3)不\xe2\x80\x99工作(并且它\xe2\x80\x99s只是为了完整性而包含在内)。根据 的输出set -x
,shell 正确地引用了 (1) 和 (2) 中的各个数组元素,甚至在整个内容周围添加了转义引号。但随后它会分解单独引用的项目。有办法防止这种情况吗?
顺便说一下,Shellcheck (SC2145) 建议将上述[@]
部分替换为[*]
上述部分。这显然会破坏带有空格的文件名。
使用数组代替数组set -- file1 file2 ...
来填充参数列表,然后使用uote运算符进行bash
参数转换:Q
set -- 'a "b' c "d 'e" "f 'g "'"h' ; (set -x; printf 'cmd %s\n' "${*@Q}")
Run Code Online (Sandbox Code Playgroud)
输出:
+ printf 'cmd %s\n' ''\''a "b'\'' '\''c'\'' '\''d '\''\'\'''\''e'\'' '\''f '\''\'\'''\''g "h'\'''
cmd 'a "b' 'c' 'd '\''e' 'f '\''g "h'
Run Code Online (Sandbox Code Playgroud)
或者,如果我们删除该set -x;
部分,输出将变为:
cmd 'a "b' 'c' 'd '\''e' 'f '\''g "h'
Run Code Online (Sandbox Code Playgroud)
LL3的评论提出了一种不需要的更好方法
set -- ...
:
export x; n=(a "b 'c"); x="${n[@]@Q}"
( n=($x); printf 'cmd %s\n' "${n[*]}"; )
Run Code Online (Sandbox Code Playgroud)
简单版本:
set -- 'a "b' c "d 'e" "f 'g "'"h' ; (set -x; printf 'cmd %s\n' "${*@Q}")
Run Code Online (Sandbox Code Playgroud)
输出:
cmd 'a' 'b '\''c'
Run Code Online (Sandbox Code Playgroud)
另一种方法是使用带有赋值运算符的bash
参数转换(也需要):A
eval
export x;n=(a b 'c d');x="${n[@]@A}"; (eval "$x";printf '%s\n' "${n[@]}")
Run Code Online (Sandbox Code Playgroud)
输出显示所printf
看到的内容:
a
b
c d
Run Code Online (Sandbox Code Playgroud)