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

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

FOO=( a b c )

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

Ответы [ 28 ]

1 голос
/ 15 июля 2018

Использование переменной косвенности для прямой ссылки на массив также работает. Именованные ссылки также могут быть использованы, но они стали доступны только в 4.3.

Преимущество использования этой формы функции заключается в том, что разделитель может быть необязательным (по умолчанию используется первый символ по умолчанию IFS, который является пробелом; возможно, при желании можно сделать его пустой строкой) избегает двойного расширения значений (сначала при передаче в качестве параметров, а затем как "$@" внутри функции).

Это решение также не требует, чтобы пользователь вызывал функцию внутри подстановки команд, которая вызывает подоболочку, чтобы получить объединенную версию строки, назначенной другой переменной.

Что касается недостатков: вам нужно быть осторожным при передаче правильного имени параметра, а передача __r даст вам __r[@]. Поведение переменной косвенности для расширения других форм параметров также явно не задокументировано.

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.

Работает с 3,1 до 5,0-альфа. Как уже отмечалось, переменная косвенность работает не только с переменными, но и с другими параметрами.

Параметр - это объект, который хранит значения. Это может быть имя, число или один из специальных символов, перечисленных ниже в разделе «Специальные» Параметры. Переменная - это параметр, обозначаемый именем.

Массивы и элементы массива также являются параметрами (сущностями, которые хранят значения), и ссылки на массивы также технически являются ссылками на параметры. И так же, как специальный параметр @, array[@] также делает допустимую ссылку.

Измененные или выборочные формы расширения (например, расширение подстроки), которые отклоняются от самого параметра, больше не работают.

1 голос
/ 22 марта 2018

Спасибо @gniourf_gniourf за подробные комментарии о моей комбинации лучших миров на данный момент. Извините за публикацию кода, который не был тщательно разработан и протестирован. Здесь лучше попробовать.

# join with separator
join_ws() { local d=$1 s=$2; shift 2 && printf %s "$s${@/#/$d}"; }

Эта красота по замыслу

  • (все еще) 100% чистый bash (спасибо, что явно указали, что printf также является встроенным. Я не знал об этом раньше ...)
  • работает с многосимвольными разделителями
  • более компактный и более полный и на этот раз тщательно продуманный и длительный стресс-тест со случайными подстроками из сценариев оболочки, среди прочего, охватывающий использование специальных символов оболочки или управляющих символов или отсутствие символов в разделителе и / или параметрах и крайние случаи, и угловые случаи и другие придирки как никакие аргументы вообще. Это не гарантирует, что ошибок больше нет, но найти их будет немного сложнее. Кстати, даже самые популярные в настоящее время ответы и связанные с ними вопросы страдают от таких вещей, как -e ошибка ...

Дополнительные примеры:

$ join_ws '' a b c
abc
$ join_ws ':' {1,7}{A..C}
1A:1B:1C:7A:7B:7C
$ join_ws -e -e
-e
$ join_ws $'\033[F' $'\n\n\n'  1.  2.  3.  $'\n\n\n\n'
3.
2.
1.
$ join_ws $ 
$
0 голосов
/ 12 февраля 2019

x=${"${arr[*]}"// /,}

Это самый короткий способ сделать это.

Пример

arr=(1 2 3 4 5)
x=${"${arr[*]}"// /,}
echo $x  # output: 1,2,3,4,5
0 голосов
/ 07 октября 2009

Этот подход заботится о пробелах в пределах значений, но требует цикла:

#!/bin/bash

FOO=( a b c )
BAR=""

for index in ${!FOO[*]}
do
    BAR="$BAR,${FOO[$index]}"
done
echo ${BAR:1}
0 голосов
/ 26 января 2012
liststr=""
for item in list
do
    liststr=$item,$liststr
done
LEN=`expr length $liststr`
LEN=`expr $LEN - 1`
liststr=${liststr:0:$LEN}

Это также касается дополнительной запятой в конце. Я не эксперт по Bash. Просто мой 2с, так как это более элементарно и понятно

0 голосов
/ 09 февраля 2017

Если вы строите массив в цикле, вот простой способ:

arr=()
for x in $(some_cmd); do
   arr+=($x,)
done
arr[-1]=${arr[-1]%,}
echo ${arr[*]}
0 голосов
/ 22 февраля 2014
awk -v sep=. 'BEGIN{ORS=OFS="";for(i=1;i<ARGC;i++){print ARGV[i],ARGC-i-1?sep:""}}' "${arr[@]}"

или

$ a=(1 "a b" 3)
$ b=$(IFS=, ; echo "${a[*]}")
$ echo $b
1,a b,3
0 голосов
/ 14 августа 2014

Возможно, я упускаю что-то очевидное, так как я новичок во всем, что касается bash / zsh, но мне кажется, что вам вообще не нужно использовать printf. Да и без этого не обидно.

join() {
  separator=$1
  arr=$*
  arr=${arr:2} # throw away separator and following space
  arr=${arr// /$separator}
}

По крайней мере, до сих пор это работало без проблем.

Например, join \| *.sh, который, скажем, я нахожусь в моем каталоге ~, выводит utilities.sh|play.sh|foobar.sh. Достаточно хорошо для меня.

РЕДАКТИРОВАТЬ: Это в основном ответ Нила Гейсвайлера , но обобщенный в функцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...