ส่วนหัวเชลล์สคริปต์ (#! / bin / sh vs #! / bin / csh)


92

เหตุใดไฟล์สคริปต์ทั้งหมดจึงเริ่มต้นด้วยไฟล์

#!/bin/sh

หรือด้วย

#!/bin/csh

จำเป็นหรือไม่ จุดประสงค์ของสิ่งนี้คืออะไร? แล้วทั้งสองต่างกันอย่างไร?


1
สำหรับสคริปต์ csh คุณควรใช้#!/bin/csh -f; คำสั่ง-fบอกเชลล์ไม่ให้แหล่งที่มาของผู้ใช้.loginและ.cshrcทำให้สคริปต์ทำงานได้เร็วขึ้นและหลีกเลี่ยงการพึ่งพาการตั้งค่าของผู้ใช้ (หรือดีกว่านั้นอย่าเขียนสคริปต์ csh) อย่าใช้-fสำหรับสคริปต์ sh หรือ bash มันไม่ได้มีความหมายเหมือนกัน
Keith Thompson

คำตอบ:


99

สิ่งนี้เรียกว่าShebang:

http://en.wikipedia.org/wiki/Shebang_(Unix)

#! ล่าม [ตัวเลือก - อาร์กิวเมนต์]

Shebang จะเกี่ยวข้องก็ต่อเมื่อสคริปต์มีสิทธิ์ในการดำเนินการ (เช่น chmod u + x script.sh)

เมื่อเชลล์รันสคริปต์จะใช้ตัวแปลที่ระบุ

ตัวอย่าง:

#!/bin/bash
# file: foo.sh
echo 1

$ chmod u+x foo.sh
$ ./foo.sh
  1

@Kolob Canyon คุณไม่จำเป็นต้องทำ แต่มันสามารถช่วยบรรณาธิการบางคนด้วยการเน้นไวยากรณ์ (แม้ว่าโดยปกติจะมีวิธีอื่น ๆ ในการบรรลุสิ่งเดียวกัน): unix.stackexchange.com/a/88730/193985
Braham Snyder

42

#!เส้นบอกเคอร์เนล (โดยเฉพาะการดำเนินงานของexecveระบบโทร) ว่าโปรแกรมนี้ถูกเขียนในภาษาแปล; ชื่อพา ธ สัมบูรณ์ที่ตามหลังระบุตัวแปล โปรแกรมที่คอมไพล์เป็นรหัสเครื่องจะเริ่มต้นด้วยลำดับไบต์ที่แตกต่างกัน - บน Unixes ที่ทันสมัยที่สุด7f 45 4c 46( ^?ELF) ซึ่งระบุว่าเป็นเช่นนั้น

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

./script arg1 arg2 arg3 ...

โดย./scriptเริ่มต้นด้วยพูด#! /usr/bin/perlราวกับว่าบรรทัดคำสั่งเป็นจริง

/usr/bin/perl ./script arg1 arg2 arg3

หรือตามที่คุณได้เห็นคุณสามารถใช้ในการเขียนสคริปต์ตั้งใจที่จะตีความโดย#! /bin/shsh

#!สายการประมวลผลเฉพาะถ้าคุณโดยตรงเรียกใช้สคริปต์ ( ./scriptในบรรทัดคำสั่ง); ไฟล์ต้องสามารถเรียกใช้งานได้ด้วย ( chmod +x script) ถ้าคุณทำสายไม่จำเป็น (และจะถูกละเว้นถ้ามี) และไฟล์ไม่ต้องมีการปฏิบัติการ จุดของคุณลักษณะที่จะช่วยให้คุณโดยตรงเรียกใช้โปรแกรมตีความภาษาโดยไม่ต้องรู้ว่าสิ่งที่ภาษาที่พวกเขาจะถูกเขียนใน. (Do - คุณจะพบว่าโปรแกรมที่ดีหุ้นจำนวนมากในความเป็นจริงการใช้คุณลักษณะนี้.)sh ./script#!grep '^#!' /usr/bin/*

กฎบางประการในการใช้คุณลักษณะนี้มีดังนี้

  • #!ต้องเป็นสองคนแรกมากไบต์ในแฟ้ม โดยเฉพาะอย่างยิ่งไฟล์จะต้องอยู่ในการเข้ารหัสที่เข้ากันได้กับ ASCII (เช่น UTF-8 จะทำงานได้ แต่ UTF-16 จะไม่ทำงาน) และต้องไม่ขึ้นต้นด้วย "เครื่องหมายลำดับไบต์" มิฉะนั้นเคอร์เนลจะไม่รู้จักว่าเป็น#!สคริปต์
  • เส้นทางหลัง#!ต้องเป็นเส้นทางสัมบูรณ์ (เริ่มต้นด้วย/) ไม่สามารถมีช่องว่างแท็บหรืออักขระขึ้นบรรทัดใหม่
  • มันเป็นรูปแบบที่ดี แต่ไม่จำเป็นต้องใส่ช่องว่างระหว่างและ#! /อย่าใส่มากกว่าหนึ่งช่องที่นั่น
  • คุณไม่สามารถใส่ตัวแปรเชลล์ใน#!บรรทัดได้พวกมันจะไม่ถูกขยาย
  • คุณสามารถใส่อาร์กิวเมนต์บรรทัดคำสั่งหนึ่งรายการหลังพา ธ สัมบูรณ์โดยคั่นด้วยช่องว่างเดียว เช่นเดียวกับเส้นทางสัมบูรณ์อาร์กิวเมนต์นี้ต้องไม่มีช่องว่างแท็บหรืออักขระขึ้นบรรทัดใหม่ บางครั้งสิ่งนี้จำเป็นในการทำให้สิ่งต่างๆทำงานได้ ( #! /usr/bin/awk -f) บางครั้งมันก็มีประโยชน์ ( #! /usr/bin/perl -Tw) น่าเสียดายที่คุณไม่สามารถใส่อาร์กิวเมนต์สองตัวขึ้นไปหลังพา ธ สัมบูรณ์ได้
  • บางคนจะบอกให้ใช้#! /usr/bin/env interpreterแทน#! /absolute/path/to/interpreter. นี่เป็นความผิดพลาดเกือบตลอดเวลา มันทำให้พฤติกรรมของโปรแกรมของคุณขึ้นอยู่กับ$PATHตัวแปรของผู้ใช้ที่เรียกใช้สคริปต์ และไม่ใช่ทุกระบบenvในตอนแรก
  • โปรแกรมที่จำเป็นsetuidหรือsetgidสิทธิพิเศษนี้ไม่สามารถใช้#!; ต้องรวบรวมเป็นรหัสเครื่อง (หากคุณไม่รู้ว่าอะไรsetuidคืออะไรอย่ากังวลกับสิ่งนี้)

เกี่ยวกับcshมันเกี่ยวข้องกับshโดยประมาณอย่างที่ Nutrimat Advanced Tea Substituteทำกับชา แต่ก็มี (หรือมากกว่ามี; การใช้งานที่ทันสมัยของshได้จับขึ้นไป) จำนวนหนึ่งได้เปรียบกว่าshสำหรับการใช้งานแบบโต้ตอบ แต่ใช้มัน (หรือลูกหลานของมันtcsh) สำหรับการเขียนสคริปต์คือมักจะผิดพลาด shหากคุณกำลังใหม่ที่จะเปลือกสคริปต์โดยทั่วไปผมขอแนะนำให้คุณไม่สนใจมันและมุ่งเน้น หากคุณกำลังใช้cshญาติเป็นเชลล์ล็อกอินของคุณให้เปลี่ยนไปใช้bashหรือzshเพื่อให้ภาษาคำสั่งแบบโต้ตอบจะเหมือนกับภาษาสคริปต์ที่คุณกำลังเรียนรู้


Linux เวอร์ชันล่าสุดอนุญาตให้ล่ามที่ระบุเป็นสคริปต์ เป็นเรื่องปกติที่จะเว้นช่องว่างหลัง#!; ไม่มีความคิดเห็นว่าเป็นสไตล์ที่ดีหรือไม่ ดูคำถามนี้และคำตอบของฉันสำหรับการอภิปรายข้อดีข้อเสียของการ#!/usr/bin/envแฮ็ก
Keith Thompson

@KeithThompson ฉันรู้สึกประทับใจ Linux เป็นตัวแปร Unix ทั่วไปเพียงตัวเดียวที่อนุญาตให้ล่ามเป็นสคริปต์ดังนั้นจึงไม่ใช่สิ่งที่ต้องพึ่งพา ตั้งแต่ฉันเขียนสิ่งนี้ฉันได้พบกับสถานการณ์ที่สิ่งที่#!/usr/bin/envถูกต้องคือ แต่ฉันยังคงมีความเห็นว่ามันเป็นความคิดที่ไม่ดี
zwol

4

สิ่งนี้กำหนดเชลล์ (ตัวแปลคำสั่ง) ที่คุณใช้สำหรับการตีความ / รันสคริปต์ของคุณ แต่ละเชลล์มีความแตกต่างกันเล็กน้อยในวิธีที่มันโต้ตอบกับผู้ใช้และรันสคริปต์ (โปรแกรม)

เมื่อคุณพิมพ์คำสั่งที่พรอมต์ Unix แสดงว่าคุณกำลังโต้ตอบกับเชลล์

เช่น#!/bin/cshหมายถึง C-shell, /bin/tcsht-shell, /bin/bashbash shell เป็นต้น

คุณสามารถบอกได้ว่าคุณใช้เชลล์แบบโต้ตอบใด

 echo $SHELL

คำสั่งหรืออีกทางหนึ่ง

 env | grep -i shell

คุณสามารถเปลี่ยนเชลล์คำสั่งของคุณด้วยchshคำสั่ง

แต่ละชุดมีชุดคำสั่งและวิธีการกำหนดตัวแปรและชุดโครงสร้างการเขียนโปรแกรมที่แตกต่างกันเล็กน้อย ตัวอย่างเช่นคำสั่ง if-else ที่มี bash ดูแตกต่างจากคำสั่งใน C-shell

หน้านี้อาจเป็นที่สนใจเนื่องจาก "แปล" ระหว่างคำสั่ง bash และ tcsh / ไวยากรณ์

การใช้คำสั่งในเชลล์สคริปต์ช่วยให้คุณรันโปรแกรมโดยใช้เชลล์อื่น ตัวอย่างเช่นฉันใช้tcshเชลล์แบบโต้ตอบ แต่มักจะเรียกใช้สคริปต์ bash โดยใช้ / bin / bash ในไฟล์สคริปต์

นอกเหนือจาก:

แนวคิดนี้ขยายไปยังสคริปต์อื่น ๆ ด้วย ตัวอย่างเช่นหากคุณตั้งโปรแกรมใน Python คุณจะต้องใส่

 #!/usr/bin/python

ที่ด้านบนสุดของโปรแกรม Python


จำเป็นหรือไม่? ฉันจะรู้ได้อย่างไรว่าฉันใช้เชลล์อะไรอยู่
วันทูทรี

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

1
@OneTwoThree ระบบส่วนใหญ่มีเชลล์มาตรฐานหากคุณเขียนสคริปต์ bash หรือ csh คุณจะโอเค สิ่งที่พวกเขาใช้ในการโต้ตอบไม่สำคัญนั่นคือความสวยงามของเช่น!#/bin/bashคำสั่ง มันบอกระบบว่าจะใช้เชลล์อะไรในการรันเชลล์สคริปต์ของคุณ
Levon

ค่าของ$SHELLไม่จำเป็นต้องบอกคุณว่าคุณกำลังใช้เชลล์ใดอยู่ในขณะนี้ โดยปกติจะบอกเชลล์เริ่มต้นของคุณ ชุด tcsh $versionและ$tcsh; $BASH_VERSIONชุดทุบตี ไม่ใช่ทุกเปลือกที่จำเป็นต้องมีกลไกที่คล้ายกัน
Keith Thompson

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