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

FOO=( a b c )

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


当前回答

s=$(IFS=, eval 'echo "${FOO[*]}"')

其他回答

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

这些解决方案中的许多(如果不是大多数的话)依赖于晦涩难懂的语法、耗费脑力的正则表达式技巧或对外部可执行文件的调用。我想提出一个简单的、只使用bash的解决方案,它非常容易理解,在性能方面只是稍微次优。

join_by () {
    # Argument #1 is the separator. It can be multi-character.
    # Argument #2, 3, and so on, are the elements to be joined.
    # Usage: join_by ", " "${array[@]}"
    local SEPARATOR="$1"
    shift

    local F=0
    for x in "$@"
    do
        if [[ F -eq 1 ]]
        then
            echo -n "$SEPARATOR"
        else
            F=1
        fi
        echo -n "$x"
    done
    echo
}

例子:

$ a=( 1 "2 2" 3 )
$ join_by ", " "${a[@]}"
1, 2 2, 3
$ 

我想指出,任何使用/usr/bin/[或/usr/bin/printf的解决方案本质上都比我的解决方案慢,因为我使用100%纯bash。作为性能的一个例子,这里有一个演示,我创建了一个包含1,000,000个随机整数的数组,然后用逗号将它们全部连接起来,并计时。

$ eval $(echo -n "a=("; x=0 ; while [[ x -lt 1000000 ]]; do echo -n " $RANDOM" ; x=$((x+1)); done; echo " )")
$ time join_by , ${a[@]} >/dev/null
real    0m8.590s
user    0m8.591s
sys     0m0.000s
$ 

这个特别适用于busybox的sh和$@:

$ FOO=(a b c)
$ printf '%s\n' "${FOO[@]}" | paste -sd,
a,b,c

Or:

join_by() {
    local d=$1
    shift
    printf '%s\n' "$@" | paste -sd "$d"
}
join_by , "${FOO[@]}"  # a,b,c

我相信这是最短的解决方案,正如Benamin W.已经提到的:

(IFS=,; printf %s "${a[*]}")

想要添加,如果你使用zsh,你可以删除子shell:

IFS=, printf %s "${a[*]}"

测试:

a=(1 'a b' 3)
IFS=, printf %s "${a[*]}"
1,a b,3

通过重用@doesn't matters的解决方案,但通过避免${:1}的替换和中间变量的需要,使用了一个语句。

echo $(printf "%s," "${LIST[@]}" | cut -d "," -f 1-${#LIST[@]} )

printf有'格式字符串被重复使用,以满足参数。,这样字符串的连接就被记录下来了。然后诀窍是使用LIST长度来切割最后一个sperator,因为cut将只保留LIST的长度作为字段计数。