W sekcji specyfikacji POSIX na temat [Parametry specjalne [(http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02) znajdujemy.
@
rozszerza do pozycyjnych, począwszy od pierwszego. Kiedy rozwijanie odbywa się w podwójnych cudzysłowach i gdy wykonywane jest dzielenie pól (patrz Podział na pole), każdy parametr pozycyjny powinien rozszerzać się jako oddzielne pole, z zastrzeżeniem, że rozszerzenie pierwszego parametru będzie nadal połączone z początkową częścią oryginalne słowo (zakładając, że rozszerzony parametr został osadzony w słowie), a rozwinięcie ostatniego parametru będzie nadal połączone z ostatnią częścią oryginalnego słowa. Jeśli nie ma żadnych parametrów pozycyjnych, rozszerzenie "@" powinno generować zero pól, nawet gdy "@" jest podwójnie cytowane.
*
Rozwija się do parametrów pozycyjnych, zaczynając od jednego. Gdy rozszerzenie występuje w ciągu podwójnego cudzysłowu (patrz Podwójne cudzysłowy), powinno ono rozwinąć się do pojedynczego pola z wartością każdego parametru oddzielonego pierwszym znakiem zmiennej IFS lub przez if, jeśli IFS jest rozbrojony.Jeśli IFS jest ustawiony na łańcuch zerowy, nie jest to równoważne z rozbrojeniem; jego pierwszy znak nie istnieje, więc wartości parametrów są łączone.
zatem począwszy od podanych wariantów (są prostsze)
Widzimy zatem, że rozszerzanie *
„rozwinąć [S] w jednym polu wartości każdego parametru, oddzielone przez pierwszy znak Zmienna IFS ". Dlatego otrzymujesz a|b
od echo "${X[*]"
i log2 "${X[*]}"
.
Widzimy również, że ekspansja @
rozwija się w taki sposób, że "każdy parametr pozycyjny rozwija się jako osobne pole". To dlatego otrzymujesz a b
od echo "${X[@]}"
i log2 "${X[@]}"
.
Czy widzisz tę notatkę dotyczącą podziału pola w tekście specyfikacji? "gdzie odbywa się dzielenie pól (patrz Podział na pola)"? To klucz do tajemnicy tutaj.
Poza ofertami zachowanie rozszerzeń jest takie samo. Różnica polega na tym, co dzieje się później. W szczególności podział na pola i słowa.
Najprostszym sposobem pokazania problemu jest uruchomienie kodu z włączoną opcją set -x
.
Który dostaje to:
+ X=(a b)
+ IFS='|'
+ echo a b
a b
+ echo a b
a b
+ echo a b
a b
+ echo 'a|b'
a|b
+ echo ---
---
+ log1 a b
+ echo a b
a b
+ log1 a b
+ echo a b
a b
+ log1 a b
+ echo a b
a b
+ log1 'a|b'
+ echo a b
a b
+ echo ---
---
+ log2 a b
+ echo a b
a b
+ log2 a b
+ echo a b
a b
+ log2 a b
+ echo a b
a b
+ log2 'a|b'
+ echo 'a|b'
a|b
Rzeczą godną uwagi jest to, że przez czas log1
nazywany jest we wszystkich, ale ostateczna przypadku |
jest już poszedł.
Powodem, dla którego już go nie ma, jest to, że bez cytowań wyniki z rozszerzenia zmiennej (w tym przypadku rozszerzenie *
) są podzielone na pola/słowa. I od IFS
jest używany zarówno, aby połączyć rozszerzane pola, a następnie podzielić je ponownie |
zostanie połknięty przez podział pola.
I dokończyć wyjaśnienia (na razie faktycznie w pytaniu), powód ten nie powiedzie log1
nawet z cytowanego wersji ekspansji na rozmowy (tzn log1 "${X[*]}"
który rozszerza się log1 "a|b"
poprawnie) dlatego log1
samego nie używa cytowanej ekspansji @
, więc rozszerzenie @
w samej funkcji jest podzielone na słowa (co można zauważyć w przypadku echo a b
w tej obudowie log1
, a także wszystkich innych przypadkach log1
).
Oooh. Dobrze. Czy istnieje sposób, aby wytworzyć zachowanie, które próbowałem mieć (tj. Formatowanie z ogranicznikiem, ale nie dzielenie go słowami)? Myślę, że 'printf' może być najprostszą opcją (na przykład: http://stackoverflow.com/questions/12985178/) – Norswap
Zawsze używaj cytowanych rozszerzeń. To jest odpowiedź. Twoja funkcja 'log1' jest po prostu niepoprawna. 'log2' jest poprawną formą. –
Dokładniej, zawsze cytuj '$ @'. (Są przypadki narożne, w których możesz celowo zostawić '$ *' lub inne parametry nie cytowane, ale '$ @' * istnieje * do cytowania, poza tym jest identyczne z '$ *'.) – chepner