ล่าม #! / bin / sh อ่านหรือไม่


66

ในbashหรือshผมคิดว่าอะไรที่เริ่มต้นด้วยการ#เป็นความคิดเห็น

แต่ในbashสคริปต์เราเขียน:

#!/bin/bash

และในสคริปต์ Python มี:

#!/bin/python

นี่หมายความว่า#โดยตัวของมันเองเป็นความคิดเห็นในขณะที่#!ไม่ได้เป็น?


1
และเมื่อคุณเริ่มมองหาที่โปรไฟล์ AppArmor #includeคุณจะเห็น นั่นก็#หมายความว่าไม่ได้เป็นความคิดเห็น

4
@ vasa1 แต่จุดสำคัญที่มักจะไม่ได้ชื่นชมเกี่ยวกับสาย hashbang ที่จุดเริ่มต้นของเชลล์สคริปต์คือการที่พวกเขามีความคิดเห็น
Eliah Kagan

คำตอบ:


100

#!บรรทัดใช้ก่อนสคริปต์ที่จะดำเนินการไม่สนใจแล้วเมื่อสคริปต์

คุณกำลังถามว่าความแตกต่างระหว่างเส้น Shebangกับความคิดเห็นทั่วไปคืออะไร

บรรทัดที่เริ่มต้นด้วยการ#!เป็นเพียงเท่าแสดงความคิดเห็นเป็นสายอื่น ๆ #ที่เริ่มต้นด้วย กรณีนี้เป็นจริงถ้า#!บรรทัดแรกของไฟล์หรือที่อื่น ๆ #!/bin/sh มีผลกระทบแต่มันก็ไม่ได้อ่านโดยล่ามของตัวเอง

#ไม่ใช่ความคิดเห็นในภาษาการเขียนโปรแกรมทั้งหมด แต่อย่างที่คุณทราบมันเป็นความคิดเห็นในเชลล์สไตล์ Bourne ซึ่งรวมถึงshและbash( รวมถึงเชลล์ที่ไม่ใช่บอร์นแบบส่วนใหญ่เช่นcsh) มันยังมีความคิดเห็นในหลาม และมันเป็นความคิดเห็นในไฟล์การกำหนดค่าที่หลากหลายซึ่งไม่ใช่สคริปต์จริงๆ (เหมือน/etc/fstab)

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

จุดประสงค์ของ#!สายไม่ให้ข้อมูลกับล่าม วัตถุประสงค์ของ#!เส้นคือการบอกระบบปฏิบัติการ (หรือกระบวนการสิ่งที่เปิดตัวล่าม) สิ่งที่จะใช้เป็นล่าม

  • หากคุณเรียกใช้สคริปต์เป็นไฟล์เรียกทำงานตัวอย่างเช่นโดยการรัน./script.shระบบจะพิจารณาบรรทัดแรกเพื่อดูว่าเริ่มต้นด้วย#!ตามด้วยศูนย์หรือมากกว่าเว้นวรรคตามด้วยคำสั่ง หากเป็นเช่นนั้นคำสั่งนั้นจะรันพร้อมชื่อของสคริปต์เป็นอาร์กิวเมนต์ ในตัวอย่างนี้มันทำงาน/bin/sh script.sh(หรือในทางเทคนิค/bin/sh ./script.sh)

  • หากคุณเรียกใช้สคริปต์โดยการเรียกล่ามอย่างชัดเจน#!จะไม่มีการปรึกษาสาย ดังนั้นหากคุณเรียกใช้sh script.shบรรทัดแรกจะไม่มีผลใด ๆ หากscript2.shบรรทัดแรกคือการ#!/usr/games/nibblesทำงานsh script2.shจะไม่พยายามเปิดสคริปต์ในnibbles(แต่./script2.shจะ)

