Dlaczego w ksh wymagana jest spacja między „[[” a „-e xxx”?

9

Na przykład następujące polecenie nie działa:

if [[-e xyz]]; then echo File exists;fi

ksh daje następujący błąd

[[-e: command not found

Czy to dlatego, że „[[-” jest niejednoznaczny?

AcBap
źródło
2
[[jest słowem kluczowym - przypuszczalnie parsowanie drzewa powłoki wymaga, aby słowa kluczowe zostały określone przez spacje
steeldriver
Innym sposobem na to jest prawdopodobnie napisanie funkcji [[-, skąd powłoka wiedziałaby, że parsuje "zrobić flagę e na [[" zamiast "wykonać funkcję [[- na e").
pbhj

Odpowiedzi:

15

Najprostszym wyjaśnieniem byłoby, ponieważ w instrukcji wydaje się, [[ expression ]]więc nie musi być przestrzeń pomiędzy [[a expressioni zamykanie ]]. Ale oczywiście możemy spróbować przyjrzeć się temu bardziej szczegółowo. Muszle mają nieco złożoną gramatykę i polegają w dużej mierze na koncepcji „dzielenia słów”. W szczególności instrukcja ksh (1) stwierdza:

Powłoka zaczyna analizować dane wejściowe, dzieląc je na słowa. Słowa, które są sekwencjami znaków, są rozdzielane niecytowanymi białymi znakami (spacja, tabulator i nowa linia) lub metaznakami (<,>, |,;, &, (i)). Oprócz ograniczania słów spacje i tabulatory są ignorowane, podczas gdy znaki nowej linii zwykle ograniczają polecenia.

Tak jak podano w instrukcji, sekwencja znaków [[-ejest uważana za słowo powłoki. kshszukałby takiego polecenia na liście wbudowanych i operatorów specjalnych (takich jak forlub while), a następnie szukał polecenia zewnętrznego - i voilà - nie znaleziono, stąd pojawia się komunikat o błędzie dotyczący polecenia nie znalezionego.

W tym przypadku [[nie są też specjalnymi metaznakami. Gdyby tak było, ta -eczęść [[-ebyłaby uważana za słowo powłoki, a nie argument [[sam w sobie. Jest jednak [[opisany jako polecenie złożone, a instrukcja mówi:

Polecenia złożone są tworzone przy użyciu następujących słów zastrzeżonych - słowa te są rozpoznawane tylko wtedy, gdy nie są cytowane i jeśli są używane jako pierwsze słowo polecenia (tzn. Nie mogą być poprzedzone przypisaniem parametrów ani przekierowaniem):

Aby więc [[mógł zostać rozpoznany jako polecenie złożone, musi być pierwszym słowem polecenia lub listy poleceń, co zgodnie z poprzednią definicją „słowa” oznacza, że ​​muszą być one oddzielone spacjami, tabulatorami lub znakami nowej linii od innych słowa / argumenty.


W komentarzach zapytałeś : „Dlaczego parser nie może zatrzymać się, gdy tylko znajdzie wzorzec” [[”, i traktuje to jako początek polecenia warunkowego?” Krótka odpowiedź jest prawdopodobnie spowodowana 1) wpływem [składni, ponieważ pierwotnie była to komenda zewnętrzna, a dla POSIX standardowa zgodność istnieje już jako komenda zewnętrzna, a 2) ponieważ parser powłoki jest tak zbudowany. Analizator składni powłoki rozpoznaje inne znaki specjalne rozdzielane spacjami: echo $((2+2))i (echo foobar)działa doskonale. Być może w przyszłości, gdy kshprogramowanie zostanie wznowione lub pojawi się widelec lub klon (jak mkshlub pdksh), ktoś zaimplementuje składnię bez spacji [[-e.

Zobacz też:

Sergiy Kolodyazhnyy
źródło
3
Myślę, że instrukcja manualna ksh naprawdę jest na miejscu. Jest podobny do każdego innego języka programowania: w C charjest słowem kluczowym, ale nadal nie można pisać charsomeletter = 'A';i oczekiwać, że parser zatrzyma się po zobaczeniu char.
Peter - Przywróć Monikę
Myślę, że główna różnica między twoim przykładem „char” a symbolem „[[” w pytaniu polega na tym, że identyfikator alfanumeryczny wymaga ogólnie separatora spacji, aby oddzielić się od innych; z drugiej strony specjalne symbole jako tokeny nie wymagają separatorów spacji.
AcBap
Dokładnie. Nie jestem pewien, czy porównanie z C jest pomocne. W C można powiedzieć i--<=++j, a kompilator nie ma problemu z analizowaniem tego jako i -- <= ++ j.
Scott
@Scott Myślę, że porównanie C jest pomocne (i że jest to rodzaj wtórny, że identyfikatory C dopuszczają tylko znaki alfanumeryczne i _). Ksh ma (jako operator i (echo hello)analizuje jako ( echo hello ). To if, {, [[, i inne słowa kluczowe , a ifx, {xi [[xkażdy jeden żeton - podobnie jak charsomeletterto jeden żeton C. W przeciwieństwie do tego, cudzysłow (xw ksh to dwa tokeny - podobnie jak i--dwa tokeny w C. To, co koncepcyjnie oznacza nawet, że jest operatorem, różni się między powłokami typu Bourne'a (jak ksh) i C. Ale Peter A. Schneider zwrócił uwagę na prawdziwe podobieństwo leksykalne .
Eliah Kagan
2

Należy zauważyć, że [jest to polecenie, a słowo kluczowe powłoki [[w bash i ksh jest oparte [.

[[jest używany w podobny sposób jak [. Czasami można nawet zastąpić [ ]z [[ ]]bez zmiany zachowań. W przypadku [, jak każdego polecenia, spacja jest obowiązkowa między poleceniem a jego argumentami. To samo dotyczy [[.

Kiedyś mieliśmy tylko /usr/bin/[. Teraz większość powłok ma [wbudowaną wydajność - ale składnia jest taka sama. W powłokach, które to zapewniają [[, działa ona jako bardziej wszechstronna alternatywa dla [.

Oto opis [w bash:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

[Jest to więc równoważne z test(poza oczekiwaniem ]argumentu na samym końcu). help testpoda jeszcze więcej szczegółów. Możesz to porównać do help [[.

Istnieje również strona podręcznika dla zewnętrznego polecenia [( man \[).

W przypadku

if [[-e
  • Tam też [nie [[ma polecenia. Pełne słowo [[-eto.
  • To if [[-esprawia, że ​​jest to test na wartość prawda / fałsz. Czy istnieje polecenie [[-e?

Czy to dlatego, że „[[-” jest niejednoznaczny?

Tak. Albo nie. [[-ejest tym, czym jest: nic, co powłoka rozumie, więc zakłada, że ​​jest to jej własne polecenie. ;-)

Rinzwind
źródło
„% help [[” mówi, że „[[” to polecenie warunkowe. Dlaczego analizator składni nie może zatrzymać się, gdy tylko znajdzie wzorzec „[[”, i traktuje to jako początek polecenia warunkowego?
AcBap
@Myloti [[-eto potencjalnie poprawna (choć dziwnie wyglądająca) nazwa polecenia.
Gordon Davisson,
2

Przestrzeń jest ogranicznikiem i jest wymagana. Jak widać z shellcheck:

$ shellcheck gmail-browse-msgs-algorithm.sh

In gmail-browse-msgs-algorithm.sh line 847:
        [[$Today != "${DaysArr[ i + DAY_DELETED_ON_NDX ]}" ]] && continue
          ^-- SC1035: You need a space after the [[ and before the ]].

(Zarówno obsługa ksh, jak i bash [[i nie działa bez spacji. Shellcheck daje dokładnie takie wyjście z podobnymi skryptami ksh i bash zawierającymi tę błędną linię.)

Dlaczego separatory są potrzebne, mają związek z tokenami i leksykonami .

WinEunuuchs2Unix
źródło
Uwaga, OP zapytał o kshshell w pytaniu. Bash pożycza [[operatora przed kshiw tej kwestii ich zachowanie jest identyczne, ale byłbym ostrożny z założeniami, jak istnieje kilka istotnych różnic kshi bashzachowań i wewnętrznych. To dlatego, że shellcheck działa na skrypcie bash, niekoniecznie może być odpowiednim narzędziem dla skryptów ksh. Tylko coś, o czym należy pamiętać zbliżając się do różnych pocisków
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Byłem oportunistyczny, używając shellcheck, aby podświetlić [[wbudowane reguły. W żaden sposób nie wywnioskowałem, że kshjest to powłoka, w której shellcheckmożna znaleźć błędy. Mam nadzieję, że inni to docenią kshi bashsą dwoma różnymi tłumaczami. Dziękuję, że o tym wspomniałeś. Warto również zauważyć [[wbudowane bash, podczas gdy [jest to polecenie zewnętrzne.
WinEunuuchs2Unix
W rzeczywistości [jest też wbudowany w bash :) manpages.ubuntu.com/manpages/bionic/man7/bash-builtins.7.html Ale nie jesteś daleko - [lub testwymagane jest, aby być zewnętrznym poleceniem. W czasach, gdy oryginalna powłoka Bourne'a [była w rzeczywistości zewnętrzną komendą i nadal istnieje, /usr/bin/[ponieważ POSIX wymaga, aby była to zewnętrzna komenda pubs.opengroup.org/onlinepubs/009695399/utilities/test.html Bardzo niewiele jest wymaganych przez POSIX do bądź wbudowany pubs.opengroup.org/onlinepubs/009695399/idx/sbi.html Wbudowany [jest dla wydajności
Sergiy Kolodyazhnyy
2
Shellcheck wie ksh; użyj hashbanga lub -s ksh. Btw, ksh i bash mają [[„wbudowane”, ale jest to słowo kluczowe , w przeciwieństwie [do wbudowanej powłoki . (ksh :; whence -v [ [[bash type [ [[:) Wbudowane i zewnętrzne polecenia są podobne pod względem składniowym. Jednym ze sposobów [[różni się od [tego, że [[tłumi pewne rozszerzenia w swoich argumentach, których nie mógł jako wbudowane - podobnie jak {grupowanie, gdyby nie było wbudowane. Być może dlatego {x(lub [[x) bycie jednym żetonem wydaje się dziwne. Myślę, że słusznie jest powiedzieć, że wbudowane i słowa kluczowe są leksykalnie, ale nie podobne pod względem składniowym . @SergiyKolodyazhnyy
Eliah Kagan
1
@EliahKagan Właściwie wysłałem pytanie do U&L, aby uzyskać dokładną odpowiedź na pytanie, co POSIX myśli o tej sytuacji unix.stackexchange.com/questions/526574/… Język powłoki jest wystarczająco złożony, więc mam nadzieję, że ktoś może to wyjaśnić w bardziej formalny sposób
Sergiy Kolodyazhnyy
1

[[może być poleceniem zewnętrznym! Oznacza to, że może to być program, a nie jakaś składnia obsługiwana bezpośrednio przez powłokę. Obsługa składni bez przestrzeni byłaby możliwa, kshale zawiodłaby w systemach z zewnętrznymi, [[więc ze względu na kompatybilność lepiej zachować wymaganą przestrzeń.

BusyBox udostępnia [[na przykład jako polecenie zewnętrzne .

DarkDust
źródło
0

Ponieważ [[-emoże uczestniczyć w rozszerzaniu powłoki (może być używany jak

echo [[-e]*

aby wymienić wszystkie pliki zaczynające się od litery pomiędzy [i ewłącznie), byłby to kompletny bałagan, jeśli [i ]były znaki specjalne, które nie uczestniczą w normalnej spacji rządzone słowo podziału.


źródło