โปรแกรม Bash ไม่ทำงานหากการเปลี่ยนเส้นทางจะล้มเหลว


9

ใน bash ฉันสังเกตเห็นว่าหากคำสั่งที่ใช้การเปลี่ยนเส้นทางจะล้มเหลวโปรแกรมใด ๆ ที่ทำงานก่อนหน้านั้นจะไม่ทำงาน

ตัวอย่างเช่นโปรแกรมนี้เปิดไฟล์ "a" และเขียน 50 ไบต์ไปยังไฟล์ "a" อย่างไรก็ตามการรันคำสั่งนี้ด้วยการเปลี่ยนเส้นทางไปยังไฟล์ที่มีสิทธิ์ไม่เพียงพอ (~ root / log) ทำให้ไม่มีการเปลี่ยนแปลงขนาดไฟล์ของ "a"

$ ./write_file.py >> ~root/log
-bash: /var/root/log: Permission denied
cdal at Mac in ~/experimental/unix_write
$ ls -lt
total 16
-rw-rw-r--  1 cdal  staff  0 Apr 27 08:54 a <-- SHOULD BE 50 BYTES

ใคร ๆ ก็คิดว่าโปรแกรมจะทำงานเรียกใช้เอาต์พุตใด ๆ (แต่เขียนไปยังไฟล์ "a") แล้วไม่สามารถเขียนเอาต์พุตใด ๆ ไปยัง ~ root / log แต่โปรแกรมจะไม่ทำงาน

เหตุใดจึงเป็นเช่นนี้และทุบตีเลือกลำดับของ "การตรวจสอบ" ที่ทำงานก่อนที่จะดำเนินการโปรแกรมอย่างไร มีการตรวจสอบอื่น ๆ เช่นกันหรือไม่?

ป.ล. ฉันพยายามที่จะตรวจสอบว่าโปรแกรมทำงานภายใต้ cron ทำงานจริงเมื่อเปลี่ยนเส้นทางไปยังไฟล์ "สิทธิ์ถูกปฏิเสธ"


ทุกอย่างอยู่ในสภาพที่ใช้งานได้ดี (เช่นการเป็นเจ้าของและการอนุญาตของไฟล์. py ของคุณ) โปรแกรมของคุณทำงานได้ดี ปัญหาของคุณมาจากการเปลี่ยนเส้นทาง คุณไม่ได้รับอนุญาตให้เขียนไดเรกทอรี filein / root และคุณได้เปลี่ยนเส้นทางของคุณstdoutให้ทำอย่างนั้น ดังนั้นคุณจะไม่เห็นผลลัพธ์ใด ๆ แม้ว่าโปรแกรมของคุณจะทำงาน
MelBurslan

2
เมลนั่นไม่จริงโปรแกรมไม่เคยวิ่ง ดูคำตอบด้านล่าง
Charlie Dalsass

คุณ: "รันwrite_file.pyโปรแกรมและส่งออกไปยัง~root/logbash:" ขออภัย แต่คุณไม่ได้รับอนุญาตให้เขียนไปยังไฟล์นั้น! "เชลล์ทำสิ่งที่ควรทำถ้ามันไม่สามารถทำสิ่งที่คุณถาม ทำมันจะแจ้งให้คุณทราบทันทีว่าเหตุใดจึงเกิดปัญหาทำให้คุณมีโอกาสตัดสินใจว่าจะจัดการกับมันอย่างไรสำหรับผู้ดูแลระบบทุบตีทุกคนรู้ว่าสิ่งเลวร้ายมากอาจเกิดขึ้นได้หากคุณเรียกใช้คำสั่งนั้นและไม่บันทึกผลลัพธ์ หากมีความสำคัญพอที่คุณกำหนดสถานที่เพื่อบันทึกมันจะผิดกับ ASS | U | ME มันก็โอเคที่จะทำงานโดยไม่บันทึก stdout
Monty Harder

คำตอบ:


18

ไม่ใช่เรื่องของการสั่งซื้อเช็ค แต่เป็นเรื่องของการตั้งค่าเชลล์ การเปลี่ยนเส้นทางถูกตั้งค่าก่อนที่คำสั่งจะรัน ดังนั้นในตัวอย่างของเปลือกพยายามที่จะเปิดสำหรับการผนวกก่อนที่จะพยายามที่จะทำอะไรที่เกี่ยวข้องกับ~root/log ./write_file.pyเนื่องจากไม่สามารถเปิดไฟล์บันทึกได้การเปลี่ยนเส้นทางจึงล้มเหลวและเชลล์หยุดประมวลผลบรรทัดคำสั่ง ณ จุดนั้น

วิธีหนึ่งในการสาธิตสิ่งนี้คือการใช้ไฟล์ที่ไม่สามารถเรียกใช้งานได้และพยายามเรียกใช้:

