Як я можу приєднати елементи масиву до 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.