การexec
เรียกระบบของเคอร์เนล Linux เข้าใจ shebangs ( #!
) โดยกำเนิด
เมื่อคุณทุบตี:
./something
บน Linux นี้เรียกสายระบบกับเส้นทางexec
./something
บรรทัดของเคอร์เนลนี้ถูกเรียกบนไฟล์ที่ส่งผ่านไปยังexec
: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
#!
มันอ่านไบต์แรกของไฟล์และเปรียบเทียบพวกเขาไป
หากการเปรียบเทียบเป็นจริงดังนั้นส่วนที่เหลือของบรรทัดจะถูกวิเคราะห์โดยเคอร์เนล Linux ซึ่งทำให้การexec
เรียกด้วยพา ธ/usr/bin/env python
และไฟล์ปัจจุบันเป็นอาร์กิวเมนต์แรก:
/usr/bin/env python /path/to/script.py
และใช้ได้กับภาษาสคริปต์ใด ๆ ที่ใช้#
เป็นอักขระความคิดเห็น
และใช่คุณสามารถสร้างวงวนไม่สิ้นสุดด้วย:
printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a
Bash รู้จักข้อผิดพลาด:
-bash: /a: /a: bad interpreter: Too many levels of symbolic links
#!
เพิ่งเกิดขึ้นกับมนุษย์อ่านได้ แต่ไม่จำเป็น
หากไฟล์เริ่มต้นด้วยไบต์ที่แตกต่างกันการexec
เรียกของระบบจะใช้ตัวจัดการที่แตกต่างกัน ตัวจัดการในตัวที่สำคัญที่สุดสำหรับไฟล์เรียกทำงานของ ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305ซึ่งจะตรวจสอบไบต์7f 45 4c 46
(ซึ่งเกิดขึ้นกับมนุษย์ด้วย) สามารถอ่านได้สำหรับ.ELF
) เรามายืนยันว่าโดยการอ่าน 4 ไบต์แรกของ/bin/ls
ซึ่งเป็นปฏิบัติการ ELF:
head -c 4 "$(which ls)" | hd
เอาท์พุท:
00000000 7f 45 4c 46 |.ELF|
00000004
ดังนั้นเมื่อเคอร์เนลเห็นไบต์เหล่านั้นจะใช้ไฟล์ ELF วางลงในหน่วยความจำอย่างถูกต้องและเริ่มกระบวนการใหม่ด้วย ดูเพิ่มเติมที่: https://stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861
ในที่สุดคุณสามารถเพิ่มตัวจัดการ Shebang ของคุณเองด้วยbinfmt_misc
กลไก ตัวอย่างเช่นคุณสามารถเพิ่มตัวจัดการที่กำหนดเองสำหรับ.jar
ไฟล์ กลไกนี้ยังรองรับตัวจัดการโดยนามสกุลไฟล์ การประยุกต์ใช้ก็คือการโปร่งใสทำงาน executables ของสถาปัตยกรรมที่แตกต่างกันกับ QEMU
ฉันไม่คิดว่า POSIX จะระบุ shebangs อย่างไรก็ตาม: https://unix.stackexchange.com/a/346214/32558ถึงแม้ว่ามันจะพูดถึงมันในส่วนของเหตุผลและในรูปแบบ "ถ้าสคริปต์ที่สนับสนุนการดำเนินการได้รับการสนับสนุนจากระบบ อาจเกิดขึ้น "
chmod +x my_shell_script.sh ; /path/to/my_shell_script.sh # or ./my_shell_script.sh if you happen to be in its directory