Arrays Bash引用的数组扩展
当我编写bash程序时,我通常构造如下调用:Arrays Bash引用的数组扩展,arrays,bash,quotes,Arrays,Bash,Quotes,当我编写bash程序时,我通常构造如下调用: declare -a mycmd=( command.ext "arg1 with space" arg2 thing etc ) "${mycmd[@]}" || echo "Failed: foo" 其中,diefoo是一个bash函数,它打印Error foo并退出 但如果我想弄清楚错误原因,我想打印失败的命令: "${mycmd[@]}" || echo "Failed: foo: ${mycmd[*]}" 因此,用户可以运行dead命
declare -a mycmd=( command.ext "arg1 with space" arg2 thing etc )
"${mycmd[@]}" || echo "Failed: foo"
其中,diefoo
是一个bash函数,它打印Error foo
并退出
但如果我想弄清楚错误原因,我想打印失败的命令:
"${mycmd[@]}" || echo "Failed: foo: ${mycmd[*]}"
因此,用户可以运行dead命令并找出原因。但是,引用在此过程中丢失-包含空格或转义字符的失败消息参数不会以剪切粘贴和运行的方式打印
有人对解决这个问题的紧凑方法有什么建议吗
我认为问题在于bash处理命令参数解析的方式,以及(内置)echo处理参数的方式。说明问题的另一种方式是: 在下面的bash示例中(必须作为脚本运行,而不是以立即模式运行),如何打印带有空格的参数周围的引号: 实际结果:
1 2 3 4
1 2 3 4
期望结果
1 2 3 4
1 2 "3 4"
或
在几个额外的bash代码字符中
问题结束:@camh出色地回答了这个问题: 更新脚本:
#!/bin/bash
mkdir qatest; cd qatest
declare -a myargs=(1 2 "3 4")
touch "${myargs[@]}"
ls
echo "${myargs[@]}"
echo $(printf "'%s' " "${myargs[@]}")
输出:
1 2 3 4
1 2 3 4
'1' '2' '3 4'
如何
声明-p quotedarray
--编辑--
实际上,declare-pquotedArray
将很好地满足您的需求。如果您坚持结果的输出格式,那么我有一个小技巧可以完成这项工作,但只针对索引数组,而不是关联数组
declare -a quotedarray=(1 2 "3 4")
temparray=( "${quotedarray[@]/#/\"}" ) #the outside double quotes are critical
echo ${temparray[@]/%/\"}
您的问题在于
echo
。它获得了正确数量的参数,有些参数包含空格,但它的输出失去了参数之间的空格和参数内的空格的区别
相反,您可以使用printf(1)
输出参数并始终包含引号,利用printf的功能,当格式字符串中的参数多于格式说明符时,将格式字符串依次应用于参数:
echo "Failed: foo:" $(printf "'%s' " "${mycmd[@]}")
这将在每个参数周围加上单引号,即使不需要:
Failed: foo: 'command.ext' 'arg1 with space' 'arg2' 'thing' 'etc'
我使用了单引号来确保其他shell元字符没有被错误处理。这将适用于除单引号本身之外的所有字符-即,如果您的参数包含单引号,则上述命令的输出将无法正确剪切和粘贴。这很可能是最接近你会得到没有变得混乱
编辑:差不多5年后,自从我回答这个问题以来,Bash4.4已经发布了。这有”${var@Q}“
expansion,它引用变量,以便bash可以解析回该变量
这将此答案简化为:
echo "Failed: foo: " "${mycmd[@]@Q}"
这将正确处理参数中的单引号,而我的早期版本没有这样做。bash的printf命令具有%q格式,可以在打印字符串时将适当的引号添加到字符串中:
echo "Failed: foo:$(printf " %q" "${mycmd[@]}")"
请注意,它对引用某些内容的“最佳”方式的想法并不总是与我的想法相同,例如,它倾向于转义有趣的字符,而不是将字符串括在引号中。例如:
crlf=$'\r\n'
declare -a mycmd=( command.ext "arg1 with space 'n apostrophe" "arg2 with control${crlf} characters" )
echo "Failed: foo:$(printf " %q" "${mycmd[@]}")"
印刷品:
Failed: foo: command.ext arg1\ with\ space\ \'n\ apostrophe $'arg2 with control\r\n characters'
繁琐的方法(只引用包含空格的参数):
输出:
1 2 "3 4"
顺便说一下,关于此过程中的
报价丢失
,请注意,报价从未保存<代码>“是一个特殊字符,它告诉shell将其中的任何内容视为单个字段/参数(即,不拆分它)。另一方面,文字引号(像这样键入)被保留。我喜欢将代码放在函数中,以便更容易重用和记录:
function myjoin
{
local list=("${@}")
echo $(printf "'%s', " "${list[@]}")
}
declare -a colorlist=()
colorlist+=('blue')
colorlist+=('red')
colorlist+=('orange')
echo "[$(myjoin ${colorlist[@]})]"
请注意,我是如何在解决方案中添加逗号的,因为我正在使用bash脚本生成代码
[编辑:修复EM0指出它正在返回['blue',]的问题]另一种方法
$bash--版本
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
产生:
declare-aquotedaray='([0]=“1”[1]=“2”[2]=“3”[3]=“4”)
。对我来说似乎没有帮助。@AlexBrown你确定你的结果吗?请再试一次。如果你这样定义数组,就不可能得到你的结果虽然您提出了一个有趣的观点,但我的示例似乎有点错误。@AlexBrown如果您得到的结果是声明-a quotedarray='([0]=“1”[1]=“2”[2]=“3”[3]=“4”)
,那么quotedarray必须定义为(1 2 3 4)
包含四个元素。请仔细检查数组,或取消设置数组,然后重试。感谢您指出问题所在,我需要一个bash脚本来显示问题,因此我修改了示例。这将返回['blue',]
对我来说。尽管大家都在寻找bash 4.4功能的文档:下面讨论了我发现很有见解的可能的用例。我要感谢@camh you,感谢@camh you对bash 4.4功能进行了足够深入的跟进。如果我现在没有发现这一点,你可能为我节省了大量的重写时间!Thanks!您应该更喜欢或使用Bash 4.4的“${mycmd[@]@Q}”
扩展而不是printf''%s'
。像这样简单的引用在很多情况下都会失败,例如myargs=(12“3'4”)
。
1 2 "3 4"
function myjoin
{
local list=("${@}")
echo $(printf "'%s', " "${list[@]}")
}
declare -a colorlist=()
colorlist+=('blue')
colorlist+=('red')
colorlist+=('orange')
echo "[$(myjoin ${colorlist[@]})]"
# echo_array.sh
contains_space(){ [[ "$1" =~ " " ]]; return $?; }
maybe_quote_one(){ contains_space "$1" && echo \'"$1"\' || echo "$1"; }
maybe_quote(){ while test "$1"; do maybe_quote_one "$1"; shift; done; }
arridx(){ echo '${'$1'['$2']}'; }
arrindir(){ echo $(eval echo `arridx $1 $2`); }
arrsize(){ echo `eval echo '${'#$1'[@]}'`; }
echo_array()
{
echo -n "$1=( "
local i=0
for (( i=0; i < `arrsize a`; i++ )); do
echo -n $(maybe_quote "$(arrindir $1 $i)") ''
done
echo ")"
}
source echo_array.sh
a=( foo bar baz "It was hard" curious '67 - 00' 44 'my index is 7th' )
echo_array a
# a=( foo bar baz 'It was hard' curious '67 - 00' 44 'my index is 7th' )
arrindir a 7
# my index is 7th
arrindir a 0
# foo
arrsize a
# 8
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.