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 0do 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 0do 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 echojeszcze 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 echopo 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 5i 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 bashobjaśnieniu nullglobopcji, 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 failglobaby uzyskać bardziej przydatne zachowanie podobne do współczesnych powłok, takich jak zshlub fish.
failglob. nullglobmoże powodować nieoczekiwane problemy, np. podczas wklejania adresu URL, który ma ?.
nullglobaby 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, echolub 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 echonigdy nie widzi [[:digit:]], tylko widzi 1 3. Możesz to zobaczyć w działaniu poprzez uruchomienie, set -xktóre wypisze uruchomione polecenia (uruchom, set +xaby je ponownie wyłączyć).
echonie 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