Precedence of Pipe (|) dan logical dan (&&) dalam bash


17

Skenario klasik dengan Operator Precedence, Anda memiliki garis seperti:

(cd ~/screenshots/ && ls screenshot* | head -n 5)

Dan Anda tidak tahu apakah itu diuraikan ((A && B) | C)atau (A && B | C)...

The dokumentasi hampir resmi ditemukan di sini tidak mencantumkan pipa dalam daftar jadi saya tidak bisa hanya memeriksa di meja.

Selanjutnya dalam bash, (tidak hanya untuk mengubah urutan operasi tetapi membuat subkulit , jadi saya tidak 100% yakin baris ini setara dengan baris sebelumnya:

((cd ~/screenshots/ && ls screenshot*) | head -n 5)

Lebih umum, bagaimana cara mengetahui AST dari bash line? Dalam python saya memiliki fungsi yang memberi saya pohon sehingga saya dapat dengan mudah memeriksa urutan operasi.


1
Mungkin membantu untuk mengetahui bahwa |itu hanya konektor yang sesuai dengan stdout LHS ke stdin RHS. Jadi, jika cdperintah dalam contoh Anda gagal, itu tidak akan mengirim output ke head, tetapi headpada kenyataannya akan tetap execute, mengurai apa-apa, dan mengembalikan tidak ada output.
DopeGhoti


1
bashmenggunakan parser yacc bukan hal ad-hoc; jika Anda menjalankannya, yacc -vitu akan memberi Anda y.outputtata bahasa yang bagus yang menunjukkan itu &&dan ||menggabungkan daftar, dan daftar pada akhirnya terdiri dari jaringan pipa (dan bukan sebaliknya); tl; dr; A && B | Csama dengan A && { B | C; }, seperti yang diharapkan. Jangan menganggap urutan eksekusi antara Bdan C; perintah-perintah dalam sebuah pipa dijalankan secara paralel .
Mosvy

1
Perhatikan bahwa "dokumentasi hampir resmi" yang Anda tunjukkan sama sekali tidak relevan, karena ini tentang operator yang digunakan dalam [...]pengujian dan $((...))evaluasi aritmatika; khususnya, ||dan &&seperti yang digunakan dengan daftar perintah dalam bahasa shell memiliki prioritas yang sama , tidak seperti masing-masing operator di Catau dalam evaluasi aritmatika (di mana &&mengikat lebih erat daripada ||).
Mosvy

@mosvy, dokumen itu bahkan tidak mengatakan dalam konteks apa tabel ini berlaku, jadi sepertinya kurang berguna dalam arti itu juga ...
ilkkachu

Jawaban:


21
cd ~/screenshots/ && ls screenshot* | head -n 5

Ini setara dengan

cd ~/screenshots && { ls screenshot* | head -n 5 ; }

( kelompok kawat gigi perintah bersama tanpa subkulit ). Diutamakan |dengan demikian lebih tinggi (mengikat lebih ketat) daripada &&dan ||. Itu adalah,

A && B | C

dan

A || B | C

selalu berarti bahwa output saja B adalah untuk diberikan kepada C . Anda dapat menggunakan (...)atau { ... ; }untuk menggabungkan perintah bersama sebagai satu kesatuan untuk disambiguasi jika perlu:

{ A && B ; } | C
A && { B | C ; } # This is the default, but you might sometimes want to be explicit

Anda dapat menguji ini menggunakan beberapa perintah yang berbeda. Jika Anda berlari

echo hello && echo world | tr a-z A-Z

maka kamu akan mendapatkan

hello
WORLD

kembali: tr a-z A-Zhuruf besar inputnya , dan Anda dapat melihat bahwa hanya echo worlddisalurkan ke dalamnya, sementara echo hellomelalui sendiri.


Ini adalah didefinisikan dalam shell tata bahasa , meskipun tidak sangat jelas: yang and_orproduksi (untuk &&/ ||) didefinisikan memiliki aa pipelinedi tubuhnya, sementara pipelinehanya berisi command, yang tidak mengandung and_or- hanya complete_commandproduksi dapat mencapai and_or, dan hanya ada di tingkat atas dan di dalam tubuh konstruksi struktural seperti fungsi dan loop.

Anda bisa secara manual menerapkan tata bahasa itu untuk mendapatkan parse tree untuk suatu perintah, tetapi Bash tidak menyediakan apa pun itu sendiri. Saya tidak tahu ada shell yang melebihi apa yang digunakan untuk parsing mereka sendiri.

Tata bahasa shell memiliki banyak kasus khusus yang hanya didefinisikan secara semi-formal dan ini bisa menjadi misi yang harus diselesaikan. Bahkan Bash sendiri terkadang salah , sehingga kepraktisan dan cita-cita mungkin berbeda.

Ada parser eksternal yang mencoba mencocokkan sintaks dan menghasilkan pohon, dan dari mereka saya akan merekomendasikan secara luas Morbig , yang mencoba menjadi yang paling dapat diandalkan.


7

TL; DR : daftar pemisah seperti ;. &,&& dan ||tentukan urutan penguraian.

The bash manual memberitahu kita:

Daftar AND dan OR adalah urutan dari satu atau beberapa saluran pipa yang dipisahkan oleh && dan || masing-masing mengendalikan operator.

Atau bagaimana wiki Bash Hacker menjelaskannya dengan ringkas

<PIPELINE1> && <PIPELINE2>

Dengan demikian, cd ~/screenshots/ && ls screenshot* | head -n 5 ada satu pipa - ls screenshot* | head -n 5dan satu perintah sederhana cd ~/screenshots/. Perhatikan bahwa sesuai dengan manual

Setiap perintah dalam sebuah pipa dieksekusi sebagai proses terpisah (yaitu, dalam subkulit).

Di sisi lain, (cd ~/screenshots/ && ls screenshot*) | head -n 5berbeda - Anda memiliki satu pipa: di sebelah kiri ada subkulit dan di sebelah kanan Anda miliki head -n 5. Dalam hal ini, menggunakan notasi OP itu(A && B) | C


Mari kita ambil contoh lain:

$ echo foo | false &&  echo 123 | tr 2 5
$

Di sini kita punya satu daftar <pipeline1> && <pipeline2>. Karena kita tahu bahwa status keluar dari pipa sama dengan perintah terakhir dan falsemengembalikan status negatif alias gagal, &&tidak akan menjalankan sisi kanan.

$ echo foo | true &&  echo 123 | tr 2 5
153

Di sini pipa kiri memiliki status keluar yang sukses, jadi pipa kanan dieksekusi dan kami melihat hasilnya.


Perhatikan bahwa tata bahasa shell tidak menyiratkan urutan eksekusi aktual. Mengutip salah satu jawaban Gilles :

Perintah pipa dijalankan secara bersamaan. Ketika Anda menjalankan ps | grep…, itu adalah keberuntungan dari undian (atau masalah detail dari cara kerja shell yang dikombinasikan dengan penjadwal fine-tuning jauh di dalam usus kernel) untuk apakah ps atau grep dimulai pertama kali, dan dalam kasus apapun mereka melanjutkan untuk mengeksekusi secara bersamaan.

Dan dari bash manual:

Daftar AND dan OR dijalankan dengan asosiasi kiri.

Berdasarkan bahwa dalam cd ~/screenshots/ && ls screenshot* | head -n 5yang cd ~/screenshots/akan dieksekusi pertama, ls screenshot* | head -n 5jika perintah sebelumnya berhasil, tapi head -n 5mungkin menjadi proses yang pertama melahirkan bukan lskarena mereka berada dalam pipa.


1
Saya tidak melihat di mana pertanyaan itu menanyakan apa pun tentang pesanan apa yang ditimbulkan.
Michael Homer

"tidak ada yang diutamakan yang ditimbulkan pertama, cd ~/screenshots/ && ls screenshot*atau head -n 5" Yah, ya, itu masalahnya ((cd ~/screenshots/ && ls screenshot*) | head -n 5). Tapi itu tidak mengatakan apa-apa tentang kasus ambigu cd ~/screenshots/ && ls screenshot* | head -n 5yang tampaknya menjadi titik pertanyaan (dengan atau tanpa tanda kurung di sekitarnya).
ilkkachu

@ilkkachu Benar, tetapi kalimat berikut menyentuh itu. cd ~/screenshots/ && ls screenshot* harus diproses terlebih dahulu karena &&daftar lebih tinggi berdasarkan urutan prioritas (berdasarkan pada jawaban l0b0, setidaknya). Menurut Anda di mana saya harus meningkatkan jawabannya?
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy, well, mengingat bahwa pertanyaannya memiliki keduanya (cd && ls | head)dan ((cd && ls) | head), mungkin baik untuk secara eksplisit tentang yang Anda maksud.
ilkkachu

@ilkkachu OK, saya akan menghapus ini untuk saat ini, dan mengeditnya sementara itu
Sergiy Kolodyazhnyy

3

Di sinilah ditentukan bash(1):

SHELL GRAMMAR
[...]
   Pipelines
       A  pipeline  is  a sequence of one or more commands separated by one of
       the control operators | or |&.
[...]
   Lists
       A list is a sequence of one or more pipelines separated by one  of  the
       operators ;, &, &&, or ||, and optionally terminated by one of ;, &, or
       <newline>.

Jadi, &&pisahkan jalur pipa.


2

Anda bisa mencobanya echo hello && echo world | less. Anda akan melihat bahwa |memiliki prioritas lebih tinggi (Sebuah pipa perintah adalah perintah). Ada untuk contoh 2 Anda TIDAK sama. Namun karena cdtidak memiliki output, Anda tidak akan melihat perbedaan, dengan cara lain.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.