Myślę, że pytasz o dwie różne rzeczy.
Czy istnieje sposób, aby bash wydrukował te informacje bez pętli?
Tak, ale nie są tak dobre, jak zwykła pętla.
Czy istnieje prostszy sposób na uzyskanie / wydrukowanie tylko części klucz = wartość wyniku?
Tak, for
pętla. Ma to tę zaletę, że nie wymaga programów zewnętrznych, jest proste i sprawia, że kontrolowanie dokładnego formatu wyjściowego jest dość łatwe bez niespodzianek.
Każde rozwiązanie, które próbuje obsłużyć dane wyjściowe declare -p
( typeset -p
), musi dotyczyć: a) możliwości samych zmiennych zawierających nawiasy lub nawiasy, b) cytowania, żedeclare -p
należy dodać, aby dane wyjściowe były prawidłowe dla powłoki.
Na przykład twoje rozwinięcie b="${a##*(}"
zjada niektóre wartości, jeśli dowolny klucz / wartość zawiera nawias otwierający. Jest tak, ponieważ użyłeś ##
, który usuwa najdłuższy prefiks. To samo dotyczy c="${b%% )*}"
. Chociaż możesz oczywiście dopasować declare
dokładnie wydrukowany szablon , nadal będziesz miał trudności, jeśli nie chcesz wszystkich cytatów.
Nie wygląda to zbyt ładnie, chyba że jest to potrzebne.
$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'
Dzięki for
pętli łatwiej jest wybrać format wyjściowy, jak chcesz:
# 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\'
Stamtąd można również w prosty sposób zmienić format wyjściowy (usuń nawiasy wokół klucza, umieść wszystkie pary klucz / wartość w jednym wierszu ...). Jeśli potrzebujesz cytowania czegoś innego niż sama powłoka, nadal musisz to zrobić sam, ale przynajmniej masz surowe dane do pracy. (Jeśli masz nowe znaki w kluczach lub wartościach, prawdopodobnie będziesz potrzebować cytowania).
Przy obecnej wersji Bash (wydaje mi się, że 4.4) możesz także użyć printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"
zamiast printf "%q=%q"
. Tworzy nieco ładniejszy format cytowany, ale oczywiście jest trochę więcej pracy do zapamiętania, aby pisać. (I cytuje przypadek narożny @
jako klucz tablicy, który %q
nie cytuje.)
Jeśli pętla for wydaje się zbyt zmęczona, aby pisać, zapisz gdzieś funkcję (bez cytowania tutaj):
printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ; }
A następnie użyj tego:
$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)
Działa również z indeksowanymi tablicami:
$ b=(abba acdc)
$ printarr b
0=abba
1=acdc
printf ...%q...
wariantu nie nadaje się do ponownego włożenia do powłoki, jeśli tablica ma@
klucz, ponieważ% q go nie zacytuje ia=([@]=value)
jest to błąd składniowy wbash
."${x@Q}"
cytuje to również, ponieważ cytuje wszystkie ciągi (i wygląda ładniej). dodano notatkę o korzystaniu z tego.zsh
ze swoimi zmiennymi flagami ekspansji (które ponownie wyprzedzają bash o dekady i za pomocą których możesz wybrać styl cytowania: $ {(q) var}, $ {(qq) var} ...) dla lepszego projektu. bash ma ten sam problem co mksh, ponieważ nie cytuje pustego ciągu (nie jest to problemem, ponieważ bash nie obsługuje pustych kluczy). Ponadto, przy użyciu cytując style inne niż pojedynczy cudzysłów (${var@Q}
ośrodki do$'...'
niektórych wartości) ważne jest, że kod będzie reinput w tej samej lokalizacji.x=; echo "${x@Q}"
daje''
,unset x; echo "${x@Q}"
nic nie daje).@Q
Wydaje się, że Bash woli$'\n'
od dosłownego nowego wiersza, co w niektórych sytuacjach może być dobre (ale nie wiem, co wolą inni). Oczywiście wybór nie byłby zły.$'...'
Składnia jest potencjalnym problemem w takie rzeczy jakLC_ALL=zh_HK.big5hkscs bash -c 'a=$'\''\n\u3b1'\''; printf "%s\n" "${a@Q}"'
które wyjścia$'\n<0xa3><0x5c>'
i0x5c
sam jest backslash tak, że masz wątpliwości, czy to cytat był interpretowany w innej lokalizacji.2 widelec
Może to:
3 widelce
albo to:
Bez widelca
do porównania
Porównanie czasów wykonania
Ponieważ ostatnia składnia nie korzysta z fork, mogą być szybsze:
Ale ta afirmacja nie pozostaje prawdziwa, jeśli tablica staje się duża; jeśli redukcja widelców jest wydajna w przypadku małych procesów, użycie dedykowanych narzędzi jest bardziej wydajne w przypadku większych procesów.
Uwaga
Ponieważ oba ( rozwidlone ) rozwiązania wykorzystują wyrównanie , żadne z nich nie będzie działać, jeśli jakakolwiek zmienna zawiera nowy wiersz . W takim przypadku jedynym sposobem jest
for
pętla.źródło
for
. To naprawdę szkoda.pr
jest krótsze ... Nie jestem pewien, czypr
składnia pozostanie wolniejsza, nawet przy dużych tablicach!${!array[@]}
i${array[@]}
najpierw to zadziała.paste
jest dłuższy niżfor
pętla w pytaniu zapisanym w jednym wierszufor i in "${!array[@]}"; do echo "$i=${array[$i]}" ; done
, ale wymaga dwóch podpowłok i zewnętrznego programu. Jak to jest schludne? Rozwiązanie zpr
również się psuje, jeśli jest wiele elementów, ponieważ próbuje paginować dane wyjściowe. Musisz użyć czegoś,| pr -2t -l"${#array[@]}"
co zaczyna być trudne do zapamiętania w porównaniu do prostej pętli, i znowu jest dłuższe.bash
,cmd1 | cmd2
środki 2 widelce, nawet jeśli cmd1 lub cmd2 lub oba są wbudowane.Jeśli szukasz powłoki z lepszą obsługą macierzy asocjacyjnych, spróbuj
zsh
.W
zsh
(gdzie tablice asocjacyjne zostały dodane w 1998 r., W porównaniu do 1993 r. Dla ksh93 i 2009 dla bash),$var
lub${(v)var}
rozwija się do (niepustych) wartości skrótu,${(k)var}
do (niepustych) kluczy (w tej samej kolejności), oraz${(kv)var}
do kluczy i wartości.Aby zachować puste wartości, jak w przypadku tablic, musisz zacytować i użyć
@
flagi.Aby wydrukować klucze i wartości, to tylko kwestia
Aby uwzględnić ewentualny pusty skrót, powinieneś:
Zauważ też, że zsh używa znacznie bardziej sensownej i użytecznej składni definicji tablicy niż
ksh93
's (kopiowane przezbash
):Co znacznie ułatwia kopiowanie lub scalanie tablic asocjacyjnych:
(nie można łatwo skopiować skrótu bez pętli
bash
i należy to zauważyćbash
obecnie nie obsługuje pustych kluczy ani kluczy / wartości z bajtami NUL).Zobacz także
zsh
funkcje kompresji tablic, których zwykle potrzebujesz do pracy z tablicami asocjacyjnymi:źródło
Skoro skład tekstu ma to, czego chcesz, dlaczego nie po prostu edytować jego wynik?
daje
Gdzie
Pełne, ale dość łatwo jest zobaczyć, jak działa formatowanie: po prostu uruchom potok z stopniowo większą liczbą poleceń sed i tr . Zmodyfikuj je, aby pasowały do ładnych gustów drukowania.
źródło
sed
s itr
nie jest nawet znacznie prostszy niżfor
pętla zprintf
.tr
tłumaczysz znak po znaku, nie pasuje do ciągów?tr "]=" " ="
zmienia „]” na spację i=
na=
, niezależnie od pozycji. Więc prawdopodobnie możesz po prostu połączyć wszystkie trzytr
z jednym.Jeszcze jedną opcją jest wyświetlenie wszystkich zmiennych i grep dla tej, którą chcesz.
set | grep -e '^aa='
Używam tego do debugowania. Wątpię, aby był bardzo wydajny, ponieważ zawiera listę wszystkich zmiennych.
Jeśli często to robisz, możesz ustawić taką funkcję:
aap() { set | grep -e "^$1="; }
Niestety, gdy sprawdzamy wydajność za pomocą czasu:
$ time aap aa aa=([0]="abc") . real 0m0.014s user 0m0.003s sys 0m0.006s
Dlatego, jeśli robisz to bardzo często, chciałbyś wersji NO FORKS @ F.Hauri, ponieważ jest ona o wiele szybsza.
źródło