如果我在Bash中有一个这样的数组:

FOO=( a b c )

如何用逗号连接元素?例如,生成a b c。


当前回答

这里有一个单行,有点奇怪,但适用于多字符分隔符,并支持任何值(包括包含空格或任何东西):

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,因为作者并不真正理解他们在做什么……

其他回答

x = $ {arr [*] / /,)

这是做这件事最短的方法。

的例子,

# ZSH:
arr=(1 "2 3" 4 5)
x=${"${arr[*]}"// /,}
echo $x  # output: 1,2,3,4,5

# ZSH/BASH:
arr=(1 "2 3" 4 5)
a=${arr[*]}
x=${a// /,}
echo $x  # output: 1,2,3,4,5

支持多字符分隔符的100%纯Bash函数是:

function join_by {
  local d=${1-} f=${2-}
  if shift 2; then
    printf %s "$f" "${@/#/$d}"
  fi
}

例如,

join_by , a b c #a,b,c
join_by ' , ' a b c #a , b , c
join_by ')|(' a b c #a)|(b)|(c
join_by ' %s ' a b c #a %s b %s c
join_by $'\n' a b c #a<newline>b<newline>c
join_by - a b c #a-b-c
join_by '\' a b c #a\b\c
join_by '-n' '-e' '-E' '-n' #-e-n-E-n-n
join_by , #
join_by , a #a

上面的代码基于@gniourf_gniourf、@AdamKatz、@MattCowell和@x-yuri的想法。它使用选项errexit (set -e)和nounset (set -u)。

或者,一个更简单的只支持单个字符分隔符的函数是:

function join_by { local IFS="$1"; shift; echo "$*"; }

例如,

join_by , a "b c" d #a,b c,d
join_by / var local tmp #var/local/tmp
join_by , "${FOO[@]}" #a,b,c

这个解决方案是基于Pascal Pilz最初的建议。

前面提出的解决方案的详细说明可以在“如何在bash脚本中连接()数组元素”中找到,这是meleu在dev.to上发表的一篇文章。

这里有一个单行,有点奇怪,但适用于多字符分隔符,并支持任何值(包括包含空格或任何东西):

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,因为作者并不真正理解他们在做什么……

下面是一个100%纯Bash函数,它可以完成这项工作:

join() {
    # $1 is return variable name
    # $2 is sep
    # $3... are the elements to join
    local retname=$1 sep=$2 ret=$3
    shift 3 || shift $(($#))
    printf -v "$retname" "%s" "$ret${@/#/$sep}"
}

看:

$ a=( one two "three three" four five )
$ join joineda " and " "${a[@]}"
$ echo "$joineda"
one and two and three three and four and five
$ join joinedb randomsep "only one element"
$ echo "$joinedb"
only one element
$ join joinedc randomsep
$ echo "$joinedc"

$ a=( $' stuff with\nnewlines\n' $'and trailing newlines\n\n' )
$ join joineda $'a sep with\nnewlines\n' "${a[@]}"
$ echo "$joineda"
 stuff with
newlines
a sep with
newlines
and trailing newlines


$

这甚至保留了末尾的换行符,并且不需要一个子shell来获得函数的结果。如果你不喜欢printf -v(为什么你不喜欢它?)并传递一个变量名,你当然可以为返回的字符串使用一个全局变量:

join() {
    # $1 is sep
    # $2... are the elements to join
    # return is in global variable join_ret
    local sep=$1 IFS=
    join_ret=$2
    shift 2 || shift $(($#))
    join_ret+="${*/#/$sep}"
}

也许,例如,

SAVE_IFS="$IFS"
IFS=","
FOOJOIN="${FOO[*]}"
IFS="$SAVE_IFS"

echo "$FOOJOIN"