Как я могу преобразовать массив в список, разделенный запятыми в Bash?

Как я могу преобразовать массив в список, разделенный запятыми в Bash?

18.12.2018 09:42:16 Просмотров 42 Источник

У меня есть массив, который я хочу преобразовать в одну строку или переменную, разделенную запятыми, используя скрипт оболочки.

У меня есть массив данных, и я просто печатаю значения, которые находятся внутри:

echo "${data[*]}"

Выход:

/QE-CI-RUN-71/workspace/QE-AU/57/testng-results_1.xml 
/QE-CI-RUN-71/workspace/QE-AU/57/testng-results_2.xml

Я просто хочу, чтобы вышеприведенный вывод хранился в одной переменной, разделенной запятыми, и я хочу использовать его для дальнейшей ссылки. Как я могу достичь этого в Баш?

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

У вопроса есть решение - Посмотреть?

Ответы - Как я могу преобразовать массив в список, разделенный запятыми в Bash? / How can I convert an array to a comma separated list in Bash?

Charles Duffy

18.12.2018 09:52:33

Если вы хотите отделить запятыми, сделайте это первым символом в IFS:

data=( first second third )
IFS=,
echo "${data[*]}"

...испустит:

first,second,third
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839385#comment94529284_53839385
Будьте осторожны, оставляя $IFSопределенным таким образом; вам может не понравиться то, что он делает с командами позже в вашем сценарии.
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839385#comment94529492_53839385
Проблема в том, что oIFS=$IFS; ...; IFS=$oIFSтакже не является noop - он изменит unset IFSна IFS='', которые являются двумя различными состояниями с различным поведением (первое действует как IFS=$' \t\n'). localподход, или определение области с помощью подрешетки, подходит, если вы хотите быть в безопасности - и ответ чепнера уже охватывает их. Я бы предпочел не писать код, который притворяется, что у него есть функции безопасности, которых на самом деле не существует, а также рассматривать любой код, который выполняет расширение без кавычек без явного значения IFS (или который не может установить IFSявно при запуске read), по своей сути сломанным.
Является ответом!
Adam Katz

18.12.2018 09:55:13

Есть несколько способов сделать это:

1. Присоединяйтесь непосредственно к printf(через комментарий Чарльза Даффи)

printf -v joined '%s,' "${data[@]}"
echo "${joined%,}"

printf, но у вас останется конечная запятая. (Этот метод работает даже в оболочке POSIX, хотя вам придется использовать printf '%s,' "${data[@]}"в качестве массива, так как POSIX не может обрабатывать другие типы массивов).

2. Измените $@

Это переопределяет разделитель полей только в пределах области действия этой функции, поэтому при stringили (если это пустое или неопределенное) пространство.

Это может быть сделано без функции, но есть некоторые гадости о сохранении join_arr() { local IFS="$1" shift echo "${data[$*]}" } join_arr , "${data[@]}" : Чарльз Даффи отмечает, что возвращаясь $data после временно переназначить это можно оценить, чтобы string, но если бы string был ранее определен, который отличается от IFS="$OLD_IFS" и пока это возможно, чтобы подразнить тех отдельно, этот функциональный подход намного чище, благодаря использованию IFS="" ограничить join_arr() { local IFS="$1" shift echo "${data[$*]}" } join_arr , "${data[@]}" с размахом.

3a. перелистайте его содержимое (и печатайте постепенно)

unset IFS

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

3B. перебираем его содержимое (и строим переменную)

string

4. Сохраните массив в виде строки и выполните замену (обратите внимание, что в массиве не должно быть пробелов*)

delim=""
for item in "${data[@]}"; do
  printf "%s" "$delim$item"
  delim=","
done
echo # add a newline

* Это будет работать только в том случае, если первый символ sleep 0.1(пробел по умолчанию) не существует ни в одном из элементов массива.

