#! / bin / sh -
(หรืออย่างน้อยเป็น) มักจะแนะนำshebang/bin/sh
จะมีสคริปต์ตีความโดย
ทำไมไม่เพียง#! /bin/sh
หรือ#!/bin/sh
?
มีอะไรที่-
หา?
#! / bin / sh -
(หรืออย่างน้อยเป็น) มักจะแนะนำshebang/bin/sh
จะมีสคริปต์ตีความโดย
ทำไมไม่เพียง#! /bin/sh
หรือ#!/bin/sh
?
มีอะไรที่-
หา?
คำตอบ:
นั่นเป็นเหตุผลที่คล้ายกันว่าทำไมคุณต้องเขียน:
rm -- *.txt
และไม่
rm *.txt
ถ้าคุณสามารถรับประกันไม่มีไฟล์ในไดเรกทอรีปัจจุบันมีชื่อที่ขึ้นต้นด้วย.txt
-
ใน:
rm <arg>
<arg>
ถือเป็นตัวเลือกถ้ามันเริ่มต้นด้วย-
หรือไฟล์ที่จะลบอย่างอื่น ใน
rm - <arg>
arg
ถูกพิจารณาว่าเป็นไฟล์ที่จะลบโดยไม่คำนึงว่าไฟล์นั้นขึ้นต้นด้วย-
หรือไม่
sh
นั่นเป็นเหมือนกันสำหรับ
เมื่อหนึ่งรันสคริปต์ที่เริ่มต้นด้วย
#! /bin/sh
โดยทั่วไปด้วย:
execve("path/to/the-script", ["the-script", "arg"], [environ])
ระบบเปลี่ยนเป็น:
execve("/bin/sh", ["/bin/sh", "path/to/the-script", "arg"], [environ])
path/to/the-script
ไม่ปกติบางสิ่งบางอย่างที่อยู่ภายใต้การควบคุมของผู้เขียนสคริปต์ ผู้เขียนไม่สามารถคาดเดาได้ว่าจะเก็บสำเนาของสคริปต์ไว้ที่ใดหรือภายใต้ชื่อใด โดยเฉพาะอย่างยิ่งพวกเขาไม่สามารถรับประกันได้ว่าสิ่งpath/to/the-script
ที่เรียกว่าจะไม่เริ่มต้นด้วย-
(หรือ+
ที่เป็นปัญหาด้วยsh
) นั่นเป็นเหตุผลที่เราต้องการที่-
นี่เพื่อทำเครื่องหมายจุดสิ้นสุดของตัวเลือก
ตัวอย่างเช่นในระบบของฉันzcat
(เหมือนจริง ๆ กับสคริปต์อื่น ๆ ) เป็นตัวอย่างหนึ่งของสคริปต์ที่ไม่ปฏิบัติตามคำแนะนำนั้น:
$ head -n1 /bin/zcat
#!/bin/sh
$ mkdir +
$ ln -s /bin/zcat +/
$ +/zcat
/bin/sh: +/: invalid option
[...]
ตอนนี้คุณอาจถามว่าทำไม#! /bin/sh -
และไม่#! /bin/sh --
?
แม้ว่า#! /bin/sh --
จะทำงานกับ POSIX เชลล์ได้ แต่#! /bin/sh -
ก็พกพาได้มากกว่า sh
โดยเฉพาะอย่างยิ่งกับรุ่นโบราณ sh
การปฏิบัติ-
เป็นและสิ้นสุดของตัวเลือกถือกำเนิดgetopt()
และการใช้งานทั่วไปของ--
เพื่อทำเครื่องหมายจุดสิ้นสุดของตัวเลือกโดยใช้เวลานาน วิธีที่บอร์นเชลล์ (จากปลายยุค 70) -
แยกวิเคราะห์อาร์กิวเมนต์เท่านั้นอาร์กิวเมนต์แรกที่ได้รับการพิจารณาให้เป็นตัวเลือกถ้ามันเริ่มต้นด้วย อักขระทั้งหมดหลังจากนั้น-
จะถือว่าเป็นชื่อตัวเลือก หากไม่มีตัวละครหลังจาก-
นั้นก็ไม่มีตัวเลือก สิ่งนั้นติดอยู่และเชลล์ที่เหมือนบอร์นในภายหลังทั้งหมดรู้จัก-
เป็นวิธีการทำเครื่องหมายจุดสิ้นสุดของตัวเลือก
ในเชลล์เป้าหมาย (แต่ไม่ใช่ในเชลล์บอร์นที่ทันสมัย) #! /bin/sh -euf
จะแก้ไขปัญหาได้เนื่องจากอาร์กิวเมนต์แรกเท่านั้นที่ถูกพิจารณาเป็นตัวเลือก
ทีนี้ใคร ๆ ก็บอกได้ว่าเราเป็นคนอวดดีที่นี่และมันก็เป็นเหตุผลที่ฉันเขียนความต้องการเป็นตัวเอียงด้านบน:
-
หรือ+
หรือใส่ไว้ในไดเรกทอรีที่มีชื่อขึ้นต้นด้วยหรือ-
+
execvp()
/ execlp()
- ประเภท และในกรณีนั้นโดยทั่วไปคุณเรียกใช้the-script
มันเพื่อค้นหา$PATH
ในกรณีที่อาร์กิวเมนต์เส้นทางไปยังการexecve()
เรียกระบบโดยทั่วไปจะเริ่มต้นด้วย/
(ไม่ใช่-
หรือ+
) หรือราวกับ./the-script
ว่าคุณต้องการthe-script
ให้เรียกใช้ไดเรกทอรีปัจจุบัน (และ จากนั้นพา ธ เริ่มต้นด้วย./
ไม่ใช่-
หรือ+
อย่างใดอย่างหนึ่ง)ตอนนี้ข้างทางทฤษฎีถูกต้องปัญหามีเหตุผลว่าทำไมอีก#! /bin/sh -
กลายเป็นที่แนะนำเป็นวิธีปฏิบัติที่ดี และนั่นกลับไปเป็นช่วงเวลาที่หลาย ๆ ระบบยังคงรองรับสคริปต์ setuid
หากคุณมีสคริปต์ที่มี:
#! /bin/sh
/bin/echo "I'm running as root"
และสคริปต์นั้นคือรูท setuid (เช่นมี-r-sr-xr-x root bin
สิทธิ์) บนระบบเหล่านั้นเมื่อผู้ใช้ทั่วไปเรียกใช้
execve("/bin/sh", ["/bin/sh", "path/to/the-script"], [environ])
จะทำตามroot
!
หากผู้ใช้สร้างลิงก์สัญลักษณ์/tmp/-i -> path/to/the-script
และดำเนินการเป็น-i
แล้วมันจะเริ่มต้นเปลือกโต้ตอบ ( /bin/sh -i
) root
ในฐานะ
-
ที่จะทำงานรอบที่ (มันจะไม่ทำงานรอบปัญหาการแข่งขันสภาพหรือความจริงที่ว่าบางsh
การใช้งานเช่นบางksh88
คนจะชั่นค้นหาข้อโต้แย้งโดยไม่ต้องสคริปต์/
ใน$PATH
แม้ว่า)
ปัจจุบันแทบจะทุกระบบสนับสนุนสคริปต์ setuid อีกต่อไปและบางส่วนที่ยังคงทำอยู่ (โดยปกติจะไม่เป็นค่าเริ่มต้น) จบลงด้วยการทำexecve("/bin/sh", ["/bin/sh", "/dev/fd/<n>", arg])
(ซึ่ง<n>
เป็นไฟล์อธิบายเปิดให้อ่านสคริปต์) ซึ่งทำงานทั้งปัญหานี้และ สภาพการแข่งขัน.
โปรดทราบว่าคุณได้รับปัญหาที่คล้ายกันกับล่ามส่วนใหญ่ไม่เพียง แต่เปลือกหอย Bourne โดยทั่วไปเชลล์ที่ไม่คล้ายบอร์นไม่ได้รับการสนับสนุน-
ในฐานะเครื่องหมายสิ้นสุดของตัวเลือก แต่โดยทั่วไปแล้วจะรองรับ--
แทน (อย่างน้อยสำหรับเวอร์ชันที่ทันสมัย)
ด้วย
#! /usr/bin/awk -f
#! /usr/bin/sed -f
ไม่มีปัญหาเนื่องจากอาร์กิวเมนต์ถัดไปถือเป็นอาร์กิวเมนต์ของ-f
ตัวเลือกในทุกกรณี แต่ก็ยังใช้งานไม่ได้หากpath/to/script
เป็น-
(ในกรณีนี้โดยส่วนใหญ่sed
/ awk
การใช้งานsed
/ awk
ไม่อ่านโค้ดจาก-
ไฟล์ แต่มาจาก stdin แทน)
โปรดทราบว่าในระบบส่วนใหญ่ระบบหนึ่งไม่สามารถใช้:
#! /usr/bin/env sh -
#! /usr/bin/perl -w --
ในระบบส่วนใหญ่กลไก shebang อนุญาตการโต้แย้งเพียงครั้งเดียวหลังจากเส้นทางล่าม
ว่าจะใช้#! /bin/sh -
vs #!/bin/sh -
หรือไม่นั่นเป็นเพียงเรื่องของรสนิยม ฉันชอบแบบดั้งเดิมเนื่องจากทำให้เส้นทางล่ามมองเห็นได้ชัดเจนขึ้นและทำให้การเลือกเมาส์ง่ายขึ้น มีตำนานกล่าวว่าจำเป็นต้องใช้พื้นที่ใน Unix บางรุ่นแต่เป็น AFAIK ซึ่งไม่เคยมีการยืนยัน
การอ้างอิงที่ดีมากเกี่ยวกับ Unix shebang สามารถดูได้ที่https://www.in-ulm.de/~mascheck/various/shebang
bash
ยอมรับตัวเลือกเช่นเปลือกอื่น ๆ เป็นบอร์นเหมือนและ POSIX เปลือกbash
ยอมรับทั้งในและ#! /bin/bash -
#! /bin/bash --