คุณจะสังเกตเห็นว่าไม่ว่าในกรณีใดส่วนขยายของสคริปต์ ( .sh) ถ้ามีจะมีผลกระทบต่อการทำงานของสคริปต์ ในระบบที่คล้าย Unix สิ่งนี้จะไม่ส่งผลกระทบต่อวิธีการเรียกใช้สคริปต์ ในบางระบบเช่น Windows #!บรรทัด shebang อาจถูกละเว้นโดยระบบและส่วนขยายอาจเป็นตัวกำหนดว่าจะเรียกใช้สคริปต์อย่างไร (นี่ไม่ได้หมายความว่าคุณต้องให้ส่วนขยายสคริปต์ของคุณ แต่นี่เป็นหนึ่งในสาเหตุที่ทำให้คุณต้องแก้ไขให้ถูกต้อง)

#!ได้รับเลือกให้ตอบสนองวัตถุประสงค์นี้อย่างแม่นยำเพราะ #เริ่มแสดงความคิดเห็น #!สายสำหรับระบบที่ไม่ได้ล่ามและมันควรจะละเว้นโดยล่าม

Shebang Line สำหรับสคริปต์ทุบตี

คุณ (เดิม) กล่าวว่าคุณใช้#!/bin/shสำหรับbashสคริปต์ คุณควรทำเช่นนั้นหากสคริปต์ไม่ต้องการbashส่วนขยายใด ๆ- shจะต้องสามารถเรียกใช้สคริปต์ได้ shไม่เคย symlink bashไป มักจะรวมถึงในทุกระยะไกลล่าสุด Debian และ Ubuntu ระบบ , shเป็น symlink dashไป

Shebang Line สำหรับสคริปต์ Python

นอกจากนี้คุณยังกล่าวว่า (ในรุ่นแรกของคำถามของคุณก่อนที่จะแก้ไข) #!/bin/sh read by the interpretorที่คุณเริ่มต้นงูหลามสคริปต์ของคุณด้วย หากคุณหมายถึงอย่างแท้จริงคุณควรหยุดทำอย่างนั้น หากhello.pyเริ่มต้นด้วยบรรทัดนั้นการ./hello.pyรันจะดำเนินการ:

/bin/sh read by the interpretor hello.py

/bin/shจะพยายามเรียกใช้งานสคริปต์ที่เรียกว่าread(โดยใช้by the interpretor hello.pyเป็นอาร์กิวเมนต์) readจะ (หวังว่า) จะไม่พบและสคริปต์ Python ของคุณจะไม่ถูกมองเห็นโดยล่าม Python

หากคุณทำผิดพลาด แต่ไม่พบปัญหาที่ฉันอธิบายคุณอาจเรียกใช้ Scrips Python ของคุณโดยระบุล่าม (เช่นpython hello.py) ทำให้เกิดการละเว้นบรรทัดแรก เมื่อคุณแจกจ่ายสคริปต์ของคุณให้ผู้อื่นหรือใช้ในภายหลังในภายหลังอาจไม่ชัดเจนว่านี่เป็นสิ่งจำเป็นสำหรับพวกเขาในการทำงาน เป็นการดีที่สุดที่จะแก้ไขพวกเขาตอนนี้ หรืออย่างน้อยลบบรรทัดแรกทั้งหมดเพื่อว่าเมื่อพวกเขาล้มเหลวในการทำงานด้วย./ข้อความแสดงข้อผิดพลาดจะทำให้รู้สึก

สำหรับสคริปต์ Python ถ้าคุณรู้ว่าตัวแปล Python อยู่ที่ไหน (หรือจะเป็น) คุณสามารถเขียน#!บรรทัดได้ในลักษณะเดียวกัน:

#!/usr/bin/python

หรือถ้าเป็นสคริปต์ Python 3 คุณควรระบุpython3เนื่องจากpythonเกือบจะเป็น Python 2 เสมอ :

#!/usr/bin/python3

อย่างไรก็ตามปัญหาคือในขณะที่/bin/shควรจะมีอยู่เสมอและ/bin/bashเกือบจะมีอยู่ในระบบที่bashมาพร้อมกับระบบปฏิบัติการ Python อาจมีอยู่ในหลายสถานที่

