如果我在Bash中有一个这样的数组:
FOO=( a b c )
如何用逗号连接元素?例如,生成a b c。
如果我在Bash中有一个这样的数组:
FOO=( a b c )
如何用逗号连接元素?例如,生成a b c。
当前回答
结合所有最好的世界到目前为止与以下的想法。
# join with separator
join_ws() { local IFS=; local s="${*/#/$1}"; echo "${s#"$1$1$1"}"; }
这个小杰作是
100%纯bash(参数扩展,暂时不设置IFS,没有外部调用,没有printf…) 紧凑、完整和完美(适用于单字符和多字符限制符,适用于包含空格、换行符和其他shell特殊字符的限制符,适用于空分隔符) 高效(无子shell,无数组复制) 简单而愚蠢,但在某种程度上,也很漂亮,很有教育意义
例子:
$ join_ws , a b c
a,b,c
$ join_ws '' a b c
abc
$ join_ws $'\n' a b c
a
b
c
$ join_ws ' \/ ' A B C
A \/ B \/ C
其他回答
下面是大多数POSIX兼容shell支持的一个:
join_by() {
# Usage: join_by "||" a b c d
local arg arr=() sep="$1"
shift
for arg in "$@"; do
if [ 0 -lt "${#arr[@]}" ]; then
arr+=("${sep}")
fi
arr+=("${arg}") || break
done
printf "%s" "${arr[@]}"
}
这里有一个单行,有点奇怪,但适用于多字符分隔符,并支持任何值(包括包含空格或任何东西):
ar=(abc "foo bar" 456)
delim=" | "
printf "%s\n$delim\n" "${ar[@]}" | head -n-1 | paste -sd ''
这将在控制台显示为
abc | foo bar | 456
注意:注意一些解决方案是如何使用${ar[*]}和${ar[@]}的printf ?
带@的使用printf特性,通过重复格式模板来支持多个参数。
带*的不应该使用。它们实际上不需要打印,而是依赖于操作字段分隔符和bash的单词展开。这些方法同样适用于echo、cat等——这些解决方案可能使用printf,因为作者并不真正理解他们在做什么……
这与现有的解决方案并没有太大的不同,但它避免了使用单独的函数,不修改父shell中的IFS,并且都在一行中:
arr=(a b c)
printf '%s\n' "$(IFS=,; printf '%s' "${arr[*]}")"
导致
a,b,c
限制:分隔符不能超过一个字符。
可以化简为
(IFS=,; printf '%s' "${arr[*]}")
在这一点上,它基本上与Pascal的答案相同,但使用printf而不是echo,并将结果打印到stdout,而不是将其赋值给变量。
这个简单的单字符分隔符解决方案需要非posix模式。在POSIX模式下,元素仍然正确地连接,但IFS=,赋值变成永久的。
IFS=, eval 'joined="${foo[*]}"'
使用#!bash头在默认情况下以非posix模式执行,但为了帮助确保脚本以非posix模式运行,请在脚本开头添加set +o posix或shop -uo posix。
对于多字符分隔符,我建议使用带有转义和索引技术的printf解决方案。
function join {
local __sep=${2-} __temp
printf -v __temp "${__sep//%/%%}%s" "${@:3}"
printf -v "$1" %s "${__temp:${#__sep}}"
}
join joined ', ' "${foo[@]}"
Or
function join {
printf -v __ "${1//%/%%}%s" "${@:2}"
__=${__:${#1}}
}
join ', ' "${foo[@]}"
joined=$__
这是基于里卡多加利的回答和我的建议。
Printf解决方案,接受任何长度的分隔符(基于@doesn't matters answer)
#/!bin/bash
foo=('foo bar' 'foo baz' 'bar baz')
sep=',' # can be of any length
bar=$(printf "${sep}%s" "${foo[@]}")
bar=${bar:${#sep}}
echo $bar