Если у меня есть такой массив в Bash:
FOO=( a b c )
Как мне соединить элементы запятыми? Например, производство a,b,c
.
Решение по перезаписи Паскаля Пилза как функции в 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
konsolebox
стиль :)function join { local IFS=$1; __="${*:2}"; }
илиfunction join { IFS=$1 eval '__="${*:2}"'; }
. Тогда используйте__
после. Да, я пропагандирую использование__
в качестве переменной результата;) (и общей итерационной переменной или временной переменной). Если концепция попала на популярный вики-сайт Bash, меня скопировали :)$d
в спецификатор форматаprintf
. Вы думаете, что в безопасности, так как «избежали»,%
но есть и другие предостережения: когда разделитель содержит обратную косую черту (например,\n
) или когда разделитель начинается с дефиса (и, возможно, других, о которых я не могу думать сейчас). Конечно, вы можете исправить это (заменить обратную косую черту на двойную обратную косую черту и использоватьprintf -- "$d%s"
), но в какой-то момент вы почувствуете, что сражаетесь с оболочкой, а не работаете с ней. Вот почему в своем ответе ниже я добавил разделитель к условиям, которые необходимо объединить.Еще одно решение:
Редактировать: то же самое, но для многосимвольного разделителя переменной длины:
источник
printf -v bar ",%s" "${foo[@]}"
. Это на одинfork
меньше (на самом делеclone
). Он даже разветвление чтения файла:printf -v bar ",%s" $(<infile)
.$separator
, не содержит%s
или тому подобное, вы можете сделать свойprintf
крепкийprintf "%s%s" "$separator" "${foo[@]}"
.printf "%s%s"
будет использовать разделитель в первом наборе ТОЛЬКО набора выходных данных, а затем простоprintf "%s" "${foo[@]/#/$separator}"
.IFS=; regex="${foo[*]/#/$separator}"
. На этом этапе это, по сути, становится ответом gniourf_gniourf, который IMO чище с самого начала, то есть использование функции для ограничения объема изменений IFS и временных переменных.источник
bar=$( IFS=, ; echo "${foo[*]}" )
@
вместо*
, как в$(IFS=, ; echo "${foo[@]}")
? Я вижу, что*
уже сохраняет пробел в элементах, опять же не знаю, как, так@
как обычно требуется для этого.*
. На странице руководства bash найдите «Специальные параметры» и найдите объяснение рядом с*
:Может быть, например,
источник
echo "-${IFS}-"
(фигурные скобки отделяют черточки от имени переменной).echo $IFS
делает то же самое.Удивительно, но мое решение еще не дано :) Это самый простой способ для меня. Это не нуждается в функции:
Примечание. Было замечено, что это решение хорошо работает в режиме без POSIX. В режиме POSIX элементы все еще правильно соединены, но
IFS=,
становятся постоянными.источник
Вот 100% чистая функция Bash, которая делает эту работу:
Смотреть:
Это сохраняет даже завершающие символы новой строки и не нуждается в подоболочке для получения результата функции. Если вам не нравится
printf -v
(почему бы вам это не понравилось?) И передача имени переменной, вы, конечно, можете использовать глобальную переменную для возвращаемой строки:источник
join_ret
локальную переменную, а затем повторить ее в конце. Это позволяет использовать join () обычным способом сценариев оболочки, например$(join ":" one two three)
, и не требует глобальной переменной.$(...)
уравновешивает конечные строки; поэтому, если последнее поле массива содержит завершающие символы новой строки, они будут обрезаны (см. демонстрацию, где они не обрезаны с моим дизайном).Это не слишком отличается от существующих решений, но позволяет избежать использования отдельной функции, не изменяется
IFS
в родительской оболочке и находится в одной строке:в результате чего
Ограничение: разделитель не может быть длиннее одного символа.
источник
Не используя никаких внешних команд:
Предупреждение, предполагается, что элементы не имеют пробелов.
источник
echo ${FOO[@]} | tr ' ' ','
Я бы выводил массив в виде строки, затем преобразовывал пробелы в переводы строк, а затем использовал
paste
для объединения всего в одну строку, например, так:tr " " "\n" <<< "$FOO" | paste -sd , -
Результаты:
a,b,c
Это кажется самым быстрым и чистым для меня!
источник
$FOO
это всего лишь первый элемент массива. Кроме того, это разбивается для элементов массива, содержащих пробелы.С повторным использованием @ не имеет значения решение, но с одним утверждением, избегая подстановки $ {: 1} и необходимости промежуточной переменной.
printf имеет «Строка формата используется повторно так часто, как это необходимо для удовлетворения аргументов». на его страницах руководства, так что конкатенации строк задокументированы. Тогда хитрость заключается в том, чтобы использовать длину LIST для нарезки последнего sperator, поскольку cut будет сохранять только длину LIST при подсчете полей.
источник
источник
@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 '
решение printf, которое принимает разделители любой длины (на основе @ не имеет значения ответ)
источник
printf
спецификатор формата (например,%s
непреднамеренное$sep
sep
можно продезинфицировать с${sep//\%/%%}
. Мне нравится ваше решение лучше чем${bar#${sep}}
или${bar%${sep}}
(альтернатива). Это хорошо, если преобразовать в функцию, которая сохраняет результат в переменную общего типа__
, а не вecho
нее.function join_by { printf -v __ "${1//\%/%%}%s" "${@:2}"; __=${__:${#1}}; }
источник
HISTSIZE=0
?paste -sd,
не в использовании истории.HISTSIZE=0
- попробуйте.Укороченная версия топ-ответа:
Применение:
источник
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '%s' "${@/#/$d}"; }
join_strings () { local d="$1"; echo -n "$2"; shift 2 && printf '$d%s' "${@}"; }
это работает с использованием:join_strings 'delim' "${array[@]}"
или безjoin_strings 'delim' ${array[@]}
Объедините лучшее из всех миров до сих пор со следующей идеей.
Этот маленький шедевр
Примеры:
источник
join_ws ,
(без аргументов) неправильно выводит,,
. 2.join_ws , -e
неправильно выводит ничего (это потому, что вы неправильно используетеecho
вместоprintf
). На самом деле я не знаю, почему вы рекламировали использованиеecho
вместоprintf
:echo
общеизвестно, что оно сломано иprintf
является надежным встроенным.Прямо сейчас я использую:
Это работает, но (в общем случае) ужасно сломается, если в элементах массива будет пробел.
(Для тех, кто заинтересован, это скрипт-оболочка для pep8.py )
источник
ARGS="--ignore $(echo "${TO_IGNORE[@]}" | tr ' ' ',')"
. Оператор$()
более мощный, чем backtics (допускает вложение$()
и""
). Заключение в${TO_IGNORE[@]}
двойные кавычки также должно помочь.Моя попытка
источник
Используйте perl для мультисимвольных разделителей:
Или в одну строку:
источник
join
название конфликтует с какой-то хреньюOS X
.. я бы назвал этоconjoined
, или может бытьjackie_joyner_kersee
?Спасибо @gniourf_gniourf за подробные комментарии о моей комбинации лучших миров до сих пор. Извините за публикацию кода, который не был тщательно разработан и протестирован. Здесь лучше попробовать.
Эта красота по замыслу
Дополнительные примеры:
источник
Если элементы, к которым вы хотите присоединиться, не являются массивом, а просто строкой, разделенной пробелами, вы можете сделать что-то вроде этого:
Например, мой вариант использования заключается в том, что в моем сценарии оболочки передаются некоторые строки, и мне нужно использовать это для запуска SQL-запроса:
В my_script мне нужно сделать «SELECT * FROM table WHERE name IN (« aa »,« bb »,« cc »,« dd »). Тогда приведенная выше команда будет полезна.
источник
printf -v bar ...
вместо того, чтобы запускать цикл printf в подоболочке и захватывать вывод.Вот то, что поддерживает большинство POSIX-совместимых оболочек:
источник
local
).Использование косвенной переменной для прямого обращения к массиву также работает. Именованные ссылки также могут быть использованы, но они стали доступны только в 4.3.
Преимущество использования этой формы функции заключается в том, что вы можете использовать разделитель необязательно (по умолчанию используется первый символ по умолчанию
IFS
, то есть пробел; возможно, если хотите, сделать его пустой строкой), и это позволяет избежать расширения значений дважды (сначала при передаче в качестве параметров, а во-вторых, как"$@"
внутри функции).Это решение также не требует, чтобы пользователь вызывал функцию внутри подстановки команд, которая вызывает подоболочку, чтобы получить объединенную версию строки, назначенной другой переменной.
Не стесняйтесь использовать более удобное название для функции.
Это работает с 3,1 до 5,0-альфа. Как уже отмечалось, переменная косвенность работает не только с переменными, но и с другими параметрами.
Массивы и элементы массива также являются параметрами (объектами, которые хранят значения), и ссылки на массивы также технически являются ссылками на параметры. И так же, как специальный параметр
@
,array[@]
также делает действительную ссылку.Измененные или выборочные формы расширения (например, расширение подстроки), которые отклоняются от самого параметра, больше не работают.
Обновить
В выпускной версии Bash 5.0 косвенное изменение переменных уже называется косвенным расширением, и его поведение уже явно задокументировано в руководстве:
Принимая во внимание, что в документации
${parameter}
,parameter
упоминается как «параметр оболочки, как описано (в) PARAMETERS или ссылка на массив ». А в документации массивов упоминается, что «на любой элемент массива можно ссылаться с помощью${name[subscript]}
». Это делает__r[@]
ссылку на массив.Присоединяйтесь по версии аргументов
Смотрите мой комментарий в ответе Риккардо Галли .
источник
__
в качестве имени переменной? Делает код действительно нечитаемым.Этот подход заботится о пробелах в значениях, но требует цикла:
источник
Если вы строите массив в цикле, вот простой способ:
источник
x=${"${arr[*]}"// /,}
Это самый короткий способ сделать это.
Пример,
источник
bash: ${"${arr[*]}"// /,}: bad substitution
Возможно, поздно для вечеринки, но это работает для меня:
источник
Возможно, я упускаю что-то очевидное, так как я новичок во всем, что касается bash / zsh, но мне кажется, что вам вообще не нужно его использовать
printf
. Да и без этого не обидно.По крайней мере, до сих пор это работало без проблем.
Например,
join \| *.sh
который, скажем, я нахожусь в моем~
каталоге, выводитutilities.sh|play.sh|foobar.sh
. Достаточно хорошо для меня.РЕДАКТИРОВАТЬ: Это в основном ответ Нила Гейсвайлера , но обобщен в функцию.
источник
Это заботится о дополнительной запятой в конце также. Я не эксперт по Bash. Просто мой 2с, так как это более элементарно и понятно
источник
или
источник