W jaki sposób bash rozróżnia rozwijanie nawiasów i grupowanie poleceń?

48

Zauważyłem, że {można to wykorzystać w rozszerzaniu nawiasów:

echo {1..8}

lub w grupie poleceń:

{ls;echo hi}

Skąd Bash zna różnicę?

uwielbia wiosnę
źródło
1
Doskonałe pytanie, +1. Wygląda na to, że może to być {interpretowane jako lista poleceń, jeśli pojawia się na początku polecenia i jako rozszerzenie nawiasów klamrowych, ale nie jestem pewien.
Celada,
16
{ls;echo hi}nie jest legalne bash. Potrzebujesz spacji po otwierającym nawiasie i średnikiem przed zamykającym.
PSkocik,

Odpowiedzi:

39

Uproszczona powodem jest istnienie jednego znaku: space.

Rozszerzenia nawiasów nie przetwarzają (niecytowanych) spacji.

{...}Lista potrzebuje (UN-cytowany) przestrzenie.

Bardziej szczegółową odpowiedzią jest sposób, w jaki powłoka analizuje wiersz poleceń .


Pierwszym krokiem do przeanalizowania (zrozumienia) wiersza polecenia jest podzielenie go na części.
Te części (zwykle nazywane słowami lub tokenami) wynikają z dzielenia wiersza polecenia na każdym metaznaku z łącza :

  1. Dzieli polecenie na tokeny oddzielone stałym zestawem metaznaków: SPACJA, TAB, NEWLINE,;, (,), <,>, | i &. Typy tokenów obejmują słowa, słowa kluczowe, przekierowania we / wy i średniki.

Metaznaki: spacetabenter;,<>|i &.

Po podziale słowa mogą być typu (w rozumieniu powłoki):

  • Wstępne przypisania poleceń: LC=ALL ...
  • Komenda LC=ALL echo
  • Argumenty LC=ALL echo "hello"
  • Przekierowanie LC=ALL echo "hello" >&2

Rozszerzenie klamry

Tylko jeśli „ciąg nawiasu klamrowego” (bez spacji lub metaznaków) jest pojedynczym słowem (jak opisano powyżej) i nie jest cytowany , jest kandydatem do „rozwinięcia nawiasu klamrowego ”. Więcej kontroli zostanie przeprowadzonych później w strukturze wewnętrznej.

Zatem to: {ls,-l}kwalifikuje się jako „Rozwinięcie nawiasu klamrowego”, aby stać się ls -lalbo jako first wordalbo argument(w bash, zsh jest inny).

$ {ls,-l}            ### executes `ls -l`
$ echo {ls,-l}       ### prints `ls -l`

Ale to nie będzie: {ls ,-l}. Bash podzieli się spacei parsuje linię jako dwa słowa: {lsi ,-l}który wywoła command not found(argument ,-l}zostanie utracony):

 $ {ls ,-l}
 bash: {ls: command not found

Twoja linia: {ls;echo hi}nie stanie się „rozszerzeniem nawiasu klamrowego” z powodu dwóch meta-znaków ;i space.

Zostanie on podzielony na trzy części tego: {lsnowe polecenie: echo hi}. Zrozum, że ;wyzwala uruchomienie nowego polecenia. Polecenie {lsnie zostanie odnalezione, a następne polecenie zostanie wydrukowane hi}:

$ {ls;echo hi}
bash: {ls: command not found
hi}

Jeśli zostanie umieszczony po innym poleceniu, i tak uruchomi nowe polecenie po ;:

$ echo {ls;echo hi}
{ls
hi}

Lista

Jednym z poleceń „związek” jest „Brace List” (moje słowa) { list; }.
Jak widać, jest on zdefiniowany spacjami i zamykaniem ;.
Przestrzenie i ;są potrzebne, ponieważ oba {i }są „zarezerwowane Words ”.

Dlatego, aby zostać rozpoznanym jako słowa, muszą być otoczone meta-znakami (prawie zawsze:) space.

Jak opisano w punkcie 2 połączonej strony

  1. Sprawdza pierwszy token każdego polecenia, aby sprawdzić, czy jest to ...., {lub (, wówczas polecenie jest w rzeczywistości poleceniem złożonym.

Twój przykład: {ls;echo hi}nie jest listą.

Potrzebuje zamknięcia ;i jednej spacji (przynajmniej) po {. Ostatni }jest określony przez zamknięcie ;.

To jest lista { ls;echo hi; }. I to { ls;echo hi;}jest również (rzadziej używane, ale ważne) (Dzięki @choroba za pomoc).

$ { ls;echo hi; }
A-list-of-files
hi

Ale jako argument (powłoka zna różnicę) dla polecenia, wywołuje błąd:

$ echo { ls;echo hi; }
bash: syntax error near unexpected token `}'

Uważaj jednak na to, co według Ciebie jest przetwarzane przez powłokę:

$ echo { ls;echo hi;
{ ls
hi

źródło
2
to naprawdę najlepsza odpowiedź, ponieważ naprawdę dajesz nam, jak działa parser bash! i ze szczegółowym wyjaśnieniem!
lovespring 11.01.16
2
Nie potrzebujesz odstępu między ;i }. { ls;}działa, ponieważ średnik jest już meta-znakiem.
choroba
1
@lovespring Dzięki, tak, poświęciłem trochę czasu na napisanie tego. Cieszę się, że jest to przydatne. Jeszcze raz dzięki.
świetny artykuł, wielkie dzięki za referencje
Edward Torvalds
16

Blok {jest słowem kluczowym powłoki, więc musi być oddzielony spacją od następnego słowa, podczas gdy podczas rozwijania nawiasów klamrowych nie powinno być spacji (jeśli chcesz nawiasować klamrę, spacja, musisz uciec:) echo {\ ,a}{b,c}.

Możesz użyć rozwinięcia nawiasu na początku polecenia:

{ls,.}  # expands to "ls ."

Nie można go jednak użyć do rozwinięcia do bloku, ponieważ parsowanie poleceń grupowania odbywa się przed rozwinięciami:

echo {'{ ls','.;}'}  # { ls .;}
{'{ ls','.;}'}       # bash: { ls: No such file or directory
choroba
źródło
5

Wie, sprawdzając składnię wiersza poleceń. W ten sam sposób wie, że w wyrażeniu echo echopierwsze echo powinno być traktowane jako polecenie, a drugie echo jako parametr pierwszego echa.

W bash jest to bardzo proste, ponieważ { cmd; }powinno mieć spacje i średniki. Jednak na przykład w Zsh nie są one potrzebne, ale nadal analizując kontekst {}powłoki, jest w stanie stwierdzić, co należy zrobić z jej zawartością.

Rozważ następujące:

alias 1..3=date
{ 1..3; }    #in bash
{1..3}       #in zsh

Obie zwracają aktualną datę, ale

echo {1..3}

zwraca, 1 2 3ponieważ shell zna {}argument dla polecenia echo, dlatego należy go rozwinąć.

jimmij
źródło
{po którym następuje niecytowana spacja, nie rozpoczyna rozwijania nawiasu klamrowego w bash
choroba
@choroba Tak i nie tylko zaraz po {. Nigdzie nie podano miejsca, ponieważ powłoka dzieli cały wiersz poleceń na spacje.
jimmij
0

Po pierwsze, nawias klamrowy złożony musi być samym słowem i pierwszym słowem wiersza poleceń:

echo { these braces are just words }

Po drugie, poszczególne nawiasy klamrowe nie są wyjątkowe (jak widać powyżej). Puste nawiasy klamrowe również nie są wyjątkowe:

echo {} # just the token {}: familiar from the find command

Wszystko bez przecinków jest również po prostu sobą

echo {abc} # just {abc}

Tutaj zaczyna się akcja.

echo {a,b} # braces disappear, a b results.

Zasadniczo więc, aby rozwinąć się nawias klamrowy, potrzebujemy jednego słowa (niepodzielonego na pola na białych znakach), wewnątrz którego występuje co najmniej jeden przypadek, {...}w którym występuje co najmniej jeden przecinek.

Nawiasem mówiąc, może to być pierwsze słowo w wierszu poleceń:

{ls,-l} .   # just "ls -l ."
Kaz
źródło