Chciałbym zapytać:
Dlaczego jest echo {1,2,3}
rozwinięty do 1 2 3, co jest oczekiwanym zachowaniem, podczas gdy echo [[:digit:]]
zwraca , podczas [[:digit:]]
gdy spodziewałem się, że wydrukuje wszystkie cyfry od 0
do 9
?
Chciałbym zapytać:
Dlaczego jest echo {1,2,3}
rozwinięty do 1 2 3, co jest oczekiwanym zachowaniem, podczas gdy echo [[:digit:]]
zwraca , podczas [[:digit:]]
gdy spodziewałem się, że wydrukuje wszystkie cyfry od 0
do 9
?
Odpowiedzi:
Ponieważ są to dwie różne rzeczy. {1,2,3}
Jest przykładem ekspansji usztywniającym . {1,2,3}
Konstrukt jest rozszerzony przez powłokę , zanim echo
jeszcze widzi. Możesz zobaczyć, co się stanie, jeśli użyjesz set -x
:
$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3
Jak widać, polecenie echo {1,2,3}
jest rozwinięte do:
echo 1 2 3
Jednak [[:digit:]]
jest klasa znaków POSIX . Gdy go podasz echo
, powłoka najpierw przetwarza go, ale tym razem jest przetwarzana jako glob powłoki . działa tak samo jak podczas uruchamiania, echo *
który wydrukuje wszystkie pliki w bieżącym katalogu. Ale [[:digit:]]
jest globem powłoki, który będzie pasował do dowolnej cyfry. Teraz, bash, jeśli glob powłoki nie pasuje do niczego, zostanie rozwinięty do siebie:
$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files
Jeśli glob pasuje do czegoś, co zostanie wydrukowane:
$ echo /e*c
+ echo /etc
/etc
W obu przypadkach echo
po prostu wypisuje wszystko, co powłoka nakazuje wydrukować, ale w drugim przypadku, ponieważ glob pasuje do czegoś ( /etc
), poleca się wydrukować coś.
Ponieważ nie masz żadnych plików ani katalogów, których nazwa składa się z dokładnie jednej cyfry (co [[:digit:]]
by się zgadzało), glob rozszerza się do siebie i otrzymujesz:
$ echo [[:digit:]]
[[:digit:]]
Teraz spróbuj utworzyć plik o nazwie 5
i uruchomić to samo polecenie:
$ echo [[:digit:]]
5
A jeśli istnieje więcej niż jeden pasujący plik:
$ touch 1 5
$ echo [[:digit:]]
1 5
Jest to (w pewnym sensie) udokumentowane w man bash
objaśnieniu nullglob
opcji, które wyłączają to zachowanie:
nullglob
If set, bash allows patterns which match no files (see
Pathname Expansion above) to expand to a null string,
rather than themselves.
Jeśli ustawisz tę opcję:
$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]] ## prints nothing
$
shopt -s failglob
aby uzyskać bardziej przydatne zachowanie podobne do współczesnych powłok, takich jak zsh
lub fish
.
failglob
. nullglob
może powodować nieoczekiwane problemy, np. podczas wklejania adresu URL, który ma ?
.
nullglob
aby pokazać, że wzór jest interpretowany przez powłokę jako glob.
{1,2,3}
jest rozwinięciem nawiasów , rozszerza się na wyrazy wymienione bez względu na ich znaczenie.
[...]
to grupa znaków, używana w rozszerzaniu nazw plików (lub symbolach wieloznacznych lub globalnych) podobnie jak gwiazdka *
i znak zapytania ?
. Pasuje do każdego pojedynczego znaku na liście lub znaków należących do nazwanych grup, takich jak [:digit:]
te, które są na liście. Domyślnym zachowaniem większości powłok jest pozostawienie znaku wieloznacznego bez zmian, jeśli nie ma pasujących plików.
(Zauważ, że tak naprawdę nie można przekształcić symbolu wieloznacznego / wzoru w zestaw pasujących łańcuchów. Gwiazdka może pasować do dowolnego łańcucha o dowolnej długości, więc rozwinięcie dowolnego zawierającego go wzoru spowodowałoby nieskończoną listę łańcuchów.)
Więc:
$ bash -c 'echo [[:digit:]]' # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]' # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]' # now there are two matches
1 3 # note that d, i, g and t do NOT match
Ale nadal:
$ bash -c 'echo {1,2,3}'
1 2 3
Oba są rozszerzone przez powłokę , nie ma znaczenia, czy uruchomione polecenie to ls
, echo
lub rm
. Pamiętaj też, że jeśli którykolwiek z nich jest cytowany, nie zostaną one rozwinięte:
$ bash -c 'echo "[[:digit:]]"' # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
[[:digit:]]
przed przekazaniem jej echo
, więc echo
nigdy nie widzi [[:digit:]]
, tylko widzi 1 3
. Możesz to zobaczyć w działaniu poprzez uruchomienie, set -x
które wypisze uruchomione polecenia (uruchom, set +x
aby je ponownie wyłączyć).
echo
nie szuka plików, powłoka robi to przed uruchomieniem echo
.
{1,2,3}
(i np. {1..3}
są rozszerzeniami nawiasów klamrowych . Są one interpretowane przez powłokę przed wykonaniem polecenia.
[[:digit:]]
jest tokenem pasującym do wzorca , ale nie używasz go w lokalizacji z plikami pasującymi do tego wzorca. Jeśli użyjesz dopasowania wzorca, które nie ma dopasowań, rozwija się ono do siebie:
$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3