Dostęp do wiersza poleceń bash $ @ vs $ *

327

W wielu pytaniach SO i samouczkach bash widzę, że mogę uzyskać dostęp do argumentów wiersza poleceń w skryptach bash na dwa sposoby:

$ ~ >cat testargs.sh 
#!/bin/bash

echo "you passed me" $*
echo "you passed me" $@

Co skutkuje w:

$ ~> bash testargs.sh arg1 arg2
you passed me arg1 arg2
you passed me arg1 arg2

Jaka jest różnica między $*i $@?
Kiedy należy użyć tego pierwszego, a kiedy drugiego?

Oz123
źródło
spójrz na tę odpowiedź: stackoverflow.com/a/842325/671366
kodowanie
analiza statyczna w IntelliJ traktuje echo "something $@"jako błąd
Alex Cohn

Odpowiedzi:

436

Różnica pojawia się, gdy podane są parametry specjalne. Pozwól mi zilustrować różnice:

$ set -- "arg  1" "arg  2" "arg  3"

$ for word in $*; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in $@; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in "$*"; do echo "$word"; done
arg  1 arg  2 arg  3

$ for word in "$@"; do echo "$word"; done
arg  1
arg  2
arg  3

jeszcze jeden przykład na znaczenie cytowania: zwróć uwagę, że pomiędzy „arg” a liczbą są 2 spacje, ale jeśli nie podam słowa $:

$ for word in "$@"; do echo $word; done
arg 1
arg 2
arg 3

a w bash "$@"jest „domyślną” listą do iteracji:

$ for word; do echo "$word"; done
arg  1
arg  2
arg  3
Glenn Jackman
źródło
65
+1 Zawsze myślałem, że tę koncepcję najlepiej demonstruje prosty przykład, w którym całkowicie brakuje instrukcji bash.
chepner
5
Czy istnieje możliwy przypadek użycia, kiedy $*lub "$*"może być wymagany, a cel nie może być obsługiwany przez $@lub "$@"?
anishsane
5
Która wersja jest bardziej odpowiednia dla skryptu „otoki”, w którym parametry skryptów muszą stać się parametrami nowego polecenia?
Segfault
7
@ Segfault, w tym przypadku, zawsze wybieraj "$@"z cudzysłowami.
glenn jackman
2
Ta odpowiedź zawiera użyteczne przykłady, ale lepiej byłoby, gdyby wyjaśniła także mechanizm za nimi. Dlaczego tak to działa?
Lii
255

Niezła poręczna tabela przeglądowa z Wiki Bash Hackers :

$ * w porównaniu do $ @ table

gdzie cw trzecim rzędzie znajduje się pierwszy znak $IFS, Separator pól wewnętrznych, zmienna powłoki.

Jeśli argumenty mają być przechowywane w zmiennej skryptu, a argumenty mają zawierać spacje, z całego serca polecam zastosowanie "$*"lewy z wewnętrznym separatorem pól $IFSustawionym na tab .

Serge Stroobandt
źródło
42
... gdzie „c” jest pierwszą postacią $ IFS
glenn jackman
39
... i $IFSoznacza „Internal Field Separator”.
Serge Stroobandt,
Oto przykład , który zawiera cytowane dane wejściowe. Ważny jest również wkład!
Serge Stroobandt
Powiedzmy, że chcę utworzyć skrypt opakowujący, który naśladuje funkcjonalność zawiniętego polecenia. Jakiej składni należy użyć, aby przekazać argumenty ze skryptu opakowania do komendy wewnętrznej?
Marinos An
44

$ *

Rozwija się do parametrów pozycyjnych, zaczynając od jednego. Kiedy rozwinięcie występuje w obrębie podwójnych cudzysłowów, rozwija się do pojedynczego słowa z wartością każdego parametru oddzieloną pierwszym znakiem zmiennej specjalnej IFS. Oznacza to, że „$ *” jest równoważne „$ 1c $ 2c ...”, gdzie c jest pierwszym znakiem wartości zmiennej IFS. Jeśli IFS jest rozbrojony, parametry są oddzielane spacjami. Jeśli IFS ma wartość NULL, parametry są łączone bez interwencji separatorów.

$ @

Rozwija się do parametrów pozycyjnych, zaczynając od jednego. Kiedy rozwinięcie występuje w podwójnych cudzysłowach, każdy parametr rozwija się do osobnego słowa. Oznacza to, że „$ @” jest równoważne z „$ 1” „$ 2” ... Jeśli podwójne wyrażenie występuje w jednym słowie, rozszerzenie pierwszego parametru jest łączone z początkową częścią oryginalnego słowa i rozwinięciem ostatniego parametru jest łączony z ostatnią częścią oryginalnego słowa. Gdy nie ma parametrów pozycyjnych, „$ @” i $ @ rozwijają się do zera (tzn. Są usuwane).

Źródło: Bash man

Muffo
źródło
15

$ @ jest takie samo jak $ *, ale każdy parametr jest łańcuchem cytowanym, to znaczy parametry są przekazywane w stanie nienaruszonym, bez interpretacji lub interpretacji. Oznacza to między innymi, że każdy parametr na liście argumentów jest postrzegany jako osobne słowo.

Oczywiście należy podać „$ @”.

http://tldp.org/LDP/abs/html/internalvariables.html#ARGLIST

rkosegi
źródło
1

Ten przykład pozwala wyróżnić różnice między „at” i „asterix” podczas ich używania. Zadeklarowałem dwie tablice „owoce” i „warzywa”

fruits=(apple pear plumm peach melon)            
vegetables=(carrot tomato cucumber potatoe onion)

printf "Fruits:\t%s\n" "${fruits[*]}"            
printf "Fruits:\t%s\n" "${fruits[@]}"            
echo + --------------------------------------------- +      
printf "Vegetables:\t%s\n" "${vegetables[*]}"    
printf "Vegetables:\t%s\n" "${vegetables[@]}"    

Zobacz następujący wynik powyższy kod:

Fruits: apple pear plumm peach melon
Fruits: apple
Fruits: pear
Fruits: plumm
Fruits: peach
Fruits: melon
+ --------------------------------------------- +
Vegetables: carrot tomato cucumber potatoe onion
Vegetables: carrot
Vegetables: tomato
Vegetables: cucumber
Vegetables: potatoe
Vegetables: onion
Stefanans
źródło
6
Z naukowego punktu widzenia pomidory to owoce.
Randy
1
Masz racje! „W botanice owoc jest strukturą niosącą nasiona w roślinach kwiatowych (znanych również jako okrytozalążkowe) powstających z jajnika po kwitnieniu”. en.wikipedia.org/wiki/Fruit
stefansson
@Randy: naukowo rzecz biorąc, wszystkie owoce są warzywami (to synonim nazwy „roślina”).
Cris Luengo
@CrisLuengo herezja! :)
Randy