Gdy nie są cytowane $*
i $@
są takie same. Nie powinieneś używać żadnego z nich, ponieważ mogą one niespodziewanie ulec uszkodzeniu, gdy tylko pojawią się argumenty zawierające spacje lub symbole wieloznaczne.
"$*"
rozwija się do jednego słowa "$1c$2c..."
. Zazwyczaj c
jest to spacja, ale tak naprawdę to pierwsza postać IFS
, więc może to być dowolna wybrana przez Ciebie postać .
Jedyne dobre zastosowanie, jakie kiedykolwiek znalazłem, to:
połącz argumenty przecinkiem (wersja prosta)
join1() {
typeset IFS=,
echo "$*"
}
join1 a b c # => a,b,c
łączyć argumenty z określonym ogranicznikiem (lepsza wersja)
join2() {
typeset IFS=$1 # typeset makes a local variable in ksh (see footnote)
shift
echo "$*"
}
join2 + a b c # => a+b+c
"$@"
rozwija się do oddzielnych słów: "$1"
"$2"
...
To jest prawie zawsze to, czego chcesz. Rozwija każdy parametr pozycyjny do osobnego słowa, co czyni go idealnym do przyjmowania argumentów wiersza poleceń lub funkcji, a następnie przekazywania ich do innego polecenia lub funkcji. A ponieważ rozszerza się za pomocą podwójnych cudzysłowów, oznacza to, że rzeczy się nie psują, jeśli, powiedzmy, "$1"
zawiera spację lub gwiazdkę ( *
).
Załóżmy napisać skrypt o nazwie svim
, która biegnie vim
z sudo
. Zrobimy trzy wersje, aby zilustrować różnicę.
svim1
#!/bin/sh
sudo vim $*
svim2
#!/bin/sh
sudo vim "$*"
svim3
#!/bin/sh
sudo vim "$@"
Wszystkie będą odpowiednie dla prostych przypadków, np. Pojedyncza nazwa pliku, która nie zawiera spacji:
svim1 foo.txt # == sudo vim foo.txt
svim2 foo.txt # == sudo vim "foo.txt"
svim2 foo.txt # == sudo vim "foo.txt"
Ale tylko $*
i "$@"
działa poprawnie, jeśli masz wiele argumentów.
svim1 foo.txt bar.txt # == sudo vim foo.txt bar.txt
svim2 foo.txt bar.txt # == sudo vim "foo.txt bar.txt" # one file name!
svim3 foo.txt bar.txt # == sudo vim "foo.txt" "bar.txt"
I tylko "$*"
i "$@"
działać prawidłowo, jeśli masz argumenty zawierające spacje.
svim1 "shopping list.txt" # == sudo vim shopping list.txt # two file names!
svim2 "shopping list.txt" # == sudo vim "shopping list.txt"
svim3 "shopping list.txt" # == sudo vim "shopping list.txt"
Więc tylko "$@"
będzie działał poprawnie cały czas.
typeset
jest jak utworzyć zmienną lokalną w ksh
( bash
i zamiast tego ash
użyć local
). Oznacza to IFS
, że po przywróceniu funkcji zostanie przywrócona poprzednia wartość. Jest to ważne, ponieważ polecenia uruchamiane później mogą nie działać poprawnie, jeśli IFS
są ustawione na coś niestandardowego.
$*
. Zawsze uważałem to za całkowicie bezużyteczne ... połączenie z separatorem to dobry przypadek użycia.Krótka odpowiedź: użyj
"$@"
(zwróć uwagę na podwójne cudzysłowy). Inne formy są bardzo rzadko przydatne."$@"
jest raczej dziwną składnią. Jest on zastępowany przez wszystkie parametry pozycyjne, jako osobne pola. Jeśli nie ma parametrów pozycyjnych ($#
wynosi 0), wówczas"$@"
rozwija się do zera (nie jest to pusty ciąg, ale lista z 0 elementami), jeśli istnieje jeden parametr pozycyjny, to"$@"
jest równoważny"$1"
, jeśli są dwa parametry pozycyjne, to"$@"
jest równoważny z"$1" "$2"
itd."$@"
pozwala przekazać argumenty skryptu lub funkcji do innego polecenia. Jest to bardzo przydatne w przypadku opakowań, które wykonują takie czynności, jak ustawianie zmiennych środowiskowych, przygotowywanie plików danych itp. Przed wywołaniem polecenia z tymi samymi argumentami i opcjami, które zostały wywołane przez opakowanie.Na przykład poniższa funkcja filtruje dane wyjściowe
cvs -nq update
. Oprócz filtrowania danych wyjściowych i statusu zwracanego (który magrep
raczej status niż zamiastcvs
), wywoływaniecvssm
niektórych argumentów zachowuje się jak wywoływaniecvs -nq update
z tymi argumentami."$@"
rozwija się do listy parametrów pozycyjnych. W powłokach obsługujących tablice istnieje podobna składnia, aby rozwinąć listę elementów tablicy:"${array[@]}"
(nawiasy klamrowe są obowiązkowe oprócz zsh). Ponownie, podwójne cudzysłowy są nieco mylące: chronią przed podziałem pola i generowaniem wzorca elementów tablicy, ale każdy element tablicy kończy się na swoim własnym polu.Niektóre starożytne powłoki miały coś, co jest prawdopodobnie błędem: gdy nie było argumentów pozycyjnych,
"$@"
rozszerzono je do pojedynczego pola zawierającego pusty ciąg, zamiast do żadnego pola. Doprowadziło to do obejścia${1+"$@"}
( rozsławionego za pomocą dokumentacji Perla ). Wpływa to tylko na starsze wersje rzeczywistej powłoki Bourne'a i implementacji OSF1, a żadna z jej nowoczesnych kompatybilnych zamienników (ash, ksh, bash,…) nie jest zagrożona./bin/sh
nie ma wpływu na żaden system, który został wydany w XXI wieku, o którym wiem (chyba że policzysz wersję aktualizacyjną Tru64, a nawet/usr/xpg4/bin/sh
jest bezpieczny, więc#!/bin/sh
dotyczy to tylko skryptu, a nie#!/usr/bin/env sh
skryptów, o ile twoja ŚCIEŻKA jest skonfigurowana pod kątem zgodności z POSIX) . Krótko mówiąc, jest to historyczna anegdota, o którą nie musisz się martwić."$*"
zawsze rozwija się do jednego słowa. To słowo zawiera parametry pozycyjne, połączone ze spacją pomiędzy nimi. (Mówiąc bardziej ogólnie, separator jest pierwszym znakiem wartościIFS
zmiennej. Jeśli wartośćIFS
jest pustym łańcuchem, separator jest pustym łańcuchem.) Jeśli nie ma parametrów pozycyjnych, to"$*"
jest to pusty ciąg, jeśli są dwa parametry pozycyjne, aIFS
ich wartość domyślna"$*"
jest wówczas równoważna"$1 $2"
itp.$@
a$*
cytaty zewnętrzne są równoważne. Rozwijają się do listy parametrów pozycyjnych, jako oddzielne pola, takie jak"$@"
; ale każde wynikowe pole jest następnie dzielone na osobne pola, które są traktowane jak wzorce symboli wieloznacznych nazw plików, jak zwykle z niecytowanymi rozszerzeniami zmiennych.Na przykład jeśli bieżący katalog zawiera trzy pliki
bar
,baz
afoo
następnie:źródło
"$@"
rzeczywiście rozwinął się do listy zawierającej pusty ciąg: unix.stackexchange.com/questions/68484/…Oto prosty skrypt pokazuje różnicę między
$*
i$@
:Wynik:
W składni tablicowej nie ma różnicy przy użyciu
$*
lub$@
. Ma to sens tylko wtedy, gdy używasz ich z podwójnymi cudzysłowami"$*"
i"$@"
.źródło
IFS="^${IFS}"
?IFS
.IFS="^xxxxx"
by to zrobił? Końcowy${IFS}
sufiks sprawił, że pomyślałem, że robisz coś trudniejszego, jak na przykład automatyczne odzyskanie oryginalnego IFS na końcu (np. Pierwszy znak automatycznie się zmienił lub coś takiego).Podany kod da ten sam wynik. Aby to lepiej zrozumieć, spróbuj tego:
Wynik powinien być teraz inny. Oto, co otrzymuję:
To działało dla mnie
bash
. O ile mi wiadomo, ksh nie powinien się bardzo różnić. Zasadniczo cytowanie$*
potraktuje wszystko jako jedno słowo, a cytowanie$@
potraktuje listę jako osobne słowa, jak widać w powyższym przykładzie.Jako przykład użycia
IFS
zmiennej z$*
, rozważ toOtrzymuję to w wyniku:
Poza tym właśnie potwierdziłem, że działa tak samo
ksh
. Zarówno testowane tutaj, jakbash
iksh
pod OSX, ale nie widzę, jak to by miało znaczenie.źródło
unset IFS
na końcu, aby zresetować go do oryginału, ale nie działało to dla mnie bez problemu i dzięki temu otrzymałemecho $IFS
standardowe wyjście, które z niego otrzymuję. Ustawienie zaIFS
pomocą nawiasów klamrowych wprowadza nowy zakres, więc jeśli go nie wyeksportujesz, nie wpłynie to na zewnątrzIFS
.echo $IFS
nic nie dowodzi, ponieważ powłoka widzi,
, ale potem dzieli słowa za pomocąIFS
! Spróbowaćecho "$IFS"
.IFS
powinno rozwiązać ten problem.IFS
że miał inną niestandardową wartość przed wywołaniem funkcji. Ale tak, przez większość czasu niepokojące IFS będzie działać.Różnica jest ważna przy pisaniu skryptów, które powinny prawidłowo używać parametrów pozycyjnych ...
Wyobraź sobie następujące połączenie:
Oto tylko 4 parametry:
W moim przypadku
myuseradd
jest to tylko opakowanie,useradd
które akceptuje te same parametry, ale dodaje limit dla użytkownika:Zwróć uwagę na połączenie
useradd "$@"
z$@
cytowanym. Spowoduje to przestrzeganie parametrów i wysyłanie ich takimi, jakimi sąuseradd
. Gdyby cofnąć cudzysłowy$@
(lub użyć$*
również cudzysłowu), useradd zobaczyłby 5 parametrów, ponieważ trzeci parametr zawierający spację zostałby podzielony na dwa:(i odwrotnie, jeśli były w użyciu
"$*"
, useradd zobaczy tylko jeden parametr:-m -c Carlos Campderrós ccampderros
)Krótko mówiąc, jeśli chcesz pracować z parametrami odnoszącymi się do parametrów wielowyrazowych, użyj
"$@"
.źródło
// man bash . to ksh, afair, podobne zachowanie.
źródło
Mówiąc o różnicach między
zsh
ibash
:Z cytatów wokół
$@
i$*
,zsh
ibash
zachowują się tak samo, i myślę, że wynik jest dość standardowy wśród wszystkich muszli:Bez cudzysłowu wyniki są takie same dla
$*
i$@
, ale różne dlabash
i dlazsh
. W tym przypadkuzsh
pokazuje dziwne zachowanie:(Zsh zwykle nie dzieli danych tekstowych za pomocą IFS, chyba że jest to wyraźnie wymagane, ale zauważ, że tutaj nieoczekiwanie brakuje pustego argumentu na liście).
źródło
$@
nie jest wyjątkowy pod tym względem:$x
rozwija się do co najwyżej jednego słowa, ale puste zmienne rozwijają się do zera (nie jest pustym słowem). Spróbujprint -l a $foo b
zfoo
pustym lub niezdefiniowanym.Jedna z odpowiedzi mówi
$*
(co uważam za „ikonę”) rzadko jest przydatna.Przeszukuję google za pomocą
G() { IFS='+' ; w3m "https://encrypted.google.com/search?q=$*" ; }
Ponieważ adresy URL są często dzielone z
+
, ale moja klawiatura sprawia, żełatwiej osiągnąć niż
+
,$*
+$IFS
czują się opłaca.źródło