ดังนั้นโปรแกรมเมอร์ Python หลายคนใช้สิ่งนี้แทน:

#!/usr/bin/env python

(หรือ#!/usr/bin/env python3สำหรับ Python 3)

สิ่งนี้ทำให้สคริปต์พึ่งพาenvการอยู่ใน "สถานที่ที่เหมาะสม" แทนที่จะอาศัยอยู่pythonในสถานที่ที่เหมาะสม นั่นเป็นสิ่งที่ดีเพราะ:

  • env/usr/binเกือบจะอยู่เสมอใน
  • ในระบบส่วนใหญ่แล้วแต่จำนวนใดpython ควรPATHเรียกใช้สคริปต์ของคุณเป็นหนึ่งที่ปรากฏขึ้นครั้งแรกในปีนี้ เริ่มต้นhello.pyด้วยการ#!/usr/bin/env pythonทำให้./hello.pyการทำงาน/usr/bin/env python hello.pyซึ่งเป็น (แทบ) python hello.pyเทียบเท่ากับการทำงาน

เหตุผลที่คุณไม่สามารถใช้#!pythonคือ:

  • คุณต้องการให้ล่ามที่ระบุจะได้รับจากเส้นทางที่แน่นอน (เช่นเริ่มต้นด้วย/)
  • กระบวนการเรียกจะดำเนินการในไดเรกทอรีปัจจุบันpython การค้นหาพา ธ เมื่อคำสั่งไม่มีเครื่องหมายทับเป็นลักษณะการทำงานของเชลล์เฉพาะ

บางครั้งหลามหรือสคริปต์อื่น ๆ ที่ไม่ได้เป็นสคริปต์เปลือกจะมีสาย shebang ที่เริ่มต้นด้วย#!/bin/sh ...ที่...บางรหัสอื่น ๆ บางครั้งสิ่งนี้ถูกต้องเพราะมีบางวิธีในการเรียกใช้เชลล์ที่เข้ากันได้กับ Bourne ( sh) พร้อมกับอาร์กิวเมนต์เพื่อเรียกใช้ตัวแปลภาษา Python (อาจมีหนึ่งในข้อโต้แย้งpython) อย่างไรก็ตามสำหรับจุดประสงค์ส่วนใหญ่#!/usr/bin/env pythonเรียบง่ายสง่างามและมีแนวโน้มที่จะทำงานในแบบที่คุณต้องการ

เส้น Shebang ในภาษาอื่น ๆ

ภาษาการเขียนโปรแกรมและสคริปต์จำนวนมากและรูปแบบไฟล์อื่น ๆ ใช้#เป็นความคิดเห็น สำหรับการใด ๆ ของพวกเขา, #!ไฟล์ในภาษาที่สามารถดำเนินการโดยโปรแกรมที่ใช้มันเป็นอาร์กิวเมนต์โดยระบุโปรแกรมบนบรรทัดแรกหลังจาก

ในบางภาษาการเขียนโปรแกรม#เป็นปกติไม่แสดงความคิดเห็น #!แต่เป็นกรณีพิเศษบรรทัดแรกจะถูกละเว้นถ้ามันเริ่มต้นด้วย สิ่งนี้จะช่วยให้การใช้#!ไวยากรณ์ง่ายขึ้นแม้ว่าจะ#ไม่ได้ให้ความคิดเห็นก็ตาม

Shebang Lines สำหรับไฟล์ที่ไม่ทำงานเป็นสคริปต์

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

แอปพลิเคชั่นบางตัวใช้พฤติกรรมนี้โดยเจตนา ตัวอย่างเช่นใน VMware .vmxไฟล์จะกำหนดเครื่องเสมือน คุณสามารถ "เรียกใช้" เครื่องเสมือนเสมือนว่าเป็นสคริปต์เพราะไฟล์เหล่านี้มีการทำเครื่องหมายปฏิบัติการและมีบรรทัด shebang ทำให้สามารถเปิดได้ในยูทิลิตี้ VMware

Shebang สายสำหรับไฟล์ที่ไม่ทำงานเป็น Scrips แต่ทำหน้าที่เหมือนสคริปต์อยู่แล้ว

rmลบไฟล์ มันไม่ใช่ภาษาสคริปต์ อย่างไรก็ตามไฟล์ที่เริ่มต้น#!/bin/rmและถูกทำเครื่องหมายว่าสามารถเรียกใช้งานได้และเมื่อคุณเรียกใช้ไฟล์นั้นrmจะถูกเรียกใช้โดยลบทิ้ง

สิ่งนี้มักจะเกิดขึ้นเมื่อ "ไฟล์ลบตัวเอง" แต่ไฟล์ไม่ได้ทำงานเลย นี่เป็นเหมือนสถานการณ์ที่อธิบายไว้ข้างต้นสำหรับ.vmxไฟล์

แต่ถึงกระนั้นเนื่องจาก#!บรรทัดอำนวยความสะดวกในการทำงานของคำสั่งง่าย ๆ (รวมถึงอาร์กิวเมนต์บรรทัดคำสั่ง) คุณสามารถดำเนินการสคริปต์บางอย่างด้วยวิธีนี้ เป็นตัวอย่างง่ายๆของ "สคริปต์" ที่ซับซ้อนกว่า#!/bin/rmให้พิจารณา:

#!/usr/bin/env tee -a

สิ่งนี้จะนำผู้ใช้ป้อนข้อมูลแบบโต้ตอบสะท้อนกลับไปที่ผู้ใช้ทีละบรรทัดและต่อท้ายไฟล์ "สคริปต์"

มีประโยชน์หรือไม่ ไม่มาก. แนวคิดที่น่าสนใจ? โดยสิ้นเชิง! ใช่. (ค่อนข้าง.)

แนวคิดการเขียนโปรแกรมที่คล้ายกัน / แนวคิด (เพียงเพื่อความสนุกสนาน)


@ รินซ์วินขอบคุณ! (Btw คำตอบนี้ไม่ได้เกิดที่อื่น ๆ ถ้านั่นคือสิ่งที่คุณสงสัย.)
Eliah Kagan

@Rinzwind ไม่ต้องกังวลกับ 8 upvotes หลังจาก 1 ชั่วโมงมีแนวโน้มที่จะเพิ่มมากขึ้นอีก :-)
guntbert

