shebang พิจารณาเชลล์ที่รันสคริปต์หรือไม่


84

นี่อาจเป็นคำถามที่โง่ แต่ฉันก็ยังถาม ถ้าฉันได้ประกาศ shebang

#!/bin/bash 

ในตอนต้นของmy_shell_script.shฉันจะต้องเรียกใช้สคริปต์นี้โดยใช้ทุบตี

[my@comp]$bash my_shell_script.sh

หรือฉันสามารถใช้เช่น

[my@comp]$sh my_shell_script.sh

และสคริปท์ของฉันกำหนดเชลล์ที่กำลังใช้งานอยู่โดยใช้ shebang? มันเกิดขึ้นเหมือนกันกับkshเชลล์ไหม? ฉันใช้ AIX


6
มีความสับสนเล็กน้อยในส่วนของคุณ: เมื่อคุณทำ "_some_shell some_script" มันจะเริ่ม _some_shell และขอให้ตีความ some_script ดังนั้นไม่ถ้าคุณทำ "sh my_shell_script.sh" มันจะไม่ตีความ Shebang แต่จะแปลสคริปต์เป็น sh แทน หากต้องการใช้ shebang: 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
Olivier Dulac

คำตอบ:


117

shebang #!เป็นตัวอย่างการอ่านของมนุษย์ของจำนวนมายากลประกอบด้วยสตริงไบต์0x23 0x21ซึ่งถูกใช้โดยexec()ครอบครัวของฟังก์ชั่นเพื่อตรวจสอบว่าไฟล์ที่จะดำเนินการเป็นสคริปต์หรือไบนารี เมื่อ shebang ปรากฏขึ้นให้exec()เรียกใช้ไฟล์ปฏิบัติการที่ระบุหลังจาก shebang แทน

โปรดทราบว่านี่หมายความว่าหากคุณเรียกใช้สคริปต์โดยการระบุล่ามในบรรทัดคำสั่งเช่นเดียวกับที่ทำในทั้งสองกรณีที่กำหนดในคำถามexec()จะดำเนินการล่ามที่ระบุในบรรทัดคำสั่งมันจะไม่ดูสคริปต์ด้วยซ้ำ

ดังนั้นกับคนอื่น ๆ ได้ตั้งข้อสังเกตถ้าคุณต้องการexec()ที่จะเรียกล่ามระบุในบรรทัด shebang ./my_shell_script.shสคริปต์จะต้องมีบิตปฏิบัติการตั้งค่าและเรียกว่าเป็น

ลักษณะการทำงานนั้นง่ายต่อการสาธิตด้วยสคริปต์ต่อไปนี้:

#!/bin/ksh
readlink /proc/$$/exe

คำอธิบาย:

  • #!/bin/kshกำหนดkshให้เป็นล่าม

  • $$ เก็บ PID ของกระบวนการปัจจุบัน

  • /proc/pid/exe คือ symlink ไปยัง executable ของกระบวนการ (อย่างน้อยบน Linux; บน AIX, /proc/$$/object/a.out เป็นลิงค์ไปยัง executable)

  • readlink จะเอาท์พุทค่าของลิงค์สัญลักษณ์

ตัวอย่าง:

หมายเหตุ : ฉันแสดงให้เห็นถึงนี้บน Ubuntu ที่เปลือกเริ่มต้น/bin/shเป็น symlink ไปรีบ IE /bin/dashและ/bin/kshเป็น symlink ไป/etc/alternatives/kshซึ่งจะเป็น symlink /bin/pdkshไป

$ chmod +x getshell.sh
$ ./getshell.sh 
/bin/pdksh
$ bash getshell.sh 
/bin/bash
$ sh getshell.sh 
/bin/dash

ขอบคุณ Thomas สำหรับคำตอบนี้ แกล้งเราเปิดตัวสคริปต์เป็นกระบวนการลูกจาก Node.js หรือ Java หรืออะไรก็ตาม เราสามารถเปิดตัวกระบวนการ "exec" จากนั้น exec จะรันเชลล์สคริปต์ได้หรือไม่ ฉันถาม beause ฉันกำลังมองหาคำตอบสำหรับคำถามนี้: stackoverflow.com/questions/41067872/ …
Alexander Mills