$ touch demo
$ ./demo
zsh: permission denied: ./demo
$ ./demo > ~root/log
zsh: permission denied: /root/log

นี่แสดงให้เห็นว่าเชลล์ไม่ได้มอง./demoเมื่อไม่สามารถตั้งค่าการเปลี่ยนเส้นทาง


ว้าวมันง่ายอย่างนั้นเหรอ? ฉันไม่ทราบว่ามีการเปลี่ยนเส้นทางก่อน ขอบคุณสำหรับคำตอบนี้และสำหรับคำตอบที่ยอดเยี่ยมอื่น ๆ เช่นกัน
Charlie Dalsass

6
หากพวกเขาไม่ได้ทำก่อนที่จะเขียนผลลัพธ์ที่?
Charles Duffy

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

11

จากหน้าbash man, หัวข้อ REDIRECTION (เน้นที่ฉัน):

ก่อนที่จะดำเนินการคำสั่งอินพุตและเอาต์พุตอาจถูกเปลี่ยนทิศทางโดยใช้สัญลักษณ์พิเศษที่ตีความโดยเชลล์

...

ความล้มเหลวในการเปิดหรือสร้างไฟล์ทำให้การเปลี่ยนเส้นทางล้มเหลว

ดังนั้นเชลล์จึงพยายามที่จะเปิดไฟล์เป้าหมายสำหรับstdoutซึ่งล้มเหลวและคำสั่งจะไม่ถูกดำเนินการเลย


ขอบคุณมาก. ฉันหวังว่าหน้าคนจะชี้แจงเล็กน้อยด้วย "... หากไม่สามารถเปลี่ยนเส้นทางผลลัพธ์โปรแกรมจะไม่ถูกดำเนินการ"
Charlie Dalsass

Updated; มันค่อนข้างซ่อนบางย่อหน้าด้านล่าง
เมอร์ฟี

ที่จริงแล้วมันค่อนข้างชัดเจน "ความล้มเหลวในการเปิดหรือสร้างไฟล์ทำให้การเปลี่ยนเส้นทางล้มเหลว" นั่นไง ขอบคุณอีกครั้ง.
Charlie Dalsass

3

เป็นมูลค่าการสังเกตว่าเชลล์จะต้องสร้างการเปลี่ยนเส้นทางก่อนเริ่มโปรแกรม

พิจารณาตัวอย่างของคุณ:

./write_file.py >> ~root/log

เกิดอะไรขึ้นในเปลือกคือ:

  1. เรา (เปลือก) fork(); กระบวนการ child สืบทอดตัวอธิบายไฟล์ที่เปิดจาก parent (เชลล์)
  2. ในกระบวนการ child เราfopen()(ส่วนขยาย) "~ root / log" และdup2()ไปยัง fd 1 (และclose()fd ชั่วคราว) หากfopen()ล้มเหลวโทรexit()เพื่อรายงานข้อผิดพลาดไปยังผู้ปกครอง
  3. ยังอยู่ในวัยเด็กเราexec()"./write_file.py" ตอนนี้กระบวนการนี้ไม่ได้รันรหัสใด ๆ ของเราอีกต่อไป (เว้นแต่ว่าเราไม่สามารถเรียกใช้งานได้ในกรณีนี้เราexit()จะรายงานข้อผิดพลาดกับผู้ปกครอง)
  4. ผู้ปกครองจะwait()ให้เด็กยกเลิกและจัดการรหัสออก (โดยคัดลอกลง$?ในอย่างน้อย)

ดังนั้นการเปลี่ยนเส้นทางจะต้องเกิดขึ้นในชายด์ระหว่างfork()และexec(): มันไม่สามารถเกิดขึ้นได้ก่อนfork()เพราะจะต้องไม่เปลี่ยน stdout ของเชลล์และมันไม่สามารถเกิดขึ้นได้หลังจากexec()เพราะชื่อไฟล์และรหัสปฏิบัติการของเชลล์ตอนนี้ถูกแทนที่ด้วยโปรแกรม Python . พาเรนต์ไม่สามารถเข้าถึงไฟล์ descriptors ของเด็ก (และแม้ว่าจะเป็นเช่นนั้นก็ไม่สามารถรับประกันได้ว่าจะเปลี่ยนเส้นทางระหว่างexec()และครั้งแรกที่เขียนไปยัง stdout)


0

ฉันขอโทษที่ต้องแจ้งให้คุณทราบว่ามันค่อนข้างตรงข้าม เชลล์ต้องเปิดเป็น I / O ก่อนจากนั้นจึงผ่านการควบคุมไปยังโปรแกรม

tee อาจพิสูจน์ได้ว่ามีประโยชน์ในกรณีนี้: ./write_file.py | tee -a ~root/log > /dev/null


สคริปต์ Python จะไม่ตายบน SIGPIPE หลังจากทีล้มเหลวหรือไม่
เควิน

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