How can I join elements of an array in Bash?

2019-01-01 07:38发布

If I have an array like this in Bash:

FOO=( a b c )

How do I join the elements with commas? For example, producing a,b,c.

标签: arrays bash
27条回答
长期被迫恋爱
2楼-- · 2019-01-01 08:13

Combine best of all worlds so far with following idea.

# join with separator
join_ws()  { local IFS=; local s="${*/#/$1}"; echo "${s#"$1$1$1"}"; }

This little masterpiece is

  • 100% pure bash ( parameter expansion with IFS temporarily unset, no external calls, no printf ... )
  • compact, complete and flawless ( works with single- and multi-character limiters, works with limiters containing white space, line breaks and other shell special characters, works with empty delimiter )
  • efficient ( no subshell, no array copy )
  • simple and stupid and, to a certain degree, beautiful and instructive as well

Examples:

$ 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
查看更多
初与友歌
3楼-- · 2019-01-01 08:14

My attempt.

$ array=(one two "three four" five)
$ echo "${array[0]}$(printf " SEP %s" "${array[@]:1}")"
one SEP two SEP three four SEP five
查看更多
初与友歌
4楼-- · 2019-01-01 08:15

Using variable indirection to refer directly to an array also works. Named references can also be used, but they only became available in 4.3.

The advantage of using this form of a function is that you can have the separator optional (defaults to the first character of default IFS, which is a space; perhaps make it an empty string if you like), and it avoids expanding values twice (first when passed as parameters, and second as "$@" inside the function).

This solution also doesn't require the user to call the function inside a command substitution - which summons a subshell, to get a joined version of a string assigned to another variable.

As for the disadvantages: you would have to be careful at passing a correct parameter name, and passing __r would give you __r[@]. The behavior of variable indirection to also expand other forms of parameters is also not explicitly documented.

function join_by_ref {
    __=
    local __r=$1[@] __s=${2-' '}
    printf -v __ "%s${__s//\%/%%}" "${!__r}"
    __=${__%${__s}}
}

array=(1 2 3 4)

join_by_ref array
echo "$__" # Prints '1 2 3 4'.

join_by_ref array '%s'
echo "$__" # Prints '1%s2%s3%s4'.

join_by_ref 'invalid*' '%s' # Bash 4.4 shows "invalid*[@]: bad substitution".
echo "$__" # Prints nothing but newline.

This works from 3.1 to 5.0-alpha. As observed, variable indirection doesn't only work with variables, but other parameters as well.

A parameter is an entity that stores values. It can be a name, a number, or one of the special characters listed below under Special Parameters. A variable is a parameter denoted by a name.

Arrays and array elements are also parameters (entities that store value), and references to arrays are technically references to parameters as well. And much like the special parameter @, array[@] also makes a valid reference.

Altered or selective forms of expansion (like substring expansion) that deviate reference from the parameter itself no longer work.

查看更多
公子世无双
5楼-- · 2019-01-01 08:16
s=$(IFS=, eval 'echo "${FOO[*]}"')
查看更多
明月照影归
6楼-- · 2019-01-01 08:17

I would echo the array as a string, then transform the spaces into line feeds, and then use paste to join everything in one line like so:

tr " " "\n" <<< "$FOO" | paste -sd , -

Results:

a,b,c

This seems to be the quickest and cleanest to me !

查看更多
不流泪的眼
7楼-- · 2019-01-01 08:18

Maybe, e.g.,

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

echo "$FOOJOIN"
查看更多
登录 后发表回答