Odpowiedzi:
KRÓTKA ODPOWIEDŹ
Jak powiedzieli inni - zawsze powinieneś cytować zmienne, aby zapobiec dziwnemu zachowaniu. Więc użyj echo "$ foo" in zamiast po prostu echo $ foo .
DŁUGA ODPOWIEDŹ
Myślę, że ten przykład zasługuje na dalsze wyjaśnienia, ponieważ dzieje się więcej, niż mogłoby się wydawać na pierwszy rzut oka.
Widzę, gdzie pojawia się twoje zamieszanie, ponieważ po uruchomieniu pierwszego przykładu prawdopodobnie pomyślałeś, że powłoka najwyraźniej robi:
A więc z pierwszego przykładu:
me$ FOO="BAR * BAR"
me$ echo $FOO
Po rozwinięciu parametrów odpowiada:
me$ echo BAR * BAR
A po rozwinięciu nazwy pliku jest równoważne:
me$ echo BAR file1 file2 file3 file4 BAR
A jeśli po prostu wpiszesz echo BAR * BAR
w wierszu poleceń, zobaczysz, że są one równoważne.
Więc prawdopodobnie pomyślałeś sobie „jeśli uniknę *, mogę zapobiec rozszerzaniu nazwy pliku”
A więc z drugiego przykładu:
me$ FOO="BAR \* BAR"
me$ echo $FOO
Po rozwinięciu parametrów powinno odpowiadać:
me$ echo BAR \* BAR
A po rozwinięciu nazwy pliku powinno być równoważne z:
me$ echo BAR \* BAR
A jeśli spróbujesz wpisać „echo BAR \ * BAR” bezpośrednio w wierszu poleceń, rzeczywiście wypisze to „BAR * BAR”, ponieważ rozwinięcie nazwy pliku jest blokowane przez znak ucieczki.
Dlaczego więc użycie $ foo nie działa?
To dlatego, że ma miejsce trzecie rozszerzenie - Quote Removal. Z ręcznego usuwania cytatów bash jest:
Po poprzednich rozszerzeniach wszystkie niecytowane wystąpienia znaków '\', '' 'i' "', które nie powstały w wyniku jednego z powyższych rozszerzeń, są usuwane.
Więc co się dzieje, kiedy wpiszesz polecenie bezpośrednio w linii poleceń, znak ucieczki nie jest wynikiem poprzedniego interpretacji, więc BASH usuwa go przed wysłaniem do polecenia echo, ale w drugim przykładzie znak "\ *" był wynik poprzedniej interpretacji parametru, więc NIE jest usuwany. W rezultacie echo otrzymuje "\ *" i to właśnie drukuje.
Zwróć uwagę na różnicę między pierwszym przykładem - „*” nie jest uwzględniona w znakach, które zostaną usunięte przez usunięcie cytatu.
Mam nadzieję, że to ma sens. Na koniec wniosek w tym samym - wystarczy użyć cudzysłowu. Pomyślałem tylko, że wyjaśnię, dlaczego ucieczka, która logicznie powinna działać, jeśli w grę wchodzi tylko rozszerzenie parametrów i nazwy pliku, nie działa.
Aby uzyskać pełne wyjaśnienie rozszerzeń BASH, zobacz:
http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions
Dodam trochę do tego starego wątku.
Zwykle używałbyś
$ echo "$FOO"
Jednak nawet z taką składnią miałem problemy. Rozważmy następujący skrypt.
#!/bin/bash
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1"
Te *
muszą być przekazywane do verbatim curl
, ale te same problemy pojawią. Powyższy przykład nie zadziała (rozwinie się do nazw plików w bieżącym katalogu) i też nie \*
. Nie możesz również cytować, $curl_opts
ponieważ zostanie to rozpoznane jako pojedyncza (nieprawidłowa) opcja curl
.
curl: option -s --noproxy * -O: is unknown
curl: try 'curl --help' or 'curl --manual' for more information
Dlatego zalecałbym użycie bash
zmiennej, $GLOBIGNORE
aby całkowicie zapobiec rozwijaniu nazwy pliku, jeśli zostanie zastosowana do wzorca globalnego, lub użycie set -f
wbudowanej flagi.
#!/bin/bash
GLOBIGNORE="*"
curl_opts="-s --noproxy * -O"
curl $curl_opts "$1" ## no filename expansion
W odniesieniu do oryginalnego przykładu:
me$ FOO="BAR * BAR"
me$ echo $FOO
BAR file1 file2 file3 file4 BAR
me$ set -f
me$ echo $FOO
BAR * BAR
me$ set +f
me$ GLOBIGNORE=*
me$ echo $FOO
BAR * BAR
SELECT * FROM etc.
, że to jedyny sposób, który działa.
FOO='BAR * BAR'
echo "$FOO"
echo "$FOO"
Warto przyzwyczaić się do używania printf
raczej niż echo
z linii poleceń.
W tym przykładzie nie daje to większych korzyści, ale może być bardziej przydatne przy bardziej złożonych wynikach.
FOO="BAR * BAR"
printf %s "$FOO"