Как объединить элементы массива Bash в строку с разделителем?
У меня есть массив в Bash, который выглядит следующим образом:
FOO=( a b c )
Как мне объединить элементы массива через запятую? Например, чтобы получить строку a,b,c
.
5 ответ(ов)
В вашем примере вы задаете массив foo
, состоящий из элементов a
, "b c"
и d
.
Вот шаги, которые происходят в вашем коде:
Вы создаете массив
foo
, в котором первый элемент —a
, второй элемент — строкаb c
(с пробелом), и третий элемент —d
.foo=(a "b c" d)
Затем вы используете переменную
IFS
(Internal Field Separator) для временной установки разделителя на запятую. После этого вы выводите все элементы массива в строку, где элементы массива будут разделены запятой.bar=$(IFS=, ; echo "${foo[*]}")
При выполнении команды
echo "$bar"
вы выводите значение переменнойbar
, которое будетa,b c,d
.
Таким образом, итоговый вывод будет:
a,b c,d
Таким образом, пробел в строке "b c"
сохраняется, так как он является частью одного элемента массива, а запятые вставляются между элементами массива при их объединении в строку.
Еще одно решение:
#!/bin/bash
foo=('foo bar' 'foo baz' 'bar baz')
bar=$(printf ",%s" "${foo[@]}")
bar=${bar:1}
echo $bar
Редактировать: то же самое, но для разделителя переменной длины с несколькими символами:
#!/bin/bash
separator=")|(" # например, для построения регулярного выражения, надеемся, что это не будет содержать %s
foo=('foo bar' 'foo baz' 'bar baz')
regex="$( printf "${separator}%s" "${foo[@]}" )"
regex="${regex:${#separator}}" # удаляем ведущий разделитель
echo "${regex}"
# Вывод: foo bar)|(foo baz)|(bar baz
В этом решении мы используем printf
для создания строки, в которой элементы массива foo
разделены заданным разделителем. В первом примере используется запятая в качестве разделителя, а во втором – строка с несколькими символами. С помощью обрезки строки мы удаляем ведущий разделитель, чтобы получить желаемый результат.
Да, вы можете сделать что-то подобное. Например, вы можете временно изменить значение переменной IFS
(Internal Field Separator), чтобы объединить элементы массива с использованием определенного разделителя. Вот пример кода на Bash:
SAVE_IFS="$IFS" # Сохраняем текущее значение IFS
IFS="," # Устанавливаем разделитель на запятую
FOOJOIN="${FOO[*]}" # Объединяем элементы массива FOO в строку
IFS="$SAVE_IFS" # Восстанавливаем оригинальное значение IFS
echo "$FOOJOIN" # Выводим объединенную строку
В этом коде мы сначала сохраняем текущее значение IFS
. Затем устанавливаем IFS
на запятую, что позволяет разделять элементы массива FOO
запятой при объединении. После этого мы восстанавливаем оригинальное значение IFS
, чтобы избежать неожиданных последствий в дальнейшем коде. Наконец, выводим объединенную строку. Это позволяет безопасно настраивать IFS
, не повредив другие части вашего скрипта.
Вы можете достичь желаемого результата, не используя внешние команды, с помощью параметрической подстановки в Bash. Ниже приведен пример кода, который сначала инициализирует массив, затем преобразует его в строку, разделяя элементы запятой:
FOO=( a b c ) # инициализация массива
BAR=${FOO[@]} # создание строки, разделенной пробелами, из массива
BAZ=${BAR// /,} # использование параметрической подстановки для замены пробелов на запятые
echo $BAZ # вывод результата
Вывод будет следующим:
a,b,c
Имейте в виду, что этот метод предполагает, что элементы массива не содержат пробелов, так как это может привести к некорректному поведению при подстановке.
Вот функция на чистом Bash, которая решает задачу:
join() {
# $1 - имя переменной для возврата
# $2 - разделитель
# $3... - элементы для соединения
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 - разделитель
# $2... - элементы для соединения
# результат в глобальной переменной join_ret
local sep=$1 IFS=
join_ret=$2
shift 2 || shift $(($#))
join_ret+="${*/#/$sep}"
}
Таким образом, вы можете использовать обе версии функции в зависимости от ваших предпочтений.
Как пройтись по массиву строк в Bash?
Добавить новый элемент в массив в Bash без указания индекса
Как разделить строку на массив в Bash?
Как объявить массив в одну строку?
Расширение переменных внутри одинарных кавычек в команде Bash