有几种方法可以做到这一点:
1。 直接加入printf(通过Charles Duffy’s comment)
printf -v joined '%s,' "${data[@]}"
echo "${joined%,}"
printf 内置隐式连接数组。您可以像下面的 3a 那样交互打印,使用单行阅读 printf '%s,' "${data[@]}",但您会留下一个尾随逗号。 (这种方法甚至可以在 POSIX shell 中使用,尽管您必须使用 $@ 作为您的数组,因为 POSIX 无法处理其他数组类型)。
2。 更改$IFS 字段分隔符(通过chepner’s answer)
join_arr() {
local IFS="$1"
shift
echo "$*"
}
join_arr , "${data[@]}"
这仅在此函数的范围内重新定义字段分隔符,因此当 $data 数组自动扩展时,它使用所需的分隔符而不是全局 $IFS 的第一个值或(如果它为空或未定义)空间。
这可以在没有函数的情况下完成,但是保留$IFS 有一些麻烦:Charles Duffy notes that 在临时重新分配后恢复IFS="$OLD_IFS" 可以评估为IFS="",但如果$IFS 以前未定义,那就不同了来自unset IFS,虽然可以将它们分开,但由于使用local 来限制$IFS 的范围,这种功能性方法要干净得多。
此解决方案仅支持单字符分隔符。有关支持任意长度分隔符的类似功能,请参见下面的 #5。
3a。 循环浏览其内容(并逐步打印)
delim=""
for item in "${data[@]}"; do
printf "%s" "$delim$item"
delim=","
done
echo # add a newline
如果该循环中的其他代码涉及外部调用(甚至是 sleep 0.1),您实际上会观看这个构建,这对交互式设置很有帮助。 p>
3b。 遍历其内容(并构建一个变量)
delim=""
joined=""
for item in "${data[@]}"; do
joined="$joined$delim$item"
delim=","
done
echo "$joined"
4。 将数组保存为字符串并对其运行替换(注意,数组必须缺少空格*)
data_string="${data[*]}"
echo "${data_string//${IFS:0:1}/,}"
* 仅当$IFS 的第一个字符(默认为空格)不 存在于数组的任何项中时才有效。
这使用 bash 模式替换:${parameter//pattern/string} 将用string 替换$parameter 中pattern 的每个实例。在这种情况下,string 是${IFS:0:1},$IFS 的子字符串从开头开始,在一个字符之后结束。
Z Shell (zsh) 可以在一个嵌套参数扩展中做到这一点:
echo "${${data[@]}//${IFS:0:1}/,}"
(尽管 Z Shell 也可以在此答案下方的评论中使用其专用的 join 标志作为 echo "${(j:,:)data}" 作为 noted by @DavidBaynard 更优雅地做到这一点。)
5。 在隐式循环中加入替换(通过Nicholas Sushkin's answer to a duplicate question)
join_by() {
local d="${1-}" f="${2-}"
if shift 2; then
printf %s "$f" "${@/#/$d}"
fi
}
join_by , "${data[@]}"
这与上面的 #2 非常相似(通过 chepner),但它使用模式替换而不是 $IFS,因此支持多字符分隔符。 $d 保存分隔符,$f 保存数组中的第一项(我稍后会说原因)。真正的魔力是${@/#/$d},它将每个数组元素的开头(#)替换为分隔符($d)。由于您不想从分隔符开始,因此它使用shift 不仅可以通过分隔符参数,还可以通过第一个数组元素(另存为$f),然后将其打印在替换之前。
printf 在你给它额外的参数时会有一个奇怪的行为,就像我们在这里做的那样。模板 (%s) 仅指定将有一个参数,因此其余参数就像一个循环一样,并且它们都相互连接。考虑将该关键行更改为printf "%s\n" "$f" "${@/#/$d}"。您最终会在每个元素之后添加一个换行符。如果您在打印连接的数组后想要一个尾随换行符,请使用printf %s "$f" "${@/#/$d}" $'\n'(我们需要使用$'…' 符号来告诉bash 解释转义;另一种方法是插入一个文字换行符,但是那么代码看起来很奇怪)。