อะไรคือความแตกต่างระหว่าง && กับ | ในสคริปต์ทุบตี?


13

ลองดูสองบรรทัดด้านล่างซึ่งให้ผลลัพธ์ที่แตกต่างกันสองรายการ

p=$(cd ~ && pwd) ; echo $p
p=$(cd ~ |  pwd) ; echo $p

ทั้งสองแตกต่างกันอย่างไร


@ heemayl ขอบคุณ คุณสามารถแยกความแตกต่างในกรณีของฉันได้ไหม ขอบคุณ
Nam G VU

1
คำแนะนำ:คำสั่งที่ด้านใดด้านหนึ่งของการ|ทำงานใน subshells
heemayl

คำตอบ:


21

ในp=$(cd ~ && pwd):

  • การทดแทนคำสั่ง$()จะทำงานใน subshell

  • cd ~เปลี่ยนไดเรกทอรีเป็น~(บ้านของคุณ) ถ้าcdสำเร็จ ( &&) จากนั้นpwdพิมพ์ชื่อไดเรกทอรีบน STDOUT ดังนั้นสตริงที่บันทึกไว้pจะเป็นไดเรกทอรีบ้านของคุณเช่น/home/foobar

ในp=$(cd ~ | pwd):

  • $()วางเปลือกย่อยอีกครั้ง

  • คำสั่งทั้งสองด้านของการ|ทำงานในแต่ละ subshells (และทั้งสองเริ่มออกในเวลาเดียวกัน)

  • ดังนั้นcd ~จะทำใน subshell และpwdในsubshell แยก

  • ดังนั้นคุณจะได้รับเพียง STDOUT จากpwdเช่นจากที่คุณเรียกใช้คำสั่งนี้สามารถเป็นไดเรกทอรีใด ๆ ที่คุณสามารถจินตนาการจึงpจะมีชื่อไดเรกทอรีจากที่คำสั่งถูกเรียกไม่ใช่ไดเรกทอรีบ้านของคุณ


วัตถุประสงค์ของไพพ์ระหว่างคำสั่งคืออะไร? cd ~ไม่สร้างเอาต์พุตใด ๆ และpwdไม่อ่านอินพุตใด ๆ
Barmar

คำสั่งที่สองนั้นเทียบเท่ากับคำสั่ง(cd ~);p=$(pwd)ใช่หรือไม่?
Barmar

@Barmar ใช่มันเป็นสิ่งที่ OP ใช้และฉันแค่อธิบายให้เขาฟัง
heemayl

7

ปัญหาหลักคือวิธีที่ผู้ประกอบการ&&และ|เชื่อมต่อทั้งสองคำสั่ง

การ&&เชื่อมต่อคำสั่งผ่านรหัสออก การ|เชื่อมต่อคำสั่งทั้งสองผ่านตัวอธิบายไฟล์ (stdin, stdout)

ให้ลดความซับซ้อนก่อน เราสามารถลบการบ้านและเขียน:

echo $(cd ~ && pwd)
echo $(cd ~ | pwd)

เราสามารถลบ sub-shell ของ command execution เพื่อวิเคราะห์สิ่งนี้:

$ cd ~ && pwd
$ cd ~ | pwd

&&

หากเราเปลี่ยนพรอมต์เพื่อแสดงไดเรกทอรีที่คำสั่งถูกดำเนินการPS1='\w\$ 'เราจะเห็นสิ่งนี้:

/tmp/user$ cd ~ && pwd
/home/user
~$ 
  • คำสั่งcd ~เปลี่ยน "ไดเรกทอรีปัจจุบัน" เป็นบ้านของผู้ใช้จริงที่กำลังดำเนินการคำสั่ง ( /home/user)
  • เนื่องจากผลลัพธ์ของคำสั่งประสบความสำเร็จ (รหัสทางออก 0) คำสั่งถัดไปหลังจาก && จะถูกเรียกใช้งาน
  • และจะพิมพ์ "ไดเรกทอรีทำงานปัจจุบัน"
  • เปลือกทำงานมีการเปลี่ยนแปลงของpwdการเป็นที่แสดงโดยพรอมต์ของ~~$

หากการเปลี่ยนแปลงของไดเรกทอรีไม่สำเร็จ (รหัสการออกไม่ใช่ 0) ด้วยเหตุผลบางอย่าง (ไม่มีไดเรกทอรีสิทธิ์บล็อกการอ่านไดเรกทอรี) คำสั่งต่อไปจะไม่ถูกดำเนินการ

ตัวอย่าง:

/tmp/user$ false && pwd
/tmp/user$ _ 

โค้ดทางออก 1 falseไม่อนุญาตให้เรียกใช้งานคำสั่งถัดไป

ดังนั้นรหัสทางออกของ "คำสั่ง 1" คือสิ่งที่มีผลต่อ "คำสั่ง 2"

ตอนนี้ผลกระทบของคำสั่งทั้งหมด:

/tmp/user$ echo $(cd ~ && pwd)
/home/user
/tmp/user$ _

ไดเร็กทอรีถูกเปลี่ยน แต่ภายใน sub-shell $(…)ไดเร็กทอรีที่ถูกเปลี่ยนจะถูกพิมพ์/home/userแต่ถูกยกเลิกทันทีเมื่อ sub-shell ปิด pwd ส่งคืนเป็นไดเร็กทอรีเริ่มต้น ( /tmp/user)

