Bash引用了数组扩展

Ale*_*own 21 arrays bash quotes

当我写一个bash程序时,我通常构造如下调用:

declare -a mycmd=( command.ext "arg1 with space" arg2 thing etc )

"${mycmd[@]}" || echo "Failed: foo"
Run Code Online (Sandbox Code Playgroud)

die foo打印Error foo和退出的bash函数在哪里.

但是,如果我想清楚错误原因,我想打印失败的命令:

"${mycmd[@]}" || echo "Failed: foo: ${mycmd[*]}"
Run Code Online (Sandbox Code Playgroud)

因此用户可以运行dead命令并找出原因.但是,在此过程中引用丢失 - 具有空格或转义字符的失败消息参数不会以可以剪切和粘贴的方式打印.

有没有人建议采用紧凑的方法来解决这个问题?


我认为问题是bash处理命令的参数解析的方式,以及echo(内置)echo处理参数的方式.说明问题的另一种方法是:

如何在以下bash示例中打印带空格的参数的引号(必须作为脚本运行,而不是以立即模式运行):

#!/bin/bash
mkdir qatest; cd qatest
declare -a myargs=(1 2 "3 4")
touch "${myargs[@]}"
ls
echo "${myargs[@]}"
Run Code Online (Sandbox Code Playgroud)

实际结果:

1  2  3 4
1 2 3 4
Run Code Online (Sandbox Code Playgroud)

期望的结果

1  2  3 4
1 2 "3 4"
Run Code Online (Sandbox Code Playgroud)

要么

1  2  3 4
"1" "2" "3 4"
Run Code Online (Sandbox Code Playgroud)

在少数额外的bash代码字符.


问题已经结束:@camh回答得非常好:

更新的脚本:

#!/bin/bash
mkdir qatest; cd qatest
declare -a myargs=(1 2 "3 4")
touch "${myargs[@]}"
ls
echo "${myargs[@]}"
echo $(printf "'%s' " "${myargs[@]}")
Run Code Online (Sandbox Code Playgroud)

输出:

1  2  3 4
1 2 3 4
'1' '2' '3 4'
Run Code Online (Sandbox Code Playgroud)

cam*_*amh 25

你的问题是echo.它获得了正确数量的参数,其中一些参数包含空格,但它的输出在参数和参数内的空格之间失去了区别.

相反,您可以使用printf(1)输出参数并始终包含引号,利用printf的功能,当格式字符串中的格式说明符多于参数时,将格式字符串连续应用于参数:

echo "Failed: foo:" $(printf "'%s' " "${mycmd[@]}")
Run Code Online (Sandbox Code Playgroud)

即使不需要,也会在每个参数周围加上单引号:

Failed: foo: 'command.ext' 'arg1 with space' 'arg2' 'thing' 'etc'
Run Code Online (Sandbox Code Playgroud)

我使用单引号来确保其他shell元字符不会被错误处理.这适用于除单引号本身之外的所有字符 - 即如果您有一个包含单引号的参数,则上述命令的输出将不会正确剪切和粘贴.这可能是你得到的最接近而不会变得混乱.

编辑:大约5年后,自从我回答这个问题以来,bash 4.4已经发布.这有"${var@Q}"扩展引用变量,以便可以通过bash解析它.

这简化了这个答案:

echo "Failed: foo: " "${mycmd[@]@Q}"
Run Code Online (Sandbox Code Playgroud)

这将正确处理参数中的单引号,我的早期版本没有.

  • 对于所有寻找 bash 4.4 功能的文档的人:https://lwn.net/Articles/701009/ 下面对可能的用例的讨论我发现很有洞察力。我要感谢@camh,您足够彻底地跟进了 bash 4.4 功能。如果我此时没有发现这一点,您可能会为我节省大量的重写时间!谢谢! (3认同)

Gor*_*son 14

bash的printf命令有一个%q格式,可以在打印时为字符串添加适当的引号:

echo "Failed: foo:$(printf " %q" "${mycmd[@]}")"
Run Code Online (Sandbox Code Playgroud)

请注意,它引用某些东西的"最佳"方式的想法并不总是与我的相同,例如,它倾向于转义有趣的字符,而不是将字符串包装在引号中.例如:

crlf=$'\r\n'
declare -a mycmd=( command.ext "arg1 with space 'n apostrophe" "arg2 with control${crlf} characters" )
echo "Failed: foo:$(printf " %q" "${mycmd[@]}")"
Run Code Online (Sandbox Code Playgroud)

打印:

Failed: foo: command.ext arg1\ with\ space\ \'n\ apostrophe $'arg2 with control\r\n characters'
Run Code Online (Sandbox Code Playgroud)