Подтвердить что ты не робот

Преобразование массива Bash в строку с разделителями

Я хотел бы знать следующее;

  1. Почему данный нерабочий пример не работает.
  2. Если есть какие-либо другие более чистые методы, чем те, которые приведены в рабочем примере.

Нерабочий пример

> ids=(1 2 3 4);echo ${ids[*]// /|}
1 2 3 4
> ids=(1 2 3 4);echo ${${ids[*]}// /|}
-bash: ${${ids[*]}// /|}: bad substitution
> ids=(1 2 3 4);echo ${"${ids[*]}"// /|}
-bash: ${"${ids[*]}"// /|}: bad substitution

Рабочий пример

> ids=(1 2 3 4);id="${ids[@]}";echo ${id// /|}
1|2|3|4
> ids=(1 2 3 4); lst=$( IFS='|'; echo "${ids[*]}" ); echo $lst
1|2|3|4

В контексте строка с разделителями, которая будет использоваться в команде sed для дальнейшего анализа.

4b9b3361

Ответ 1

# REVISION: 2017-03-14
# Use of read and other bash specific features (bashisms)

Поскольку скобки используются для выделения массива , а не строки :

ids="1 2 3 4";echo ${ids// /|}
1|2|3|4

Некоторые примеры: заполнение $ids двумя строками: a b и c d

ids=("a b" "c d")

echo ${ids[*]// /|}
a|b c|d

IFS='|';echo "${ids[*]}";IFS=$' \t\n'
a b|c d

... и, наконец:

IFS='|';echo "${ids[*]// /|}";IFS=$' \t\n'
a|b|c|d

Когда массив собран, разделяется 1-м char из $IFS, но с пробелом заменяется на | в каждом элементе массива.

Когда вы выполните:

id="${ids[@]}"

вы передаете строковое построение из слияния массива ids пробелом в новую переменную типа string.

Примечание:, когда "${ids[@]}" дает строку, разделенную пробелом, "${ids[*]}" (со звездой * вместо знака at @) будет отображать строку, разделенную символом первый символ $IFS.

что man bash говорит:

man -Len -Pcol\ -b bash | sed -ne '/^ *IFS /{N;N;p;q}'
   IFS    The  Internal  Field  Separator  that  is used for word splitting
          after expansion and to split  lines  into  words  with  the  read
          builtin command.  The default value is ``<space><tab><newline>''.

Игра с $IFS:

set | grep ^IFS=
IFS=$' \t\n'

declare -p IFS
declare -- IFS=" 
"
printf "%q\n" "$IFS"
$' \t\n'

Буквально a space, a tabulation и (значение or) a line-feed. Итак, в то время как первый символ - это пробел. использование * будет делать то же самое, что и @.

Но

{

    # OIFS="$IFS"
    # IFS=$': \t\n'
    # unset array 
    # declare -a array=($(echo root:x:0:0:root:/root:/bin/bash))

    IFS=: read -a array < <(echo root:x:0:0:root:/root:/bin/bash)

    echo 1 "${array[@]}"
    echo 2 "${array[*]}"
    OIFS="$IFS" IFS=:
    echo 3 "${array[@]}"
    echo 4 "${array[*]}"
    IFS="$OIFS"
}
1 root x 0 0 root /root /bin/bash
2 root x 0 0 root /root /bin/bash
3 root x 0 0 root /root /bin/bash
4 root:x:0:0:root:/root:/bin/bash

Примечание. Линия IFS=: read -a array < <(...) будет использовать : в качестве разделителя без постоянной установки $IFS. Это потому, что выходная строка #2 содержит пробелы в качестве разделителей.

Ответ 2

Ваш первый вопрос уже решен в F. Хаури ответ. Вот канонический способ объединения элементов массива:

ids=( 1 2 3 4 )
IFS=\| eval 'lst="${ids[*]}"'

Некоторые люди будут громко кричать, что eval является злом, но здесь совершенно безопасно, благодаря одинарным кавычкам. Это имеет только преимущества: нет подоболочек, IFS не глобально изменен, он не обрезает завершающие строки, и это очень просто.

Ответ 3

Вы также можете использовать printf, без каких-либо внешних команд или необходимости манипулировать IFS:

ids=(1 2 3 4)                     # create array
printf -v ids_d '|%s' "${ids[@]}" # yields "|1|2|3|4"
ids_d=${ids_d:1}                  # remove the leading '|'

Ответ 4

Вспомогательная функция для разделения массива аргументов строкой-разделителем:

# Split arguments on delimiter
# @Params
# $1: The delimiter string
# [email protected]: The arguments to delimit
# @Output
# >&1: The arguments separated by the delimiter string
split() {
  (($#<2)) && return 1 # At least 2 arguments required
  local -- delim="$1" str
  shift
  printf -v str "%s$delim" "[email protected]"
  echo "${str:0:-${#delim}}"
}

my_array=( 'Paris' 'Berlin' 'London' 'Brussel' 'Madrid' 'Oslo' )

split ', ' "${my_array[@]}"

Выход:

Paris, Berlin, London, Brussel, Madrid, Oslo