1
ถ้ามันถูกเพิกเฉยเสมอแล้ว-xธงPythons ทำอะไร?
gerrit

4
@gerrit คำถามที่ดี ในภาษาใดก็ตามที่ผู้บุกรุก / คอมไพเลอร์รายงานข้อความที่มีหมายเลขบรรทัดเนื้อหาของความคิดเห็นจะถูกละเว้น แต่ความคิดเห็นจะยังคงนับรวมอยู่ การเพิ่มความคิดเห็นหรือบรรทัดว่างก่อนที่บรรทัดของโค้ดจะส่งผลให้บรรทัดของโค้ดนั้นมีจำนวนเพิ่มขึ้น -x"ข้าม [s] บรรทัดแรก ... " บรรทัดที่ 2 รับหมายเลข1แทน2บรรทัดที่ 3 2แทน3เป็นต้นนี่คือเหตุผลที่คุณไม่ควรใช้แฟล็กนั้น ;) -xมีไว้สำหรับการเขียนสคริปต์บนระบบปฏิบัติการที่ไม่ใช่ยูนิกซ์ที่มีรูปแบบ Shebang เหมือนกับที่ไม่ได้ขึ้นต้นด้วย#(ไม่ใช่ความคิดเห็นของ Python)
Eliah Kagan

4
ใน Perl ถ้าล่ามจะเริ่มต้นโดยตรง ( perl script.plกับ./script.pl) แล้วล่ามจะอ่านบรรทัด shebang -wเพื่อแยกธงเช่น ไม่แนะนำให้ใช้คุณลักษณะนี้
OrangeDog

