2 вилки

2 вилки

Есть ли способ вывести весь массив ([ключ]=значение), не перебирая все элементы?

Предположим, я создал массив с некоторыми элементами:

declare -A array
array=([a1]=1 [a2]=2 ... [b1]=bbb ... [f500]=abcdef)

Я могу распечатать весь массив с помощью

for i in "${!array[@]}"
do
echo "${i}=${array[$i]}"
done

Однако, похоже, bash уже знает, как получить все элементы массива за один «заход» — и ключи, ${!array[@]}и значения ${array[@]}.

Есть ли способ заставить bash вывести эту информацию без цикла?

Редактировать:
typeset -p arrayделает это!
Однако я не могу удалить и префикс, и суффикс в одной замене:

a="$(typeset -p array)"
b="${a##*(}"
c="${b%% )*}"

Есть ли более чистый способ получить/вывести только часть вывода «ключ=значение»?

решение1

Я думаю, вы задаете два разных вопроса.

Есть ли способ заставить bash вывести эту информацию без цикла?

Да, но они не так хороши, как простое использование петли.

Есть ли более чистый способ получить/вывести только часть вывода «ключ=значение»?

Да, forцикл. Он имеет те преимущества, что не требует внешних программ, прост и позволяет довольно легко контролировать точный формат вывода без сюрпризов.


Любое решение, которое пытается обработать вывод declare -p( typeset -p), должно иметь дело с а) возможностью того, что сами переменные содержат скобки или квадратные скобки, б) кавычками, которые declare -pнеобходимо добавить, чтобы сделать вывод допустимым вводом для оболочки.

Например, ваше расширение b="${a##*(}"съедает некоторые значения, если какой-либо ключ/значение содержит открывающую скобку. Это потому, что вы использовали ##, что удаляетсамый длинныйпрефикс. То же самое для c="${b%% )*}". Хотя вы, конечно, можете сопоставить шаблон, напечатанный по, declareболее точно, вам все равно придется нелегко, если вы не хотите всех этих кавычек, которые он делает.

Это выглядит не очень красиво, если только вам это не нужно.

$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'

С помощью forцикла проще выбрать нужный вам формат вывода:

# without quoting
$ for x in "${!array[@]}"; do printf "[%s]=%s\n" "$x" "${array[$x]}" ; done
[def]="foo bar"
[abc]='foobar'

# with quoting
$ for x in "${!array[@]}"; do printf "[%q]=%q\n" "$x" "${array[$x]}" ; done
[def]=\"foo\ bar\"
[abc]=\'foobar\'

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

С текущим Bash (4.4, я думаю) вы также можете использовать printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"вместо printf "%q=%q". Это создает несколько более удобный формат кавычек, но, конечно, его немного сложнее запомнить, как писать. (И он закавычивает угловой случай @как ключ массива, который %qне закавычивает.)

Если цикл for кажется слишком утомительным для написания, сохраните его где-нибудь как функцию (без кавычек):

printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ;  }  

А затем просто используйте это:

$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)

Работает также с индексированными массивами:

$ b=(abba acdc)
$ printarr b
0=abba
1=acdc

решение2

declare -p array
declare -A array='([a2]="2" [a1]="1" [zz]="Hello World" [b1]="bbb" [f50]="abcd" )'

2 вилки

Может быть, это:

printf "%s\n" "${!array[@]}"
a2
a1
f50
zz
b1

printf "%s\n" "${array[@]}"
2
1
abcd
Hello World
bbb

printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t
a2                              2
a1                              1
f50                             abcd
zz                              Hello World
b1                              bbb

3 вилки

или это:

paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}")
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb

Нет вилки

сравнивать с

for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb

Сравнение времени выполнения

Поскольку последний синтаксис не использует fork, он может быть быстрее:

time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
      5      11      76
real    0m0.005s
user    0m0.000s
sys     0m0.000s

time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
      5       6      41
real    0m0.008s
user    0m0.000s
sys     0m0.000s

time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
      5       6      41
real    0m0.002s
user    0m0.000s
sys     0m0.001s

Но это утверждение не остается верным, если массив становится большим: если сокращение числа разветвлений эффективно для небольших процессов, то использование специализированных инструментов более эффективно для более крупных процессов.

for i in {a..z}{a..z}{a..z};do array[$i]=$RANDOM;done


time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
  17581   35163  292941
real    0m0.150s
user    0m0.124s
sys     0m0.036s

time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
  17581   17582  169875
real    0m0.140s
user    0m0.000s
sys     0m0.004s

time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
  17581   17582  169875
real    0m0.312s
user    0m0.268s
sys     0m0.076s

Замечание

Как оба (раздвоенный) решения используютвыравнивание, ни один из них не будет работать, если какая-либо переменная содержитновая линия. В этом случае единственный путь — это forцикл.

Более надежный и подробный ответ на StackOverflow

решение3

Bash 5.1 позволяет очень просто отображать ассоциативные массивы, используя Kзначение, как в ${arr[@]@K}:

$ declare -A arr
$ arr=(k1 v1 k2 v2)
$ printf "%s\n" "${arr[@]@K}"
k1 "v1" k2 "v2" 

ИзДокумент описания Bash 5.1:

hh. Новое преобразование параметра `K' для отображения ассоциативных массивов в виде пар ключ-значение.

Это хорошо объяснено вСправочное руководство Bash → 3.5.3 Расширение параметров оболочки:

${parameter@operator}

К
Создает версию значения параметра, возможно заключенную в кавычки, за исключением того, что она выводит значения индексированных и ассоциативных массивов в виде последовательности пар ключ-значение, заключенных в кавычки (см. Массивы).

решение4

Снабранныйделает то, что вам нужно, почему бы просто не отредактировать его вывод?

typeset -p array | sed s/^.*\(// | tr -d ")\'\""  | tr "[" "\n" | sed s/]=/' = '/

дает

a2 = 2  
a1 = 1  
b1 = bbb 

Где

array='([a2]="2" [a1]="1" [b1]="bbb" )'

Многословно, но довольно легко увидеть, как работает форматирование: просто выполните конвейер с постепенно увеличивающимся количествомседитрКоманды. Измените их, чтобы они соответствовали вкусам печати.

Связанный контент