ทำไม“ -” ใน“ #! / bin / sh -” shebang?


27
#! / bin / sh -

(หรืออย่างน้อยเป็น) มักจะแนะนำshebang/bin/shจะมีสคริปต์ตีความโดย

ทำไมไม่เพียง#! /bin/shหรือ#!/bin/sh?

มีอะไรที่-หา?

คำตอบ:


37

นั่นเป็นเหตุผลที่คล้ายกันว่าทำไมคุณต้องเขียน:

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จะแก้ไขปัญหาได้เนื่องจากอาร์กิวเมนต์แรกเท่านั้นที่ถูกพิจารณาเป็นตัวเลือก

ทีนี้ใคร ๆ ก็บอกได้ว่าเราเป็นคนอวดดีที่นี่และมันก็เป็นเหตุผลที่ฉันเขียนความต้องการเป็นตัวเอียงด้านบน:

  1. ไม่มีใครในใจขวาของพวกเขาจะเรียกสคริปต์ที่มีบางสิ่งบางอย่างที่เริ่มต้นด้วย-หรือ+หรือใส่ไว้ในไดเรกทอรีที่มีชื่อขึ้นต้นด้วยหรือ-+
  2. แม้ว่าพวกเขาจะเป็นคนแรกที่จะโต้แย้งว่าพวกเขาสามารถตำหนิตัวเองเท่านั้น แต่เมื่อคุณเรียกใช้สคริปต์บ่อยกว่าไม่นั่นคือจากเปลือกหรือจากฟังก์ชั่น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


1
สิ่งนี้เกี่ยวข้องกับ #! / bin / bash หรือไม่
Joe

1
@ โจใช่แน่นอนbashยอมรับตัวเลือกเช่นเปลือกอื่น ๆ เป็นบอร์นเหมือนและ POSIX เปลือกbashยอมรับทั้งในและ#! /bin/bash - #! /bin/bash --
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.