Я думаю, что вы спрашиваете две разные вещи там.
Есть ли способ заставить 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
printf ...%q...
варианта не подходят для повторного ввода в оболочку, если массив имеет@
ключ, так как% q его не заключает в кавычки иa=([@]=value)
является ошибкой синтаксиса вbash
."${x@Q}"
цитирует это тоже, поскольку он цитирует все строки (и выглядит лучше). добавил примечание об использовании этого.zsh
с его флагами расширения переменных (которые снова предшествуют bash на десятилетия и с помощью которых вы можете выбрать стиль цитирования: $ {(q) var}, $ {(qq) var} ...) для лучшего дизайна. bash имеет ту же проблему, что и mksh, в том, что он не заключает в кавычки пустую строку (здесь проблема не возникает, так как в любом случае bash не поддерживает пустые ключи). Кроме того, при использовании стилей кавычек, отличных от одинарных кавычек (${var@Q}
прибегает к$'...'
некоторым значениям), важно, чтобы код повторно вводился в той же локали.x=; echo "${x@Q}"
даёт''
,unset x; echo "${x@Q}"
ничего не дает.)@Q
Кажется, Bash предпочитает$'\n'
буквальный перевод строки, который в некоторых ситуациях может быть хорошим (но я не могу сказать, что предпочитают другие). Конечно, выбор там не будет плохим.$'...'
Синтаксис является потенциальной проблемой в таких вещах , как ,LC_ALL=zh_HK.big5hkscs bash -c 'a=$'\''\n\u3b1'\''; printf "%s\n" "${a@Q}"'
какие выходы$'\n<0xa3><0x5c>'
и0x5c
сам по себе является обратным косыми чертами , так что вы должны были бы проблемы , если это цитата была интерпретированы в другой местности.2 вилки
Может быть, это:
3 вилки
или это:
Без вилки
сравнивать с
Сравнение времени выполнения
Поскольку последний синтаксис не использует fork, они могут быть быстрее:
Но это утверждение не остается верным, если массив становится большим; если редукция вилки эффективна для небольших процессов, использование специальных инструментов более эффективно для больших процессов.
замечание
Поскольку оба ( разветвленных ) решения используют выравнивание , ни одно из них не будет работать, если какая-либо переменная содержит символ новой строки . В этом случае единственным способом является
for
петля.источник
for
. Который является позором, действительно.pr
будет короче ... Я не уверен, чтоpr
синтаксис остается медленнее, даже с большими массивами!${!array[@]}
и${array[@]}
сначала для этого работать.paste
длиной больше, чемfor
цикл в вопросе, записанный в одной строкеfor i in "${!array[@]}"; do echo "$i=${array[$i]}" ; done
, но требует двух подоболочек и внешней программы. Как это аккуратнее? Решение сpr
также ломает, если есть много элементов, поскольку это пытается разбить на выходные данные. Вам нужно будет использовать что-то вроде того,| pr -2t -l"${#array[@]}"
которое начинает запоминаться по сравнению с простым циклом, и опять же, длиннее его.bash
, сcmd1 | cmd2
помощью 2 вилок, даже если cmd1 или cmd2 или оба встроенных.Если вы ищете оболочку с лучшей поддержкой ассоциативных массивов, попробуйте
zsh
.В
zsh
(где ассоциативные массивы были добавлены в 1998 г. по сравнению с 1993 г. для ksh93 и 2009 г. для bash)$var
или${(v)var}
расширяются до (непустых) значений хэша,${(k)var}
до (непустых) ключей (в том же порядке), и${(kv)var}
оба ключа и значения.Чтобы сохранить пустые значения, как для массивов, вам нужно заключить в кавычки и использовать
@
флаг.Таким образом, чтобы напечатать ключи и значения, это просто вопрос
Хотя, чтобы учесть возможно пустой хеш, вы должны сделать:
Также обратите внимание, что zsh использует гораздо более разумный и полезный синтаксис определения массива, чем
ksh93
s (скопированныйbash
):Что значительно упрощает копирование или объединение ассоциативных массивов:
(вы не можете легко скопировать хеш без цикла с
bash
, и обратите внимание, что вbash
настоящее время не поддерживаются пустые ключи или ключ / значения с байтами NUL).Смотрите также
zsh
функции архивирования массивов, которые вам обычно нужны для работы с ассоциативными массивами:источник
Поскольку typeset делает то, что вы хотите, почему бы просто не отредактировать его вывод?
дает
где
Подробно, но довольно легко увидеть, как работает форматирование: просто выполните конвейер с постепенно увеличивающимся количеством команд sed и tr . Измените их, чтобы они соответствовали красивым вкусам печати.
источник
sed
s иtr
s не намного проще, чемfor
цикл сprintf
.tr
переводить символ за символом, это не соответствует строки?tr "]=" " ="
меняет "]" на пробел и a=
на a=
, независимо от положения. Таким образом, вы могли бы просто объединить все триtr
в один.Еще один вариант - перечислить все переменные и grep для той, которую вы хотите.
set | grep -e '^aa='
Я использую это для отладки. Я сомневаюсь, что он очень производительный, поскольку в нем перечислены все переменные.
Если бы вы делали это часто, вы могли бы сделать это такой функцией:
aap() { set | grep -e "^$1="; }
К сожалению, когда мы проверяем производительность, используя время:
$ time aap aa aa=([0]="abc") . real 0m0.014s user 0m0.003s sys 0m0.006s
Поэтому, если бы вы делали это очень часто, вы бы хотели версию @ F.Hauri NO FORKS, потому что она намного быстрее.
источник