1
@AlexanderMills คำที่exec()อ้างถึงในคำตอบนี้คือการเรียกของระบบคำสั่งexecคือ shell builtin ซึ่งเป็นสาเหตุที่คุณไม่สามารถเรียกใช้exec โปรแกรมจาก Node.js หรือ Java อย่างไรก็ตามคำสั่งเชลล์ที่เรียกใช้โดยเช่นRuntime.exec()ใน Java จะถูกประมวลผลในที่สุดโดยการexec()เรียกระบบ
โทมัสนีแมน

อืมใช่ฉันคุ้นเคยกับ Java API ที่คุณเพิ่งพูดถึงฉันสงสัยว่ามีวิธีที่จะเรียกการเรียก exec ระดับล่าง () จาก Node.js อย่างไร
Alexander Mills

@AlexanderMills ฉันจะจินตนาการchild_process.{exec(),execFile(),spawn()} ทั้งหมดจะถูกดำเนินการโดยใช้ C exec()(ผ่านprocess)
Thomas Nyman

10

ใช่แล้ว. โดยวิธีการที่มันไม่ใช่คำถามที่โง่ การอ้างอิงสำหรับคำตอบของฉันคือที่นี่ เริ่มต้นสคริปต์ด้วย #!

  • มันถูกเรียกว่า Shebang หรือเส้น "ปัง"

  • มันไม่มีอะไรนอกจากเส้นทางที่แน่นอนไปยังล่าม Bash

  • ประกอบด้วยเครื่องหมายตัวเลขและอักขระเครื่องหมายอัศเจรีย์ (#!) ตามด้วยเส้นทางแบบเต็มไปยังล่ามเช่น / bin / bash

    สคริปต์ทั้งหมดภายใต้ Linux ดำเนินการโดยใช้ล่ามที่ระบุในบรรทัดแรกเกือบทุกสคริปต์ทุบตีมักจะเริ่มต้นด้วย #! / bin / bash (สมมติว่าติดตั้ง Bash ใน / bin) ทำให้มั่นใจได้ว่า Bash จะใช้ในการตีความสคริปต์แม้ ถ้ามันถูกดำเนินการภายใต้เปลือกอื่น shebang ได้รับการแนะนำโดย Dennis Ritchie ระหว่าง Version 7 Unix และ 8 ที่ Bell Laboratories จากนั้นจะถูกเพิ่มเข้าไปยังบรรทัด BSD ที่ Berkeley

ไม่สนใจบรรทัดล่าม (shebang)

หากคุณไม่ได้ระบุบรรทัดล่ามค่าเริ่มต้นมักจะเป็น / bin / sh แต่ขอแนะนำให้คุณตั้งค่า #! / bin / bash


3
ในการทำอย่างละเอียดเคอร์เนลเท่านั้นที่รู้วิธีรันไบนารีที่เชื่อมโยงแบบสแตติกและตำแหน่งที่จะค้นหาข้อมูลล่ามสำหรับผู้อื่น (ฟิลด์พิเศษในไบนารีหรือบรรทัด Shebang) โดยทั่วไปการเรียกใช้งานเชลล์สคริปต์หมายถึงการติดตามบรรทัด shebang ไปยังเชลล์และจากนั้นติดตามฟิลด์ DT_INTERP ในเชลล์ไบนารี่ไปยังตัวเชื่อมโยงแบบไดนามิก
Simon Richter

5
นอกจากนี้โปรดทราบว่าสิ่งนี้ไม่ จำกัด เฉพาะเชลล์สคริปต์ ไฟล์สคริปต์ตามข้อความทั้งหมดใช้สิ่งนี้ เช่น#!/usr/bin/perl #!/usr/local/bin/python #!/usr/local/bin/rubyรายการ Shebang ทั่วไปอื่น ๆ ที่ใช้เพื่อสนับสนุนระบบหลายระบบคือการใช้ env เพื่อค้นหาล่ามที่คุณต้องการใช้เช่น#!/usr/bin/env perl #!/usr/bin/env python
sambler

@ sambler พูดถึงenvซึ่งควรจะชอบจริง? Python และ Perl มักจะใช้envในขณะที่ shellscripts จะถูกละไว้และชีบชี้ไปที่เชลล์ที่มีปัญหา
polemon

1
@polemon ซึ่งเป็นที่ต้องการน้อยกว่าและมากกว่านั้นบนเส้นทางที่แตกต่างกันไป เชลล์พื้นฐานอยู่ในเส้นทางเดียวกันในทุกระบบ เวอร์ชันล่าสุดของ perl และ python สามารถติดตั้งในตำแหน่งต่าง ๆ บนระบบที่แตกต่างกันดังนั้นการใช้ env อนุญาตให้ shebang เดียวกันทำงานได้ตลอดเวลาซึ่งเป็นสาเหตุที่ env ถูกใช้งานมากขึ้นด้วยสคริปต์ perl และ python มากกว่าเชลล์สคริปต์
sambler

envเพื่อค้นหาโปรแกรมใน $ PATH เป็นบิตของการแฮ็ค ไม่ได้ตั้งค่าตัวแปรสภาพแวดล้อมเช่นชื่อที่แสดงถึง $ PATH อาจเป็นผลลัพธ์ที่แตกต่างกันสำหรับผู้ใช้ที่ต่างกัน แต่มันช่วยให้สคริปต์ทำงานได้โดยไม่ต้องดัดแปลงระบบใด ๆ ซึ่งทำให้ล่ามภาษา Perl ในสถานที่แปลก ๆ
John Mahowald

4

การ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ถึงแม้ว่ามันจะพูดถึงมันในส่วนของเหตุผลและในรูปแบบ "ถ้าสคริปต์ที่สนับสนุนการดำเนินการได้รับการสนับสนุนจากระบบ อาจเกิดขึ้น "


1
การเรียกใช้./somethingจากเชลล์จะไม่ผ่านเส้นทางแบบเต็มไปยังexecแต่เส้นทางที่ป้อนจะถูกต้องทั้งหมด คุณสามารถแก้ไขสิ่งนี้ในคำตอบของคุณ? ทำecho "$0"ในสคริปต์ของคุณและคุณจะเห็นเป็นกรณีนี้
AndiDog

2

ในความเป็นจริงถ้าคุณเอามันปฏิบัติการที่ระบุไว้ในบรรทัด Shebang เป็นเพียงปฏิบัติการ มันเหมาะสมแล้วที่จะใช้ตัวแปลข้อความเป็นแบบปฏิบัติการได้ แต่ไม่จำเป็น เพื่อความชัดเจนและการสาธิตฉันทำแบบทดสอบที่ค่อนข้างไร้ประโยชน์:

#!/bin/cat
useless text
more useless text
still more useless text

ตั้งชื่อ test.txt ไฟล์และการตั้งค่าบิต exectuable chmod u+x test.txtแล้ว "เรียกว่า" ./test.txtมัน ตามที่คาดไว้เนื้อหาของไฟล์จะถูกส่งออก ในกรณีนี้แมวจะไม่เพิกเฉยต่อเส้น Shebang มันเพียงแค่ส่งออกสายทั้งหมด ล่ามที่มีประโยชน์ใด ๆ จึงควรละเว้นบรรทัด shebang นี้ สำหรับ bash, perl และ PHP เป็นเพียงบรรทัดความคิดเห็น ใช่แล้วสิ่งเหล่านี้ไม่สนใจเส้น Shebang


-1

จากสิ่งที่ฉันรวบรวมเมื่อใดก็ตามที่ไฟล์มีชุดบิตที่ปฏิบัติการได้และถูกเรียกใช้เคอร์เนลจะวิเคราะห์ส่วนหัวของไฟล์เพื่อกำหนดวิธีดำเนินการ (เท่าที่ฉันรู้คุณสามารถเพิ่มตัวจัดการแบบกำหนดเองสำหรับรูปแบบไฟล์แบบกำหนดเองผ่าน LKMs) หากไฟล์ดูเหมือนจะเป็นไฟล์ข้อความที่มี #! การรวมกันในตอนเริ่มต้นการประมวลผลจะถูกส่งไปยังไฟล์ปฏิบัติการอื่น (โดยปกติจะเป็นเชลล์ประเภท) เส้นทางที่จะถูกระบุโดยตรงหลังจาก Shebang ดังกล่าวในบรรทัดเดียวกัน เคอร์เนลจะดำเนินการเพื่อดำเนินการเชลล์และส่งไฟล์เพื่อจัดการ

กล่าวโดยสรุปก็ไม่สำคัญว่าเชลล์ใดที่คุณเรียกใช้สคริปต์ด้วย - เคอร์เนลจะส่งการเรียกใช้งานไปยังที่เหมาะสมด้วยวิธีใดวิธีหนึ่ง


4
มีความแตกต่างระหว่างการทำเครื่องหมายและbash ./myscript.sh ./myscript.sh
CVn

คุณหมายถึงอะไรโดย "ความแตกต่างที่ทำเครื่องหมายไว้" นี้
jrara

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