|

นี่คือสิ่งที่เกิดขึ้น:

/tmp/user$ cd ~ | pwd
/tmp/user
/tmp/user$ _

เมตาอักขระ|(ไม่ใช่ตัวดำเนินการจริง) ส่งสัญญาณเชลล์เพื่อสร้างสิ่งที่เรียกว่า "ไพพ์", (เป็นทุบตี) แต่ละคำสั่งในแต่ละด้านของไพพ์ ( |) ตั้งอยู่ภายในแต่ละเชลล์ย่อยก่อนด้านขวา คำสั่งแล้วซ้ายหนึ่ง อินพุตไฟล์ descriptor ( /dev/stdin) ของคำสั่งด้านขวาเชื่อมต่อกับ output descriptor ( /dev/stdout) จากนั้นทั้งสองคำสั่งจะเริ่มต้นและปล่อยให้โต้ตอบ คำสั่ง left ( cd -) ไม่มีเอาต์พุตและคำสั่ง right ( pwd) จะไม่รับอินพุต ดังนั้นแต่ละคนจะทำงานอย่างอิสระภายในแต่ละเชลล์ย่อย

  • การcd ~เปลี่ยนแปลง pwd ของหนึ่งเชลล์
  • pwdพิมพ์ (อิสระอย่างสมบูรณ์) pwd ย่อยเปลือกอื่น ๆ

การเปลี่ยนแปลงในแต่ละเชลล์จะถูกยกเลิกเมื่อไพพ์สิ้นสุดลง sub-shell ภายนอกไม่ได้เปลี่ยน pwd

นั่นเป็นสาเหตุที่คำสั่งทั้งสองเชื่อมต่อโดย "file descriptors" เท่านั้น
ในกรณีนี้ไม่มีอะไรส่งและอ่านอะไร

คำสั่งทั้งหมด:

$ echo "$(cd ~ | pwd)"

จะพิมพ์ไดเรกทอรีที่คำสั่งถูกดำเนินการ


5

ฉันไม่แน่ใจว่าคุณหมายถึง '|' หรือ '||' ในกรณีที่สองของคุณ

'|' ในเชลล์ไพพ์เอาต์พุตของคำสั่งหนึ่งไปยังอินพุตของเคสอื่น - กรณีการใช้งานทั่วไปนั้นเป็นสิ่งที่ต้องการเช่น: curl http://abcd.com/efgh | grep ijkl เรียกใช้คำสั่งและใช้คำสั่งอื่นเพื่อประมวลผลเอาต์พุตของคำสั่ง

ในตัวอย่างที่คุณให้มามันค่อนข้างไม่ไร้สาระเนื่องจาก 'cd' โดยทั่วไปจะไม่สร้างเอาต์พุตใด ๆ และ 'pwd' ไม่ได้คาดหวังอินพุตใด ๆ

'&&' และ '||' เป็นคำสั่งของพันธมิตรแม้ว่า ถูกออกแบบมาเพื่อใช้ในลักษณะเดียวกับตัวดำเนินการแบบลอจิคัล "และ" และ "หรือ" ในภาษาส่วนใหญ่ อย่างไรก็ตามการปรับให้เหมาะสมที่ดำเนินการจะทำให้พวกเขามีพฤติกรรมเฉพาะที่เป็นกระบวนทัศน์การเขียนโปรแกรมเชลล์

ในการกำหนดผลลัพธ์ของการดำเนินการแบบลอจิคัล "และ" คุณจะต้องประเมินเงื่อนไขที่สองหากเงื่อนไขแรกสำเร็จ - หากเงื่อนไขแรกล้มเหลวผลลัพธ์โดยรวมจะเป็นเท็จเสมอ

ในการกำหนดผลลัพธ์ของการดำเนินการแบบลอจิคัล "หรือ" คุณจะต้องประเมินเงื่อนไขที่สองหากเงื่อนไขแรกล้มเหลว - หากเงื่อนไขแรกสำเร็จผลลัพธ์โดยรวมจะเป็นจริงเสมอ

ดังนั้นในเชลล์หากคุณมีcommand1 && command2 command2จะดำเนินการเมื่อcommand1เสร็จสิ้นและส่งคืนรหัสผลลัพธ์ที่สำเร็จ หากคุณcommand1 || command2 command2จะถูกดำเนินการเมื่อcommand1เสร็จสิ้นหากcommand1ส่งคืนรหัสความล้มเหลว

กระบวนทัศน์ทั่วไปอีกอันหนึ่งคือต้องcommand1มีคำสั่งทดสอบ - สิ่งนี้จะสร้างบรรทัดเดียวถ้า / แล้วคำสั่ง - ตัวอย่างเช่น:

[ "$VAR" = "" ] && VAR="Value if empty"

เป็นวิธี (ยืดยาว) ของการกำหนดค่าให้กับตัวแปรถ้าว่างเปล่าในขณะนี้

มีตัวอย่างมากมายของการใช้กระบวนการนี้ที่อื่นในการแลกเปลี่ยนกอง

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