W klasycznym scenariuszu z priorytetem operatora masz taką linię:
(cd ~/screenshots/ && ls screenshot* | head -n 5)
I nie wiesz, czy jest parsowany ((A && B) | C)
czy (A && B | C)
...
Prawie oficjalna dokumentacja znaleźć tutaj nie wymienić rurę na liście, więc nie można po prostu sprawdzić w tabeli.
Ponadto w bash (
nie służy tylko do zmiany kolejności operacji, ale tworzy podpowłokę , więc nie jestem w 100% pewien, że te linie są odpowiednikiem poprzedniej linii:
((cd ~/screenshots/ && ls screenshot*) | head -n 5)
Mówiąc bardziej ogólnie, jak poznać AST linii bash? W Pythonie mam funkcję, która daje mi drzewo, dzięki czemu mogę łatwo dwukrotnie sprawdzić kolejność działania.
|
jest to tylko złącze, które pasuje do standardowego złącza LHS do standardowego złącza RHS. Więc jeślicd
polecenie w twoim przykładzie nie powiedzie się, nie wyśle on żadnych danych wyjściowychhead
, alehead
w rzeczywistości nadalexecute
przeanalizuje nic i nie zwróci żadnych danych wyjściowych.bash
używa parsera yacc, a nie jakiejś ad-hoc; jeśliyacc -v
przejdziesz przez to, otrzymaszy.output
przyjemną gramatykę, która to pokazuje&&
i||
łączy listy, a listy składają się w końcu z potoków (a nie odwrotnie); tl; dr;A && B | C
jest taki samA && { B | C; }
, jak oczekiwano. Nie zakładaj kolejności wykonywania międzyB
iC
; polecenia w potoku są uruchamiane równolegle .[...]
testach i$((...))
ocenach arytmetycznych; w szczególności,||
i&&
jak stosować z listy poleceń w języku powłoki mają taki sam priorytet , w przeciwieństwie do odpowiednich podmiotówC
lub w ocenie arytmetycznej (gdzie&&
wiąże mocniej niż||
).Odpowiedzi:
Jest to równoważne z
( nawiasy klamrowe grupują polecenia bez podpowłoki ). Pierwszeństwo
|
jest zatem wyższe (wiąże się mocniej) niż&&
i||
. To jest,i
zawsze oznacza, że tylko wyjście B ma być podane do C . W razie potrzeby możesz użyć
(...)
lub{ ... ; }
połączyć polecenia jako jedną całość w celu ujednoznacznienia:Możesz to przetestować za pomocą różnych poleceń. Jeśli uciekniesz
wtedy dostaniesz
back:
tr a-z A-Z
wielkie litery na wejściu i widać, że tylkoecho world
zostało do niego wpakowane, podczas gdyecho hello
przeszło samo.Jest to zdefiniowane w gramatyce powłoki , choć niezbyt wyraźnie:
and_or
produkcja (dla&&
/||
) jest zdefiniowana tak, aby mieć aapipeline
w swoim ciele, podczas gdypipeline
zawiera tylkocommand
, co nie zawieraand_or
- tylkocomplete_command
produkcja może osiągnąćand_or
i istnieje tylko w najwyższy poziom i wewnątrz obiektów konstrukcyjnych, takich jak funkcje i pętle.Możesz ręcznie zastosować tę gramatykę, aby uzyskać parsowanie drzewa dla polecenia, ale Bash sam nic nie zapewnia. Nie znam żadnej powłoki, która wykracza poza to, co jest używane do ich własnego parsowania.
Gramatyka powłoki ma wiele specjalnych przypadków zdefiniowanych tylko półformalnie i poprawienie tej misji może być dość trudne. Nawet sam Bash czasami się mylił , więc praktyczność i ideał mogą być inne.
Istnieją zewnętrzne parsery, które próbują dopasować składnię i utworzyć drzewo, a spośród nich ogólnie polecę Morbiga , który stara się być najbardziej niezawodny.
źródło
TL; DR : separatory list takie jak
;
.&
,&&
i||
zdecyduj o kolejności parsowania.Podręcznik bash mówi nam:
Lub, jak zwięźle to ujęła wiki Bash Hackera
Tak więc
cd ~/screenshots/ && ls screenshot* | head -n 5
istnieje jeden potok -ls screenshot* | head -n 5
i jedno proste poleceniecd ~/screenshots/
. Pamiętaj, że zgodnie z instrukcjąZ drugiej strony
(cd ~/screenshots/ && ls screenshot*) | head -n 5
jest inaczej - masz jeden potok: po lewej stronie znajduje się podpowłoka, a po prawej maszhead -n 5
. W tym przypadku byłoby to przy użyciu notacji OP(A && B) | C
Weźmy inny przykład:
Tutaj mamy jedną listę
<pipeline1> && <pipeline2>
. Ponieważ wiemy, że status wyjścia potoku jest taki sam jak w ostatnim poleceniu ifalse
zwraca status negatywny, czyli błąd,&&
nie zostanie wykonany po prawej stronie.Tutaj lewy potok ma status wyjścia pomyślnego, więc prawy potok jest wykonywany i widzimy jego wynik.
Zauważ, że gramatyka powłoki nie sugeruje faktycznej kolejności wykonywania. Cytując jedną z odpowiedzi Gillesa :
I z podręcznika bash:
Na tej
cd ~/screenshots/ && ls screenshot* | head -n 5
podstawiecd ~/screenshots/
zostanie wykonane najpierw,ls screenshot* | head -n 5
jeśli poprzednie polecenie się powiedzie, alehead -n 5
może być pierwszym procesem spawnowanym, a niels
ponieważ są one w przygotowaniu.źródło
cd ~/screenshots/ && ls screenshot*
lubhead -n 5
” Cóż, tak jest w przypadku((cd ~/screenshots/ && ls screenshot*) | head -n 5)
. Ale nie mówi nic o niejednoznacznym przypadku,cd ~/screenshots/ && ls screenshot* | head -n 5
który wydaje się być sednem pytania (z nawiasami otaczającymi lub bez).cd ~/screenshots/ && ls screenshot*
musiałby zostać przetworzony pierwszy, ponieważ&&
listy są wyższe w kolejności pierwszeństwa (przynajmniej na podstawie odpowiedzi l0b0). Jak myślisz, gdzie powinienem poprawić odpowiedź?(cd && ls | head)
i drugie((cd && ls) | head)
, może być wskazane wyraźne, które masz na myśli.Oto, gdzie jest określony w
bash(1)
:&&
Oddziela więc rurociągi.źródło
Możesz po prostu spróbować
echo hello && echo world | less
. Zobaczysz, że|
ma wyższy priorytet (potok poleceń jest poleceniem). Tam dla twojego drugiego przykładu NIE jest to samo. Ponieważ jednakcd
nie ma danych wyjściowych, nie zobaczysz żadnej różnicy, eterycznej.źródło