Как я могу объединить элементы массива в Bash?

418

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

FOO=( a b c )

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

Дэвид Волевер
источник

Ответы:

572

Решение по перезаписи Паскаля Пилза как функции в 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
Николай Сушкин
источник
9
Используйте это для разделителей с несколькими символами: function join {perl -e '$ s = shift @ARGV; print join ($ s, @ARGV); ' "$ @"; } join ',' abc # a, b, c
Даниэль Патру
4
@dpatru в любом случае, чтобы сделать этот чистый удар?
CMCDragonkai
4
@puchu Что не работает, так это многосимвольные разделители. Сказать «пространство не работает» звучит так, как будто соединение с пространством не работает. Оно делает.
Эрик
6
Это способствует порождению подоболочек при сохранении вывода в переменную. Используйте konsoleboxстиль :) function join { local IFS=$1; __="${*:2}"; }или function join { IFS=$1 eval '__="${*:2}"'; }. Тогда используйте __после. Да, я пропагандирую использование __в качестве переменной результата;) (и общей итерационной переменной или временной переменной). Если концепция попала на популярный вики-сайт Bash, меня скопировали :)
konsolebox
6
Не помещайте расширение $dв спецификатор формата printf. Вы думаете, что в безопасности, так как «избежали», %но есть и другие предостережения: когда разделитель содержит обратную косую черту (например, \n) или когда разделитель начинается с дефиса (и, возможно, других, о которых я не могу думать сейчас). Конечно, вы можете исправить это (заменить обратную косую черту на двойную обратную косую черту и использовать printf -- "$d%s"), но в какой-то момент вы почувствуете, что сражаетесь с оболочкой, а не работаете с ней. Вот почему в своем ответе ниже я добавил разделитель к условиям, которые необходимо объединить.
gniourf_gniourf
206

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

#!/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
не имеет значения
источник
7
+1. Что о printf -v bar ",%s" "${foo[@]}". Это на один forkменьше (на самом деле clone). Он даже разветвление чтения файла: printf -v bar ",%s" $(<infile).
TrueY
14
Вместо того, чтобы молиться $separator, не содержит %sили тому подобное, вы можете сделать свой printfкрепкий printf "%s%s" "$separator" "${foo[@]}".
Musiphil
5
@musiphil Неправильно. От bash man: «Формат используется повторно по мере необходимости для использования всех аргументов. Использование двух заполнителей формата, таких как in, printf "%s%s"будет использовать разделитель в первом наборе ТОЛЬКО набора выходных данных, а затем просто
объединит
3
@AndrDevEK: Спасибо за ошибку. Вместо этого я бы предложил что-то вроде printf "%s" "${foo[@]/#/$separator}".
Musiphil
2
@musiphil, спасибо. Да! Тогда printf становится избыточным, и эта строка может быть уменьшена до IFS=; regex="${foo[*]/#/$separator}". На этом этапе это, по сути, становится ответом gniourf_gniourf, который IMO чище с самого начала, то есть использование функции для ограничения объема изменений IFS и временных переменных.
AnyDev
145
$ foo=(a "b c" d)
$ bar=$(IFS=, ; echo "${foo[*]}")
$ echo "$bar"
a,b c,d
Паскаль Пильц
источник
3
Внешние двойные кавычки и двойные кавычки вокруг двоеточия не нужны. Необходимы только внутренние двойные кавычки:bar=$( IFS=, ; echo "${foo[*]}" )
Ceving
8
+1 для наиболее компактного решения, которое не нуждается в циклах, которое не требует внешних команд и которое не накладывает дополнительных ограничений на набор символов аргументов.
выступление
22
Мне нравится решение, но оно работает, только если IFS - один персонаж
Jayen
8
Любая идея, почему это не работает, если использовать @вместо *, как в $(IFS=, ; echo "${foo[@]}")? Я вижу, что *уже сохраняет пробел в элементах, опять же не знаю, как, так @как обычно требуется для этого.
haridsv
10
Я нашел ответ на свой вопрос выше. Ответ заключается в том, что IFS признается только за *. На странице руководства bash найдите «Специальные параметры» и найдите объяснение рядом с *:
haridsv
66

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

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

