Moje pytanie pochodzi z tego, w jaki sposób przechowywanie wyrażenia regularnego w zmiennej powłoki pozwala uniknąć problemów z cytowaniem znaków specjalnych dla powłoki? .
Dlaczego występuje błąd:
$ [[ $a = a|b ]] bash: syntax error in conditional expression: unexpected token `|' bash: syntax error near `|b'
Wewnątrz
[[ ... ]]
drugiego operandu=
ma być wzór globowania.Czy
a|b
nie jest prawidłowym wzorcem globowania? Czy możesz wskazać, która reguła składniowa narusza tę zasadę?Niektóre komentarze poniżej wskazują, że
|
jest interpretowane jako potok.Następnie zmiana
=
na wzorzec globalny na wzorzec=~
regex|
działa$ [[ $a =~ a|b ]]
Nauczyłem się z Learning Bash p180 w moim poprzednim poście, który
|
jest rozpoznawany jako potok na początku interpretacji, nawet przed innymi etapami interpretacji (w tym parsowaniem wyrażeń warunkowych w przykładach). Jak więc można|
rozpoznać jako operator wyrażenia regularnego podczas używania=~
, bez rozpoznawania go jako potoku podczas nieprawidłowego użycia, tak jak podczas używania=
? To sprawia, że myślę, że błąd składniowy w części 1 nie oznacza, że|
jest interpretowany jako potok.Każdy wiersz odczytywany przez powłokę ze standardowego wejścia lub skryptu jest nazywany potokiem; zawiera jedno lub więcej poleceń oddzielonych od zera lub więcej znaków potoku (|). Dla każdego czytanego potoku powłoka dzieli go na polecenia, konfiguruje operacje wejścia / wyjścia dla potoku, a następnie wykonuje następujące czynności dla każdego polecenia (Rysunek 7-1):
Dzięki.
|
jest specjalne) jest domyślnie włączone po prawej stronie[[ $var = $pattern ]]
. Interesujące byłoby wyodrębnienie wersji ishopt
konfiguracji opcji, w których takie zachowanie jest widoczne - jeśli tylko te, w którychextglob
jest włączony, domyślnie lub jawnie, to dobrze.pattern='a|b'
a następnie rozwiń bez$pattern
cudzysłowu na RHS.Odpowiedzi:
Nie ma żadnego dobrego powodu
Powinien zgłosić błąd zamiast sprawdzać, czy $ a jest
a|b
ciągiem, a[[ $a =~ a|b ]]
nie zwraca błędu.Jedynym powodem jest to, że
|
ogólnie (na zewnątrz i wewnątrz[[ ... ]]
) jest znakiem specjalnym. W tej[[ $a =
pozycjibash
oczekuje typu tokena, który jest normalnym SŁOWEM, takim jak argumenty lub cele przekierowań w normalnym wierszu poleceń powłoki (ale tak, jakbyextglob
opcja była włączona od wersji bash 4.1).( WORD tutaj odnoszę się do słowa w hipotetycznej gramatyce powłoki, takiej jak opisana w specyfikacji POSIX , to znaczy, że powłoka byłaby analizowana jako jeden token w prostej linii poleceń powłoki, a nie inna definicja słów takich jak angielski jedna z sekwencji liter lub ciąg znaków niespacyjnych.
foo"bar baz"
,$(echo x y)
są dwoma takimi WORD s).W normalnym wierszu poleceń powłoki:
Jest
echo a
przesyłany dob
.a|b
nie jest WORD , to trzy tokeny:a
WORD ,|
token i tokenb
WORD .W przypadku zastosowania w
[[ $a = a|b ]]
,bash
oczekuje WORD , który nie może (a
), ale potem stwierdza nieoczekiwany|
znak, który powoduje błąd.Co ciekawe,
bash
nie narzeka na:Ponieważ jest to teraz
a
token, po którym następuje||
tokenb
, więc jest on przetwarzany w ten sam sposób jak:Który testuje że
$a
jesta
albo żeb
ciąg jest niepusty.Teraz w:
bash
nie może mieć tej samej reguły analizowania. Posiadanie tej samej reguły analizowania oznaczałoby, że powyższe spowodowałoby błąd i należałoby zacytować to,|
aby upewnić się, żea|b
jest to jedno SŁOWO . Ale od wersji Bash 3.2, jeśli to zrobisz:To już nie pasuje do
a|b
wyrażenia regularnego, ale doa\|b
wyrażenia regularnego. Oznacza to, że cytowanie powłoki ma efekt uboczny usuwania specjalnego znaczenia operatorów wyrażeń regularnych. Jest to cecha, więc zachowanie jest podobne do tego[[ $a = "?" ]]
, ale wzory wieloznaczne (używane w[[ $a = pattern ]]
) są SŁOWAMI powłoki (na przykład używanymi w globach), a wyrażenia regularne nie.Więc
bash
musi traktować wszystkich rozszerzonych operatorów wyrażeń regularnych, które są normalnie inaczej znaki specjalne powłoki, takie jak|
,(
,)
inaczej podczas analizowania argument=~
operatora.Pamiętaj jednak, że podczas gdy
teraz działa
nie. Potrzebujesz:
Który w poprzednich wersjach
bash
niepoprawnie pasowałby do odwrotnego ukośnika. Ten został naprawiony, aleCzy nie zgadza się na backslashem tak jak powinien na przykład. Bo
bash
nie zdaje sobie sprawy, że)
jest w nawiasie, więc ucieka)
się doprowadzić do[^]\)]
regexp, który pasuje na dowolnym charakterze, ale]
,\
i)
.ksh93
ma znacznie gorsze błędy na tym froncie.W
zsh
, jest to normalne shell słowo, które jest oczekiwane i cytowanie operatory regexp nie wpływa na znaczenie operatorów regexp.a|b
Pasuje do wyrażenia regularnego.Oznacza to, że
=~
można również dodać do polecenia[
/test
:(również działają w
yash
.=~
Należy podać w tym miejscu,zsh
ponieważ=something
jest tam specjalny operator powłoki).bash 3.1 zachowywał się jak
zsh
. Zmieniło się w 3.2, prawdopodobnie, aby wyrównaćksh93
(choćbash
była to powłoka, która jako pierwsza wymyśliła[[ =~ ]]
), ale nadal możesz zrobićBASH_COMPAT=31
lubshopt -s compat31
powrócić do poprzedniego zachowania (z wyjątkiem tego, że chociaż[[ $a =~ a|b ]]
zwróci błąd wbash
3.1, to już nie jest wbash -O compat31
nowszych wersjachbash
).Mam nadzieję, że to wyjaśnia, dlaczego powiedziałem, że reguły są mylące i dlaczego używam:
pomaga w tym z przenośnością do innych powłok.
źródło
[[ $a = a|b ]]
.a|b
Nie jest to skorupa WORD tutaj, to jesta
,|
ab
żeton. Podobnieecho a|b
jak nie wypisujea|b
ani nie rozwijaa|b
globu, musisz to zacytować,|
ponieważ jest to specjalny znak powłoki, który jest nieprawidłowy w tym kontekście.[[ $a = (a|b) ]]
działałby takecho (a|b)
, jak działałby jak(a|b)
operator wieloznaczny Zsh.Standardowe globs ( "rozszerzania nazw") są:
*
,?
i[ ... ]
.|
nie jest poprawnym operatorem globalnym w standardowych ustawieniach (innych niż extglob).Próbować:
źródło
|
zinterpretowany dosłownie? Dlaczego występuje błąd składniowy?|
operator globalny nie jest, więc nie jest|
interpretowany dosłownie bez cytowania? Dlaczego więc występuje błąd składniowy?|
jest postacią kontrolną; nigdy nie jest traktowany jako dosłowny znak w taki sam sposób, jak litera lub cyfra.[[ $a = a
nie jest prawidłowym poleceniem, którego dane wyjściowe mogą być przesyłane do innego procesu (przynajmniej tak myślała powłoka, którą próbujesz wykonać).Jeśli chcesz dopasować wyrażenie regularne, test powinien wyglądać następująco:
źródło