ลำดับความสำคัญของ Pipe (|) และตรรกะและ (&&) ใน bash


17

สถานการณ์สุดคลาสสิกกับโอเปอเรเตอร์สำคัญคุณมีบรรทัดเหมือน:

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

และคุณไม่รู้ว่ามันแยกวิเคราะห์((A && B) | C)หรือ(A && B | C)...

เอกสารอย่างเป็นทางการเกือบจะพบที่นี่ไม่ได้แสดงรายการท่อในรายการดังนั้นผมจึงไม่สามารถเพียงแค่ตรวจสอบในตาราง

นอกจากนี้ใน bash (ไม่เพียง แต่เปลี่ยนลำดับการดำเนินการ แต่สร้าง subshellดังนั้นฉันจึงไม่ 100% แน่ใจว่าบรรทัดนี้เทียบเท่ากับบรรทัดก่อนหน้า:

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

โดยทั่วไปแล้วจะรู้ASTของ bash line ได้อย่างไร? ในหลามฉันมีฟังก์ชั่นที่ให้ต้นไม้กับฉันเพื่อให้ฉันสามารถตรวจสอบลำดับการทำงานได้อย่างง่ายดาย


1
มันอาจช่วยให้รู้ว่า|เป็นเพียงตัวเชื่อมต่อที่เหมาะกับ LHS stdout กับ RHS stdin ดังนั้นหากcdคำสั่งในตัวอย่างของคุณล้มเหลวมันจะไม่ส่งเอาต์พุตไปheadแต่headในความเป็นจริงแล้วยังคงexecuteแยกวิเคราะห์อะไรและส่งคืนไม่มีเอาต์พุต
DopeGhoti


1
bashใช้parser yacc ไม่ใช่สิ่งที่เฉพาะกิจ หากคุณเรียกใช้ผ่านyacc -vมันจะทำให้คุณy.outputมีไวยากรณ์ที่ดีที่แสดงให้เห็นว่า&&และ||รวมรายการและในที่สุดก็มีรายการของท่อ (และไม่ย้อนกลับ); TL; DR; A && B | Cเป็นเช่นเดียวกับA && { B | C; }ที่คาดไว้ อย่าถือว่าคำสั่งของการดำเนินการระหว่างBและC; คำสั่งในท่อที่มีการทำงานในแบบคู่ขนาน
mosvy

1
โปรดสังเกตว่า "เอกสารเกือบเป็นทางการ" ที่คุณชี้ไปนั้นไม่เกี่ยวข้องอย่างสมบูรณ์เนื่องจากเป็นเรื่องเกี่ยวกับตัวดำเนินการที่ใช้ใน[...]การทดสอบและ$((...))การประเมินทางคณิตศาสตร์ โดยเฉพาะอย่างยิ่ง||และ&&ตามที่ใช้กับรายการคำสั่งในภาษาของเชลล์มีลำดับความสำคัญเท่ากันซึ่งแตกต่างจากตัวดำเนินการตามลำดับในCหรือในการประเมินทางคณิตศาสตร์ (ที่&&ผูกแน่นกว่า||)
mosvy

@mosvy เอกสารนั้นไม่ได้พูดถึงบริบทที่ใช้ในตารางดังนั้นมันจึงดูเหมือนว่ามีประโยชน์น้อยกว่าในแง่นั้นเช่นกัน ...
354254

คำตอบ:


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

สิ่งนี้เทียบเท่า

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

( กลุ่มคำสั่งวงเล็บปีกกาเข้าด้วยกันโดยไม่มีเชลล์ย่อย ) ความสำคัญของการ|จึงสูงกว่า (ผูกที่เข้มงวดมากขึ้นกว่า) และ&& ||นั่นคือ,

A && B | C

และ

A || B | C

มักจะหมายความว่าการส่งออกเท่านั้น B คือการได้รับการ C คุณสามารถใช้(...)หรือ{ ... ; }เข้าร่วมคำสั่งร่วมกันเป็นเอนทิตีเดียวสำหรับแก้ความกำกวมหากจำเป็น:

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

คุณสามารถทดสอบสิ่งนี้โดยใช้คำสั่งที่แตกต่างกัน ถ้าคุณวิ่ง

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

แล้วคุณจะได้รับ

hello
WORLD

ย้อนกลับ: tr a-z A-Zพิมพ์ใหญ่และคุณสามารถเห็นได้ว่าecho worldมีการส่งไปเท่านั้นในขณะที่echo helloดำเนินการด้วยตนเอง


สิ่งนี้ถูกกำหนดไว้ในไวยากรณ์เชลล์แม้ว่าจะไม่ชัดเจนมาก: การand_orผลิต (สำหรับ&&/ ||) ถูกกำหนดให้มี aa pipelineในเนื้อหาของมันในขณะที่pipelineเพิ่งมีcommandซึ่งไม่ได้มีand_or- เฉพาะการcomplete_commandผลิตที่สามารถเข้าถึงand_orและมีอยู่ที่ ระดับสูงสุดและภายในร่างกายของโครงสร้างที่สร้างขึ้นเช่นฟังก์ชั่นและลูป

คุณสามารถใช้ไวยากรณ์นั้นด้วยตนเองเพื่อรับทรีการแยกวิเคราะห์สำหรับคำสั่ง แต่ Bash ไม่ได้ให้อะไรเลย ฉันไม่รู้เกี่ยวกับเปลือกหอยใด ๆ ที่ทำนอกเหนือจากที่ใช้สำหรับการแยกวิเคราะห์ของตนเอง

ไวยากรณ์เชลล์มีหลายกรณีพิเศษที่กำหนดไว้เพียงแบบกึ่งทางการและมันอาจเป็นภารกิจที่จะทำให้ถูกต้อง แม้แต่ Bash เองก็ทำให้มันผิดดังนั้นการปฏิบัติและอุดมคติอาจแตกต่างกัน

มีตัวแยกวิเคราะห์ภายนอกที่พยายามจับคู่ไวยากรณ์และสร้างต้นไม้และผู้ที่ฉันจะแนะนำMorbig ในวงกว้างซึ่งพยายามที่จะเชื่อถือได้มากที่สุด


7

DR; TL;คั่นรายการเช่น: &, &&และ||ตัดสินใจเรียงลำดับคำสั่ง

คู่มือทุบตีบอกเรา:

รายการ AND และ OR เป็นลำดับของหนึ่งหรือมากกว่าหนึ่งท่อคั่นด้วย && และ || ผู้ประกอบการควบคุมตามลำดับ

หรือวิกิของ Bash แฮ็คเกอร์จะวางไว้อย่างชัดเจน

<PIPELINE1> && <PIPELINE2>

ดังนั้นในการcd ~/screenshots/ && ls screenshot* | head -n 5 มีหนึ่งท่อ - และเป็นหนึ่งในคำสั่งง่ายๆls screenshot* | head -n 5 cd ~/screenshots/โปรดทราบว่าตามคู่มือ

แต่ละคำสั่งในไปป์ไลน์จะดำเนินการเป็นกระบวนการแยกต่างหาก (เช่นใน subshell)

บนมืออื่น ๆ ที่(cd ~/screenshots/ && ls screenshot*) | head -n 5แตกต่างกัน - คุณมีหนึ่งท่อ: ด้านซ้ายมี subshell head -n 5และด้านขวาที่คุณมี ในกรณีนี้ใช้สัญลักษณ์ของ OP มันจะเป็น(A && B) | C


ลองทำตัวอย่างอื่น:

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

<pipeline1> && <pipeline2>ที่นี่เรามีรายการหนึ่ง เนื่องจากเรารู้ว่าสถานะทางออกของไปป์ไลน์เหมือนกับคำสั่งสุดท้ายและfalseส่งคืนสถานะลบหรือที่รู้จักว่าล้มเหลว&&จะไม่ดำเนินการทางด้านขวามือ

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

ที่นี่ไปป์ไลน์ด้านซ้ายมีสถานะออกจากความสำเร็จดังนั้นไปป์ไลน์ที่ถูกต้องจะถูกดำเนินการและเราจะเห็นผลลัพธ์ของมัน


โปรดทราบว่าเชลล์ไวยากรณ์ไม่ได้หมายถึงลำดับการดำเนินการจริง หากต้องการอ้างอิงหนึ่งในคำตอบของ Gilles :

คำสั่ง Piped ทำงานพร้อมกัน เมื่อคุณเรียกใช้ ps | grep …มันเป็นโชคดีของการดึง (หรือรายละเอียดของการทำงานของเชลล์รวมกับตัวกำหนดการปรับละเอียดลึกลงไปในลำไส้ของเคอร์เนล) ว่า ps หรือ grep เริ่มต้นก่อนหรือไม่ เพื่อรันพร้อมกัน

และจากทุบตีคู่มือ:

รายการ AND และ OR ถูกดำเนินการด้วยการเชื่อมโยงด้านซ้าย

ขึ้นอยู่กับว่าในcd ~/screenshots/ && ls screenshot* | head -n 5การcd ~/screenshots/จะถูกดำเนินการก่อนls screenshot* | head -n 5ถ้าคำสั่งก่อนหน้าประสบความสำเร็จ แต่head -n 5อาจเป็นกระบวนการแรกที่เกิดขึ้นมากกว่าlsเพราะพวกเขาอยู่ในขั้นตอน


1
ฉันไม่เห็นว่าคำถามถามอะไรเกี่ยวกับสิ่งที่สิ่งที่เกิดวางไข่
Michael Homer

"มีความสำคัญของการที่ไม่มีใคร spawns แรกcd ~/screenshots/ && ls screenshot*หรือhead -n 5"((cd ~/screenshots/ && ls screenshot*) | head -n 5)ก็ใช่ว่าเป็นกรณีที่มี แต่มันไม่ได้พูดอะไรเกี่ยวกับกรณีที่คลุมเครือcd ~/screenshots/ && ls screenshot* | head -n 5ซึ่งน่าจะเป็นประเด็นของคำถาม (มีหรือไม่มีวงเล็บล้อมรอบ)
ilkkachu

@ilkkachu ถูกต้อง แต่ประโยคต่อไปนี้แตะที่ cd ~/screenshots/ && ls screenshot* จะต้องดำเนินการก่อนเพราะ&&รายการจะสูงกว่าตามลำดับความสำคัญ (ตามคำตอบของ l0b0 อย่างน้อย) คุณคิดว่าฉันควรปรับปรุงคำตอบที่ไหน?
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy ดีเนื่องจากคำถามมีทั้งคู่(cd && ls | head)และ((cd && ls) | head)มันอาจจะดีถ้าคุณต้องการให้ชัดเจนว่าคุณหมายถึงอะไร
ilkkachu

@ilkkachu โอเคฉันจะลบมันแล้วตอนนี้และแก้ไขมันในระหว่างนี้
Sergiy Kolodyazhnyy

3

ที่นี่มีการระบุไว้ใน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>.

ดังนั้น&&แยกท่อ


2

echo hello && echo world | lessคุณเพียงแค่อาจจะลอง คุณจะเห็นว่า|มีลำดับความสำคัญสูงกว่า (ไพพ์ไลน์ของคำสั่งคือคำสั่ง) มีตัวอย่างที่ 2 ของคุณไม่เหมือนกัน อย่างไรก็ตามเนื่องจากcdไม่มีเอาต์พุตคุณจะไม่เห็นความแตกต่างอีเทอร์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.