echo "$FOOJOIN"
Мартин Клейтон
источник
3
Если вы делаете это, он думает, что IFS- это переменная. Вы должны сделать echo "-${IFS}-"(фигурные скобки отделяют черточки от имени переменной).
Приостановлено до дальнейшего уведомления.
1
Тем не менее получил тот же результат (я просто вставил тире, чтобы проиллюстрировать суть ... echo $IFSделает то же самое.
David Wolever
41
Тем не менее, это все еще работает ... Так что, как и большинство вещей с Bash, я притворюсь, что понимаю это, и продолжаю свою жизнь.
Дэвид Волевер
2
«-» не является допустимым символом для имени переменной, поэтому оболочка правильно работает, когда вы используете $ IFS-, вам не нужны $ {IFS} - (bash, ksh, sh и zsh в linux и solaris тоже согласен).
Idelic
2
@ Разница между вашим эхом и Деннисом в том, что он использовал двойные кавычки. Содержимое IFS используется «на входе» как объявление символов-разделителей, поэтому вы всегда получите пустую строку без кавычек.
Мартин Клейтон
30

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

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

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

konsolebox
источник
к сожалению, работает только для односимвольных разделителей
маоизм
24

Вот 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}"
}
gniourf_gniourf
источник
1
Ваше последнее решение очень хорошее, но его можно сделать чище, сделав join_retлокальную переменную, а затем повторить ее в конце. Это позволяет использовать join () обычным способом сценариев оболочки, например $(join ":" one two three), и не требует глобальной переменной.
Джеймс Снирингер,
1
@JamesSneeringer Я специально использовал этот дизайн, чтобы избежать подоболочек. В сценариях оболочки, в отличие от многих других языков, глобальные переменные, используемые таким образом, не обязательно являются плохой вещью; особенно если они здесь, чтобы помочь избежать подоболочек. Кроме того, $(...)уравновешивает конечные строки; поэтому, если последнее поле массива содержит завершающие символы новой строки, они будут обрезаны (см. демонстрацию, где они не обрезаны с моим дизайном).
gniourf_gniourf
Это работает с
многосимвольными
Чтобы ответить на вопрос «почему вам не нравится printf -v?»: В Bash локальные переменные не являются действительно локальными функциями, поэтому вы можете делать такие вещи. (Вызовите функцию f1 с локальной переменной x, которая, в свою очередь, вызывает функцию f2, которая модифицирует x - которая объявлена ​​локальной в области видимости f1) Но на самом деле не так должны работать локальные переменные. Если локальные переменные действительно являются локальными (или предполагается, что они есть, например, в скрипте, который должен работать как на bash, так и на ksh), то это вызывает проблемы со всей этой схемой «вернуть значение, сохраняя его в переменной с этим именем».
tetsujin
15

Это не слишком отличается от существующих решений, но позволяет избежать использования отдельной функции, не изменяется IFSв родительской оболочке и находится в одной строке:

arr=(a b c)
printf '%s\n' "$(IFS=,; printf '%s' "${arr[*]}")"

в результате чего

a,b,c

Ограничение: разделитель не может быть длиннее одного символа.

Бенджамин В.
источник
13

Не используя никаких внешних команд:

$ 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

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

Нил Гейсвайлер
источник
4
Если вы не хотите использовать промежуточную переменную, это можно сделать еще короче:echo ${FOO[@]} | tr ' ' ','
jesjimher
2
Я не понимаю отрицательных голосов. Это гораздо более компактное и удобочитаемое решение, чем другие, размещенные здесь, и четко предупреждено, что оно не работает, когда есть пробелы.
jesjimher
12

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

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

Результаты:

a,b,c

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

Яник Жируард
источник
$FOOэто всего лишь первый элемент массива. Кроме того, это разбивается для элементов массива, содержащих пробелы.
Бенджамин В.
9

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

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

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

саквояж
источник
7
s=$(IFS=, eval 'echo "${FOO[*]}"')
угорь боже
источник
8
Вы должны конкретизировать свой ответ.
Хосе
Самый лучший. Спасибо!!
Питер Пэн,
4
Хотелось бы мне понизить этот ответ, потому что он открывает дыру в безопасности и потому что он разрушит пробелы в элементах.
угорь ghEEz
1
@bxm действительно, кажется, сохраняет пробелы и не позволяет выходить из контекста аргументов эха. Я полагал , что добавление @Qможет избежать присоединившегося значения из искажая , когда они имеют столяр в них: foo=("a ," "b ' ' c" "' 'd e" "f " ";" "ls -latr"); s=$(IFS=, eval 'echo "${foo[*]@Q}"'); echo "${s}"выходах'a ,','b '\'' '\'' c',''\'' '\''d e','f ',';','ls -latr '
угорь ghEEz
1
Избегайте решений, которые используют подоболочки без необходимости.
konsolebox
5

решение printf, которое принимает разделители любой длины (на основе @ не имеет значения ответ)

