Как я могу объединить элементы массива в Bash? - PullRequest
356 голосов
/ 06 октября 2009

Если у меня есть такой массив в Bash:

FOO=( a b c )

Как мне соединить элементы запятыми? Например, производя a,b,c.

Ответы [ 28 ]

462 голосов
/ 24 июля 2013

Решение по перезаписи Паскаля Пилза как функция в 100% чистой Bash (без внешних команд):

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

В качестве альтернативы, мы можем использовать printf для поддержки многосимвольных разделителей, используя идею @ gniourf_gniourf

function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }

Например,

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
194 голосов
/ 23 февраля 2010

Еще одно решение:

#!/bin/bash
foo=('foo bar' 'foo baz' 'bar baz')
bar=$(printf ",%s" "${foo[@]}")
bar=${bar:1}

echo $bar

Редактировать: то же самое, но для многосимвольного разделителя переменной длины:

#!/bin/bash
separator=")|(" # e.g. constructing regex, pray it does not contain %s
foo=('foo bar' 'foo baz' 'bar baz')
regex="$( printf "${separator}%s" "${foo[@]}" )"
regex="${regex:${#separator}}" # remove leading separator
echo "${regex}"
# Prints: foo bar)|(foo baz)|(bar baz
118 голосов
/ 24 февраля 2012
$ foo=(a "b c" d)
$ bar=$(IFS=, ; echo "${foo[*]}")
$ echo "$bar"
a,b c,d
65 голосов
/ 06 октября 2009

Может быть, например,

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

echo "$FOOJOIN"
24 голосов
/ 14 августа 2014

Удивительно, но мое решение еще не дано :) Это самый простой способ для меня. Для этого не нужна функция:

IFS=, eval 'joined="${foo[*]}"'

Примечание. Было замечено, что это решение хорошо работает в режиме без POSIX. В режиме POSIX элементы все еще правильно соединены, но IFS=, становится постоянным.

22 голосов
/ 15 мая 2014

Вот 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


$

Это сохраняет даже завершающие символы новой строки и не нуждается в подоболочке для получения результата функции. Если вам не нравится 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}"
}
12 голосов
/ 06 июля 2016

Я бы выводил массив в виде строки, затем преобразовывал пробелы в переводы строк, а затем использовал paste, чтобы объединить все в одну строку следующим образом:

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

Результаты:

a,b,c

Это кажется самым быстрым и чистым для меня!

8 голосов
/ 06 июля 2012

Без внешних команд:

$ FOO=( a b c )     # initialize the array
$ BAR=${FOO[@]}     # create a space delimited string from array
$ BAZ=${BAR// /,}   # use parameter expansion to substitute spaces with comma
$ echo $BAZ
a,b,c

Предупреждение: предполагается, что элементы не имеют пробелов.

8 голосов
/ 04 июня 2012

С повторным использованием @ не имеет значения решение, но с одним утверждением, избегая подстановки $ {: 1} и необходимости промежуточной переменной.

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

printf имеет 'Строка формата используется повторно так часто, как это необходимо для удовлетворения аргументов.' на его страницах руководства, так что конкатенации строк задокументированы. Тогда хитрость заключается в том, чтобы использовать длину LIST для нарезки последнего sperator, поскольку cut будет сохранять только длину LIST при подсчете полей.

8 голосов
/ 26 марта 2013
s=$(IFS=, eval 'echo "${FOO[*]}"')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...