Як я можу приєднати елементи масиву до Bash?


416

Якщо у мене є такий масив у Bash:

FOO=( a b c )

Як з'єднати елементи комами? Наприклад, виробництво a,b,c.

Відповіді:


571

Рішення для перезапису Паскаля Пільца як функція в 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
Використовуйте це для мультихарактерних роздільників: функція join {perl -e '$ s = shift @ARGV; друкувати приєднання ($ s, @ARGV); ' "$ @"; } приєднатися ',' 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[*]}" )
Потрібні

8
+1 для найбільш компактного рішення, яке не потребує циклів, яким не потрібні зовнішні команди та яке не накладає додаткових обмежень на набір символів аргументів.
закінчення

22
Мені подобається рішення, але воно працює лише в тому випадку, якщо IFS - один персонаж
Jayen

8
Будь-яка ідея, чому це не працює, якщо використовувати @замість *, як у $(IFS=, ; echo "${foo[@]}")? Я можу бачити, що *вже зберігається пробіл у елементах, знову ж таки не впевнений, як, оскільки @для цього зазвичай потрібно.
haridsv

10
Відповідь на власне питання я знайшов вище. Відповідь полягає в тому, що IFS визнається лише для *. На сторінці "bash man" знайдіть "Спеціальні параметри" і шукайте пояснення поруч із *:
haridsv

66

Можливо, наприклад,

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

echo "$FOOJOIN"

3
Якщо ви це зробите, він вважає, що IFS - це змінна. Ви повинні зробити echo "-${IFS}-"(фігурні дужки відокремлюють тире від назви змінної).
Призупинено до подальшого повідомлення.

1
Все-таки отримав той самий результат (я просто поставив тире, щоб проілюструвати точку ... echo $IFSробить те саме.
Девід Волвер

41
Однак, як здається, це все одно працює ... Отже, як і більшість речей з Башем, я буду робити вигляд, що я це розумію і продовжую своє життя.
Девід Уолвер

2
"-" не є дійсним символом для імені змінної, тому оболонка робить правильно, коли ви використовуєте $ IFS-, вам не потрібно $ {IFS} - (bash, ksh, sh і zsh в Linux та solaris також згоден).
Іделік

2
@ Дані, різниця між вашим відлунням і Деннісом полягає в тому, що він використав подвійне цитування. Вміст IFS використовується "на вході" як декларація символів для розділення слів, тому ви завжди отримаєте порожній рядок без лапок.
martin clayton

30

Дивно, але моє рішення ще не дано :) Це для мене найпростіший спосіб. Він не потребує функції:

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

Примітка. Спостерігалось, що це рішення добре працює в режимі, що не є POSIX. У режимі POSIX елементи все ще належним чином з'єднуються, але IFS=,стають постійними.


на жаль, працює лише для однозначних роздільників
maoizm

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}"
}

1
Ваше останнє рішення дуже добре, але його можна зробити більш чистим, зробивши join_retлокальну змінну, а потім повторити її в кінці. Це дозволяє join () використовувати в звичайному сценарії оболонки, наприклад $(join ":" one two three), і не вимагає глобальної змінної.
James Sneeringer

1
@JamesSneeringer Я навмисно використовував цю конструкцію, щоб уникнути передплатежів. У сценаріях оболонок, на відміну від багатьох інших мов, глобальні змінні, використовувані таким чином, не обов'язково є поганою справою; особливо, якщо вони тут, щоб допомогти уникнути передплати. Більше того, $(...)обрізки, що затягують нові лінії; тож якщо останнє поле масиву містить зворотні нові рядки, вони будуть обрізані (див. демонстрацію, де вони не оброблені моїм дизайном).
gniourf_gniourf

Це працює з мультиплікаційними роздільниками, що робить мене щасливим ^ _ ^
spiffytech

Щоб вирішити питання, "чому б вам не сподобався 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 має "Рядок формату повторно використовується стільки разів, скільки потрібно для задоволення аргументів." на його довідкових сторінках, так що зв'язування рядків задокументовано. Тоді хитрість полягає у використанні довжини СПИСОК для подрібнення останнього спіратора, оскільки вирізання збереже лише довжину СПИСОК, коли кількість лічиться.


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

8
Ви повинні конкретизувати свою відповідь.
жарт

Найкращий. Дякую!!
peter pan gz

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,не у використанні історії.
Веда

@Veda Ні, мова йде про використання комбінації, і це не спрацює, якщо 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[@]}
Cometsong

4

Поєднуйте найкраще з усіх світів поки що з наступною ідеєю.

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

Цей маленький шедевр є

  • 100% чистий баш (розширення параметрів за допомогою 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 ' ' ',')". Оператор $()є більш потужним, ніж бетік (дозволяє вкладати$() та ""). Обгортання ${TO_IGNORE[@]}подвійними цитатами також повинно допомогти.
kevinarpe

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% чистий баш (дякую за те, що явно вказував, що також printf є вбудованим. Я про це раніше не знав ...)
  • працює з багатознаковими роздільниками
  • більш компактний і повніший, на цей раз ретельно продуманий і довготривалий стрес-тест із випадковими підрядками із скриптів оболонки серед інших, що охоплюють використання спеціальних символів оболонки або керуючих символів або відсутність символів як у розділювачі, так і / або параметрах, і крайових випадках , а кутові випадки та інші висліди, як ні в яких аргументах. Це не гарантує, що більше помилок немає, але знайти її буде трохи складніше. До речі, навіть такі відповіді, які зараз голосують в голові, і пов'язані з ними страждають від таких речей, як, наприклад, помилка ...

Додаткові приклади:

$ 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

всі вище придумані рішення не спрацювали, але ваш сирий зробив для мене (Y)
ishandutta2007

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[@]}"
}

Це добре 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згадується як «раковині параметра , як описано (в) Параметри або посилання на масив ». А в документації масивів зазначається, що "Будь-який елемент масиву може бути посилається за допомогою ${name[subscript]}". Це робить__r[@] посилання на масив.

Приєднуйтесь до версії аргументів

Дивіться мій коментар у відповіді Ріккардо Галлі .


2
Чи є конкретні причини використовувати __як ім’я змінної? Робить код дійсно нечитабельним.
Pesa

@PesaThe Це просто вподобання. Я вважаю за краще використовувати загальні імена для зворотної змінної. Інші неотримані імена відносять себе до певних функцій, і це вимагає запам'ятовування. Виклик декількох функцій, які повертають значення для різних змінних, може зробити код менш легким для дотримання. Використання загального імені змусить скриптера перенести значення з змінної, що повертається, у відповідну змінну, щоб уникнути конфлікту, і це зробить код в кінці більш читабельним, оскільки він стає явним, куди повертаються значення. Я роблю кілька винятків із цього правила.
konsolebox

0

Цей підхід дбає про простори в межах значень, але вимагає циклу:

#!/bin/bash

FOO=( a b c )
BAR=""

for index in ${!FOO[*]}
do
    BAR="$BAR,${FOO[$index]}"
done
echo ${BAR:1}

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

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}"
}

-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 . Досить добре для мене.

EDIT: Це в основному відповідь Ніла Гейсвейлера , але узагальнена у функції.


1
Я не прихильник, але маніпулювання глобалом у функції здається досить дурним.
трійчатка

-2
liststr=""
for item in list
do
    liststr=$item,$liststr
done
LEN=`expr length $liststr`
LEN=`expr $LEN - 1`
liststr=${liststr:0:$LEN}

Це також переймається додатковою комою в кінці. Я не фахівець з басу. Просто мій 2с, оскільки це більш елементарно і зрозуміло


-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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.