#/!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
Риккардо Галли
источник
Это производит вывод с запятой.
Марк Ренуф
Последний бар = $ {bar: $ {# sep}} удаляет разделитель. Я просто копирую и вставляю в оболочку bash, и она работает. Какую оболочку вы используете?
Риккардо Галли
2
Любой printf спецификатор формата (например, %sнепреднамеренное $sep
внесение
sepможно продезинфицировать с ${sep//\%/%%}. Мне нравится ваше решение лучше чем ${bar#${sep}}или ${bar%${sep}}(альтернатива). Это хорошо, если преобразовать в функцию, которая сохраняет результат в переменную общего типа __, а не в echoнее.
konsolebox
function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
konsolebox
4
$ set a 'b c' d

$ history -p "$@" | paste -sd,
a,b c,d
Стивен Пенни
источник
Это должно быть наверху.
Эрик Уокер,
6
Это не должно быть на вершине: что, если HISTSIZE=0?
har-wradim
@ har-wradim, дело paste -sd,не в использовании истории.
Вед
@ Веда Нет, речь идет об использовании комбинации, и она не сработает, если HISTSIZE=0- попробуйте.
har-wradim
4

Укороченная версия топ-ответа:

joinStrings() { local a=("${@:3}"); printf "%s" "$2${a[@]/#/$1}"; }

Применение:

joinStrings "$myDelimiter" "${myArray[@]}"
Камило Мартин
источник
1
Более длинная версия, но не нужно копировать часть аргументов в переменную массива:join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '%s' "${@/#/$d}"; }
Rockallite
Еще одна версия: join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '$d%s' "${@}"; } это работает с использованием: join_strings 'delim' "${array[@]}"или без join_strings 'delim' ${array[@]}
кавычек
4

Объедините лучшее из всех миров до сих пор со следующей идеей.

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

Этот маленький шедевр

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

Примеры:

$ 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
гость
источник
1
Не очень приятно: по крайней мере, 2 проблемы: 1. join_ws ,(без аргументов) неправильно выводит ,,. 2. join_ws , -eнеправильно выводит ничего (это потому, что вы неправильно используете echoвместо printf). На самом деле я не знаю, почему вы рекламировали использование echoвместо printf: echoобщеизвестно, что оно сломано и printfявляется надежным встроенным.
gniourf_gniourf
1

Прямо сейчас я использую:

TO_IGNORE=(
    E201 # Whitespace after '('
    E301 # Expected N blank lines, found M
    E303 # Too many blank lines (pep8 gets confused by comments)
)
ARGS="--ignore `echo ${TO_IGNORE[@]} | tr ' ' ','`"

Это работает, но (в общем случае) ужасно сломается, если в элементах массива будет пробел.

(Для тех, кто заинтересован, это скрипт-оболочка для pep8.py )

Дэвид Волевер
источник
откуда вы получаете эти значения массива? если вы так жестко его кодируете, почему бы просто не foo = "a, b, c".?
ghostdog74
В этом случае , я на самом деле я жесткое кодирование значения, но я хочу , чтобы поместить их в массиве , так что я могу прокомментировать каждый по отдельности. Я обновил ответ, чтобы показать вам, что я имею в виду.
Дэвид Волевер
Предполагая , что вы на самом деле с помощью Баш, это может работать лучше: ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')". Оператор $()более мощный, чем backtics (допускает вложение $()и ""). Заключение в ${TO_IGNORE[@]}двойные кавычки также должно помочь.
Кевинарпе
1

Моя попытка

$ array=(one two "three four" five)
$ echo "${array[0]}$(printf " SEP %s" "${array[@]:1}")"
one SEP two SEP three four SEP five
Бен Дэвис
источник
1

Используйте perl для мультисимвольных разделителей:

function join {
   perl -e '$s = shift @ARGV; print join($s, @ARGV);' "$@"; 
}

join ', ' a b c # a, b, c

Или в одну строку:

perl -le 'print join(shift, @ARGV);' ', ' 1 2 3
1, 2, 3
Даниэль Патру
источник
у меня работает, хотя joinназвание конфликтует с какой-то хренью OS X.. я бы назвал это conjoined, или может быть jackie_joyner_kersee?
Алекс Грей,
1

Спасибо @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 $ 
$
гость
источник
1

Если элементы, к которым вы хотите присоединиться, не являются массивом, а просто строкой, разделенной пробелами, вы можете сделать что-то вроде этого:

foo="aa bb cc dd"
bar=`for i in $foo; do printf ",'%s'" $i; done`
bar=${bar:1}
echo $bar
    'aa','bb','cc','dd'

Например, мой вариант использования заключается в том, что в моем сценарии оболочки передаются некоторые строки, и мне нужно использовать это для запуска SQL-запроса:

./my_script "aa bb cc dd"

В my_script мне нужно сделать «SELECT * FROM table WHERE name IN (« aa »,« bb »,« cc »,« dd »). Тогда приведенная выше команда будет полезна.

Дексин Ван
источник
Вы можете использовать printf -v bar ...вместо того, чтобы запускать цикл printf в подоболочке и захватывать вывод.
codeforester
все вышеперечисленные причудливые решения не сработали, но ваш грубый
помог
1

Вот то, что поддерживает большинство POSIX-совместимых оболочек:

join_by() {
    # Usage:  join_by "||" a b c d
    local arg arr=() sep="$1"
    shift
    for arg in "$@"; do
        if [ 0 -lt "${#arr[@]}" ]; then
            arr+=("${sep}")
        fi
        arr+=("${arg}") || break
    done
    printf "%s" "${arr[@]}"
}
user541686
источник
Это хороший Bash-код, но POSIX вообще не имеет массивов (или local).
Андерс Касеорг
@Anders: Да, я научился этому нелегко совсем недавно :( Я оставлю это пока, хотя, поскольку большинство POSIX-совместимых оболочек, похоже, поддерживают массивы.
user541686
1

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

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

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

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[@]также делает действительную ссылку.

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

Обновить

В выпускной версии Bash 5.0 косвенное изменение переменных уже называется косвенным расширением, и его поведение уже явно задокументировано в руководстве:

Если первый символ параметра является восклицательным знаком (!), А параметр не является nameref, он вводит уровень косвенности. Bash использует значение, сформированное путем расширения остальной части параметра, в качестве нового параметра; затем это расширяется, и это значение используется в остальной части расширения, а не в расширении исходного параметра. Это известно как косвенное расширение.

Принимая во внимание, что в документации ${parameter}, parameterупоминается как «параметр оболочки, как описано (в) PARAMETERS или ссылка на массив ». А в документации массивов упоминается, что «на любой элемент массива можно ссылаться с помощью ${name[subscript]}». Это делает __r[@]ссылку на массив.

Присоединяйтесь по версии аргументов

Смотрите мой комментарий в ответе Риккардо Галли .

konsolebox
источник
2
Есть ли конкретная причина для использования __в качестве имени переменной? Делает код действительно нечитаемым.
PesaThe
@PesaThe это просто предпочтение. Я предпочитаю использовать общие имена для возвращаемой переменной. Другие неуниверсальные имена приписывают себя определенным функциям, и это требует запоминания. Вызов нескольких функций, которые возвращают значения для различных переменных, может сделать код менее понятным. Использование общего имени заставит сценария перевести значение из возвращаемой переменной в правильную переменную, чтобы избежать конфликта, и это сделает код более читабельным, поскольку он становится явным в том месте, куда возвращаются возвращаемые значения. Я делаю несколько исключений из этого правила, хотя.
konsolebox
0

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

#!/bin/bash

FOO=( a b c )
BAR=""

for index in ${!FOO[*]}
do
    BAR="$BAR,${FOO[$index]}"
done
echo ${BAR:1}
Dengel
источник
0

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

arr=()
for x in $(some_cmd); do
   arr+=($x,)
done
arr[-1]=${arr[-1]%,}
echo ${arr[*]}
Ян Келлинг
источник
0

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

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

Пример,

arr=(1 2 3 4 5)
x=${"${arr[*]}"// /,}
echo $x  # output: 1,2,3,4,5
user31986
источник
1
Это не работает правильно для строки с пробелами: `t = (a" b c "d); echo $ {t [2]} (печатает "b c"); echo $ {"$ {t [*]}" // /,} (печатает a, b, c, d)
kounoupis
7
bash: ${"${arr[*]}"// /,}: bad substitution
Кэмерон Хадсон
0

Возможно, поздно для вечеринки, но это работает для меня:

function joinArray() {
  local delimiter="${1}"
  local output="${2}"
  for param in ${@:3}; do
    output="${output}${delimiter}${param}"
  done

  echo "${output}"
}
TacB0sS
источник
-1

Возможно, я упускаю что-то очевидное, так как я новичок во всем, что касается 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. Достаточно хорошо для меня.

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

Иордания
источник
1
Я не downvoter, но манипулирование глобальными в функции кажется довольно странным.
tripleee
-2
liststr=""
for item in list
do
    liststr=$item,$liststr
done
LEN=`expr length $liststr`
LEN=`expr $LEN - 1`
liststr=${liststr:0:$LEN}

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

byte_array
источник
-2
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
мяу
источник