7

Shebang เป็นลำดับอักขระที่ประกอบด้วยเครื่องหมายหมายเลขอักขระและเครื่องหมายอัศเจรีย์ (เช่น "#!") เมื่อมันเกิดขึ้นเป็นอักขระสองตัวเริ่มต้นในบรรทัดเริ่มต้นของสคริปต์

ภายใต้ระบบปฏิบัติการ * ระวังเมื่อสคริปต์ที่เริ่มต้นด้วย shebang ถูกเรียกใช้งานตัวโหลดโปรแกรมจะแยกวิเคราะห์บรรทัดเริ่มต้นที่เหลือของสคริปต์เป็นคำสั่งล่าม โปรแกรมล่ามที่ระบุจะถูกเรียกใช้แทนโดยส่งผ่านเป็นอาร์กิวเมนต์สำหรับพา ธ ที่ถูกใช้ครั้งแรกเมื่อพยายามเรียกใช้สคริปต์ ตัวอย่างเช่นหากสคริปต์ตั้งชื่อด้วยพา ธ "path / to / your-script" และเริ่มต้นด้วยบรรทัดต่อไปนี้:

#!/bin/sh

จากนั้นตัวโหลดโปรแกรมจะได้รับคำสั่งให้รันโปรแกรม "/ bin / sh" แทนเช่น Bourne shell หรือเชลล์ที่เข้ากันได้โดยผ่าน "path / to / your-script" เป็นอาร์กิวเมนต์แรก

ดังนั้นสคริปต์จะถูกตั้งชื่อด้วยพา ธ "path / to / python-script" และเริ่มต้นด้วยบรรทัดต่อไปนี้:

#!/bin/python

จากนั้นโปรแกรมที่โหลดจะได้รับคำสั่งให้รันโปรแกรม "/ bin / python" แทนเช่น Python interpreter ผ่าน "path / to / python-script" เป็นอาร์กิวเมนต์แรก

ในระยะสั้น "#" จะแสดงความคิดเห็นออกบรรทัดในขณะที่ลำดับตัวละคร "#!" ที่เกิดขึ้นเป็นอักขระสองตัวแรกในบรรทัดเริ่มต้นของสคริปต์มีความหมายที่ระบุไว้ข้างต้น

สำหรับรายละเอียดดูทำไมสคริปต์บางตัวเริ่มต้นด้วย #! ...

ที่มา:บางส่วนของคำตอบนี้ได้มา (โดยมีการดัดแปลงเล็กน้อย) จากShebang (Unix)ในภาษาอังกฤษ Wikipedia (โดยผู้มีส่วนร่วม Wikipedia ) บทความนี้ได้รับอนุญาตภายใต้ CC-BY-SA 3.0เช่นเดียวกับเนื้อหาของผู้ใช้ที่นี่ใน AU ดังนั้นจึงอนุญาตให้มีการสืบทอดมาพร้อมกับการระบุแหล่งที่มา


4

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

มารยาท: http://en.wikipedia.org/wiki/Shebang_%28Unix%29

โดยทั่วไปถ้าไฟล์นั้นสามารถใช้งานได้ แต่จริงๆแล้วไม่ใช่โปรแกรมที่เรียกใช้งานได้ (ไบนารี่) และมีบรรทัดดังกล่าวอยู่โปรแกรมที่ระบุหลัง #! เริ่มต้นด้วยชื่อสคริปต์และอาร์กิวเมนต์ทั้งหมด อักขระสองตัวนี้ # และ! ต้องเป็นสองไบต์แรกในไฟล์!

ข้อมูลโดยละเอียด: http://wiki.bash-hackers.org/scripting/basics#the_shebang


0

ไม่มันถูกใช้โดยการexecเรียกระบบของเคอร์เนล Linux เท่านั้นและถือว่าเป็นความคิดเห็นโดยล่าม

เมื่อคุณทุบตี:

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

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