ลองดูสองบรรทัดด้านล่างซึ่งให้ผลลัพธ์ที่แตกต่างกันสองรายการ
p=$(cd ~ && pwd) ; echo $p
p=$(cd ~ | pwd) ; echo $p
ทั้งสองแตกต่างกันอย่างไร
|ทำงานใน subshells
ลองดูสองบรรทัดด้านล่างซึ่งให้ผลลัพธ์ที่แตกต่างกันสองรายการ
p=$(cd ~ && pwd) ; echo $p
p=$(cd ~ | pwd) ; echo $p
ทั้งสองแตกต่างกันอย่างไร
|ทำงานใน subshells
คำตอบ:
ในp=$(cd ~ && pwd):
การทดแทนคำสั่ง$()จะทำงานใน subshell
cd ~เปลี่ยนไดเรกทอรีเป็น~(บ้านของคุณ) ถ้าcdสำเร็จ ( &&) จากนั้นpwdพิมพ์ชื่อไดเรกทอรีบน STDOUT ดังนั้นสตริงที่บันทึกไว้pจะเป็นไดเรกทอรีบ้านของคุณเช่น/home/foobar
ในp=$(cd ~ | pwd):
$()วางเปลือกย่อยอีกครั้ง
คำสั่งทั้งสองด้านของการ|ทำงานในแต่ละ subshells (และทั้งสองเริ่มออกในเวลาเดียวกัน)
ดังนั้นcd ~จะทำใน subshell และpwdในsubshell แยก
ดังนั้นคุณจะได้รับเพียง STDOUT จากpwdเช่นจากที่คุณเรียกใช้คำสั่งนี้สามารถเป็นไดเรกทอรีใด ๆ ที่คุณสามารถจินตนาการจึงpจะมีชื่อไดเรกทอรีจากที่คำสั่งถูกเรียกไม่ใช่ไดเรกทอรีบ้านของคุณ
cd ~ไม่สร้างเอาต์พุตใด ๆ และpwdไม่อ่านอินพุตใด ๆ
(cd ~);p=$(pwd)ใช่หรือไม่?
ปัญหาหลักคือวิธีที่ผู้ประกอบการ&&และ|เชื่อมต่อทั้งสองคำสั่ง
การ&&เชื่อมต่อคำสั่งผ่านรหัสออก การ|เชื่อมต่อคำสั่งทั้งสองผ่านตัวอธิบายไฟล์ (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)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)"
จะพิมพ์ไดเรกทอรีที่คำสั่งถูกดำเนินการ
ฉันไม่แน่ใจว่าคุณหมายถึง '|' หรือ '||' ในกรณีที่สองของคุณ
'|' ในเชลล์ไพพ์เอาต์พุตของคำสั่งหนึ่งไปยังอินพุตของเคสอื่น - กรณีการใช้งานทั่วไปนั้นเป็นสิ่งที่ต้องการเช่น:
curl http://abcd.com/efgh | grep ijkl
เรียกใช้คำสั่งและใช้คำสั่งอื่นเพื่อประมวลผลเอาต์พุตของคำสั่ง
ในตัวอย่างที่คุณให้มามันค่อนข้างไม่ไร้สาระเนื่องจาก 'cd' โดยทั่วไปจะไม่สร้างเอาต์พุตใด ๆ และ 'pwd' ไม่ได้คาดหวังอินพุตใด ๆ
'&&' และ '||' เป็นคำสั่งของพันธมิตรแม้ว่า ถูกออกแบบมาเพื่อใช้ในลักษณะเดียวกับตัวดำเนินการแบบลอจิคัล "และ" และ "หรือ" ในภาษาส่วนใหญ่ อย่างไรก็ตามการปรับให้เหมาะสมที่ดำเนินการจะทำให้พวกเขามีพฤติกรรมเฉพาะที่เป็นกระบวนทัศน์การเขียนโปรแกรมเชลล์
ในการกำหนดผลลัพธ์ของการดำเนินการแบบลอจิคัล "และ" คุณจะต้องประเมินเงื่อนไขที่สองหากเงื่อนไขแรกสำเร็จ - หากเงื่อนไขแรกล้มเหลวผลลัพธ์โดยรวมจะเป็นเท็จเสมอ
ในการกำหนดผลลัพธ์ของการดำเนินการแบบลอจิคัล "หรือ" คุณจะต้องประเมินเงื่อนไขที่สองหากเงื่อนไขแรกล้มเหลว - หากเงื่อนไขแรกสำเร็จผลลัพธ์โดยรวมจะเป็นจริงเสมอ
ดังนั้นในเชลล์หากคุณมีcommand1 && command2 command2จะดำเนินการเมื่อcommand1เสร็จสิ้นและส่งคืนรหัสผลลัพธ์ที่สำเร็จ หากคุณcommand1 || command2 command2จะถูกดำเนินการเมื่อcommand1เสร็จสิ้นหากcommand1ส่งคืนรหัสความล้มเหลว
กระบวนทัศน์ทั่วไปอีกอันหนึ่งคือต้องcommand1มีคำสั่งทดสอบ - สิ่งนี้จะสร้างบรรทัดเดียวถ้า / แล้วคำสั่ง - ตัวอย่างเช่น:
[ "$VAR" = "" ] && VAR="Value if empty"
เป็นวิธี (ยืดยาว) ของการกำหนดค่าให้กับตัวแปรถ้าว่างเปล่าในขณะนี้
มีตัวอย่างมากมายของการใช้กระบวนการนี้ที่อื่นในการแลกเปลี่ยนกอง