При этом используется подстановка шаблона bash: delim="" joined="" for item in "${data[@]}"; do joined="$joined$delim$item" delim="," done echo "$joined" заменит каждый экземпляр data_string="${data[*]}" echo "${data_string//${IFS:0:1}/,}" в stringна ${parameter//pattern/string}. В этом случае ${parameter//pattern/string}- это $parameter, подстрока string, начинающаяся в начале и заканчивающаяся после одного символа.

Z shell (string) может сделать это в одном вложенном параметре расширения:

${IFS:0:1}
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839433#comment94527147_53839433
Подход "строка-затем-замена" изменит и другие пространства. Рассмотрим data=( "first item" "second item" "third item" ); вы хотите вывести first item,second item,third item,а не first,item,second,item,third,item.
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839433#comment94527285_53839433
истинный. мы летим вслепую, не имея лучших примеров того, как выглядят данные. Я работал над решением этой проблемы, но bash не очень умно комбинирует замены переменных или использует неявные переменные , такие как $IFS, поэтому я решил не делать этого. С тех пор я также добавил ответ на цикл for.
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839433#comment94527502_53839433
На практике, кстати, я обычно использую printf -v var '%s,' "${data[@]}"; echo "${var%,}"- не изменяет IFSи не делает предположений о том, как выглядят данные.
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839433#comment94528288_53839433
Как программист POSIX shell, я часто пропускаю такие башизмы, как sprintf-подобный printf -v var. Тем не менее, мой цикл выводит данные, как это видно, что позволяет интерактивному сеансу быть немного более отзывчивым, учитывая большие входные данные в загруженной системе (и не нужно отменять конечный разделитель). Я не уверен, сколько затрат связано с легкомысленными назначениями, но я предполагаю, что это незначительно (особенно в этом случае, поскольку мы ограничены длиной параметра команды, getconf ARG_MAX).
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839433#comment94528858_53839433
Фигурные скобки сами по себе не ограничивают область изменения. Это основное различие между группой команд и подрешеткой; группа команд все еще выполняется в текущей оболочке.
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839433#comment94529007_53839433
В цикле fordelimимеет неопределенное значение во время первой итерации. Возможно, вы хотите инициализировать его, чтобы он был пустым?
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839433#comment94529197_53839433
@CharlesDuffy-о, так тщательно ". Да, было бы безопаснее инициализировать его. Обновленный.
chepner

18.12.2018 10:14:24

Чтобы упростить локализацию изменения IFS, используйте функцию:

join () {
  local IFS="$1"
  shift
  echo "$*"
}

join , "${data[@]}"
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839647#comment94527557_53839647
Мне это нравится. Он решает проблему перезаписи $IFSи использует синтаксис, с которым пользователи других языков будут хорошо знакомы.
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839647#comment94528114_53839647
Существует более короткий способ локализации значения (IFS=,; echo "${data[*]}"), но за счет (почти наверняка) разветвления нового процесса для подрешетки.
https://stackoverflow.com/questions/53839253/how-can-i-convert-an-array-to-a-comma-separated-list-in-bash/53839647#comment94528837_53839647
Фигурные скобки также не локализуют значение IFS. Вы должны использовать подоболочку, чтобы сделать это. Преимущество функции заключается в том, что вы можете использовать localкоманду, чтобы избежать перезаписи глобального значения.
Nico

12.04.2019 06:10:07

printComma(){
    printf "%s," "${@:1:${#}-1}"
    printf "%s" "${@:${#}}"
}
printNewline(){
    printf "%s\n" "${@:1:${#}-1}"
    echo "${@:${#}}"
}
join () {
  local IFS="$1"
  shift
  echo "$*"
}
declare -a comma=(
    a
    b
    c
)
declare -a newline=(
    xyz
    abc
    def
    ghi
)
echo "\
Comma separated list $(printComma "${comma[@]}")
Newline list: $(printNewline "${newline[@]}")

Comma separated list $(join , "${comma[@]}")
Newline list: $(join \n "${newline[@]}")"
Comma separated list a,b,c
Newline list: xyz
abc
def
ghi

Comma separated list a,b,c
Newline list: xyznabcndefnghi
Dan Dimacuha

18.06.2019 07:44:58

Для ksh, попробуйте это!

foo=`echo $(echo ${data[@]}) | tr ' ' ','`

Таким образом, вы можете управлять разделителем, переводя пробел (по умолчанию) в запятую! (или любой другой, который вы можете придумать) :)

Закрыть X