เชลล์ล่ามคนใดเรียกใช้สคริปต์โดยไม่มี shebang?


17

สมมติว่าเชลล์เริ่มต้นสำหรับบัญชีของฉันคือ zsh แต่ฉันเปิดเทอร์มินัลและยิงทุบตีและเรียกใช้สคริปต์ชื่อprac002.shซึ่งล่ามเชลล์ใดที่จะใช้ในการรันสคริปต์ zsh หรือ bash? ลองพิจารณาตัวอย่างต่อไปนี้:

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % sudo cat /etc/passwd | grep papagolf
[sudo] password for papagolf: 
papagolf:x:1000:1001:Rex,,,:/home/papagolf:/usr/bin/zsh
# papagolf's default shell is zsh

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % bash
# I fired up bash. (See that '%' prompt in zsh changes to '$' prompt, indicating bash.)

papagolf@Sierra:~/My Files/My Programs/Learning/Shell$ ./prac002.sh 
Enter username : Rex
Rex
# Which interpreter did it just use?

** แก้ไข: ** นี่คือเนื้อหาของสคริปต์

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh 
read -p "Enter username : " uname
echo $uname

สคริปต์พูดอะไรที่ด้านบนสุด
Michael Homer

@MichaelHomer: ไม่มีอะไร ฉันเพิ่งแก้ไขคำถามเพื่อเพิ่มเนื้อหาของสคริปต์ สคริปต์มีสิทธิ์การใช้งานได้
7_R3X

หากคุณต้องการใช้เชลล์ปัจจุบันให้ใส่จุดดังกล่าวตามที่. prac002.shสมมติว่าสคริปต์ของคุณอยู่ใน dir ปัจจุบัน
thecarpy

1
Run . ./prac002.shและมันจะทำงานกับเชลล์ปัจจุบันนั่นคือ dot ( .), space () ตามด้วยพา ธ ของสคริปต์ของคุณ มันเรียกว่า dot-sourcing script ของคุณ ;-)
thecarpy

คำตอบ:


19

เนื่องจากสคริปต์ไม่ได้ขึ้นต้นด้วย#!บรรทัด shebang ที่ระบุว่าต้องการใช้ล่ามPOSIX จึงกล่าวว่า :

หากexecl()ฟังก์ชั่นล้มเหลวเนื่องจากข้อผิดพลาดเทียบเท่ากับข้อผิดพลาด [ENOEXEC] ที่กำหนดไว้ในระดับเสียงของระบบอินเตอร์เฟซ POSIX.1-2008, เชลล์จะดำเนินการคำสั่งเทียบเท่ากับการเรียกเปลือกที่มีชื่อพา ธ ที่เกิดจากการค้นหาเป็นครั้งแรก ตัวถูกดำเนินการโดยมีอาร์กิวเมนต์ที่เหลืออยู่ส่งผ่านไปยังเชลล์ใหม่ยกเว้นว่าค่าของ "$ 0" ในเชลล์ใหม่อาจถูกตั้งค่าเป็นชื่อคำสั่ง หากไฟล์ที่ปฏิบัติการไม่ใช่ไฟล์ข้อความเชลล์อาจข้ามการประมวลผลคำสั่งนี้ ในกรณีนี้มันจะเขียนข้อความข้อผิดพลาดและจะกลับสถานะทางออกที่ 126

การใช้ถ้อยคำนั้นคลุมเครือเล็กน้อยและกระสุนที่แตกต่างกันมีการตีความที่แตกต่างกัน

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

คุณสามารถตรวจสอบพฤติกรรมนั้นสำหรับกรณีนี้ได้โดยเพิ่มบรรทัดเหล่านี้ลงในสคริปต์:

echo $BASH_VERSION
echo $ZSH_VERSION

คุณจะสังเกตได้ว่าจาก Bash บรรทัดแรกจะแสดงผลเวอร์ชันของคุณในขณะที่บรรทัดที่สองไม่พูดอะไรเลยไม่ว่าคุณจะใช้เชลล์แบบใดก็ตาม

  • หาก/bin/shเป็นของคุณให้บอกว่าdashจะไม่มีบรรทัดใดที่จะส่งออกอะไรเมื่อสคริปต์ถูกเรียกใช้จาก zsh หรือเส้นประ
  • หาก/bin/shลิงก์ของคุณไปยัง Bash คุณจะเห็นผลลัพธ์บรรทัดแรกในทุกกรณี
  • หาก/bin/shเป็นรุ่นที่แตกต่างจาก Bash ที่คุณใช้โดยตรงคุณจะเห็นผลลัพธ์ที่แตกต่างกันเมื่อคุณเรียกใช้สคริปต์จาก bash โดยตรงและจาก zsh

ps -p $$คำสั่งจากคำตอบ rools ของยังจะแสดงข้อมูลที่เป็นประโยชน์เกี่ยวกับคำสั่งเปลือกที่ใช้ในการรันสคริปต์


ฉันลงคะแนนอย่างไรก็ตามมันใช้ DEFAULT SHELL หากไม่ระบุ Shebang ไม่ว่าคุณจะระบุเชลล์เริ่มต้นใด นี่คือเหตุผลว่าทำไมคุณควรระบุ shebang เสมอ
thecarpy

4
มันไม่ได้ซึ่งเป็นจุดรวมของคำตอบจริงๆ ประโยคที่สองของคุณเป็นจริงอย่างแน่นอน
Michael Homer

คุณพูดถูกทุบใช้มันเองเชลล์เริ่มต้นในกล่องส่วนใหญ่ของฉันเกิดขึ้นเป็น .... ทุบตีจากความสับสนของฉัน ฉันเพิ่งทดสอบบน Solaris 8 ที่มี ksh เป็นเชลล์เริ่มต้นนั่นเองทุบตีรันเป็นทุบตี ... เรียนรู้สิ่งใหม่วันนี้ขอบคุณ (upvoted!) ;-)
thecarpy

ขอบคุณ ความล้มเหลวของการexecl()โทรเกิดขึ้นเมื่อเชลล์สคริปต์ไม่มี shebang และถูกเรียกใช้งานเป็นscriptname? มันจะไม่เกิดขึ้นเมื่อเชลล์สคริปต์ถูกเรียกใช้งานเป็นbash scriptname? มันไม่ได้เกิดขึ้นเมื่อเชลล์สคริปต์มี shebang และถูกเรียกใช้งานเป็นscriptname?
StackExchange สำหรับทั้งหมด


7

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

สิ่งที่เกิดขึ้นนั้นขึ้นอยู่กับแอปพลิเคชันและ / หรือฟังก์ชั่นห้องสมุดที่ใช้ในการดำเนินการคำสั่ง

ที่สามารถเป็นเช่นเปลือกฟังก์ชันexeclp()/ execvp()libc

แอปพลิเคชันอื่น ๆ ส่วนใหญ่จะใช้อย่างใดอย่างหนึ่งของพวกเขาเมื่อพวกเขาเรียกใช้คำสั่ง พวกเขาจะเรียกเปลือกเช่นโดยวิธีการของsystem("command line")ฟังก์ชัน libc ซึ่งโดยทั่วไปจะเรียกshให้แยกวิเคราะห์บรรทัดคำสั่งนั้น (เส้นทางที่สามารถกำหนดได้ในเวลาคอมไพล์ (เช่น/bin/shvs /usr/xpg4/bin/shบน Solaris)) หรือเรียกใช้เชลล์ที่จัดเก็บ$SHELLด้วยตนเองเช่นviด้วย !คำสั่งหรือxterm -e 'command line'คำสั่งอื่น ๆ อีกมากมาย ( su user -cจะเรียกเชลล์ล็อกอินของผู้ใช้แทน$SHELL)

โดยทั่วไปแล้วไฟล์ข้อความแบบ shebang-less ที่ไม่ได้ขึ้นต้น#จะถือเป็นshสคริปต์ ซึ่งshมันจะแตกต่างกันไป

execlp()/ execvp()เมื่อexecve()กลับมาENOEXECโดยทั่วไปจะเรียกใช้shบน สำหรับระบบที่มีมากกว่าหนึ่งshเพราะพวกเขาสามารถสอดคล้องกับมาตรฐานมากกว่าหนึ่งมาตรฐานซึ่งshโดยทั่วไปจะถูกกำหนดในเวลารวบรวม (ของแอปพลิเคชันที่ใช้execvp()/ execlp()โดยการเชื่อมโยงหยดรหัสอื่นที่อ้างถึงเส้นทางที่แตกต่างกันไปsh) ตัวอย่างเช่นบน Solaris ซึ่งจะเป็น/usr/xpg4/bin/sh(มาตรฐาน, POSIX sh) หรือ/bin/sh(เชลล์เป้าหมาย (เชลล์โบราณ) บน Solaris 10 และเก่ากว่า, ksh93 ใน Solaris 11)

เมื่อพูดถึงกระสุนมันมีการเปลี่ยนแปลงมากมาย bash, AT&T kshโดยปกติแล้วเชลล์เป้าหมายจะตีความสคริปต์เอง (ในกระบวนการย่อยเว้นแต่execจะใช้) หลังจากทำการจำลองexecve() , ซึ่งเป็นการตั้งค่าตัวแปรที่ไม่ได้ส่งออกทั้งหมด, ปิด fds โคลสออป -s-exec ทั้งหมด, ลบกับดักที่กำหนดเองทั้งหมด, ชื่อแทนฟังก์ชั่น ... ( bashจะแปลสคริปต์ในshโหมด) yashจะดำเนินการตัวเอง (ด้วยshเช่นargv[0]ในshโหมด) เพื่อตีความมัน

zsh, pdksh, ashเปลือกหอยชั่นโดยทั่วไปจะเรียกsh(เส้นทางที่กำหนดในเวลาเรียบเรียง)

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

fish(อย่างน้อยรุ่น 2.4.0) เพียงแค่ส่งคืนข้อผิดพลาดหากexecve()ล้มเหลว (จะไม่พยายามถือเป็นสคริปต์)

เชลล์บางตัว (เช่นbashหรือ AT&T ksh) จะพยายามตรวจสอบด้วยตนเองก่อนว่าไฟล์นั้นน่าจะเป็นสคริปต์หรือไม่ ดังนั้นคุณอาจพบว่าบางเชลล์จะปฏิเสธที่จะรันสคริปต์ถ้ามันมีตัวอักษร NUL ในสองสามไบต์แรก

นอกจากนี้โปรดทราบว่าหากexecve()ล้มเหลวด้วย ENOEXEC แต่ไฟล์มีเส้น Shebang เชลล์บางตัวจะพยายามตีความว่า Shebang เรียงตัวเอง

ตัวอย่างเล็ก ๆ น้อย ๆ :

  • เมื่อ$SHELLเป็น/bin/bash, xterm -e 'myscript with args'จะมีการmyscriptตีความโดยbashในshโหมด ในขณะที่xterm -e myscript with args, xtermจะใช้execvp()เพื่อให้สคริปต์จะถูกตีความโดยshเพื่อให้สคริปต์จะถูกตีความโดย
  • su -c myscriptบน Solaris 10 ที่ซึ่งrootเชลล์ล็อกอินคือ/bin/shและ/bin/shเชลล์บอร์นจะมีmyscriptตีความโดยบอร์นเชลล์
  • /usr/xpg4/bin/awk 'BEGIN{system("myscript")'บน Solaris 10 จะมีการตีความโดย/usr/xpg4/bin/sh(เหมือนกันสำหรับ/usr/xpg4/bin/env myscript )
  • find . -prune -exec myscript {} \;บน Solaris 10 (การใช้execvp()) จะมีการตีความโดย/bin/shแม้กับ/usr/xpg4/bin/findแม้ในสภาพแวดล้อม POSIX (ข้อผิดพลาดความสอดคล้อง)
  • csh -c myscriptจะมีการตีความโดยcshถ้าเริ่มต้น#ด้วยshมิฉะนั้น

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

ไม่ว่าในกรณีใด ๆread -pคือbash- ไวยากรณ์เท่านั้นดังนั้นคุณต้องแน่ใจว่าสคริปต์ถูกตีความโดยbash(และหลีกเลี่ยง.shส่วนขยายที่ทำให้เข้าใจผิด) ไม่ว่าคุณจะรู้เส้นทางของการbashปฏิบัติการและการใช้งาน:

#! /path/to/bash -
read -p ...

หรือคุณสามารถลองและพึ่งพาการ$PATHค้นหาของbashexecutable (สมมติว่าbashมีการติดตั้ง) โดยใช้:

#! /usr/bin/env bash
read -p ...

( envพบเกือบทุกหนทุกแห่งใน/usr/bin) หรือคุณสามารถทำให้ POSIX + Bourne สามารถใช้งานร่วมกันได้ในกรณีที่คุณสามารถ/bin/shใช้ได้ /bin/shระบบทั้งหมดจะมี ส่วนใหญ่มันจะเป็น (ส่วนใหญ่) เข้ากันได้กับ POSIX แต่คุณยังอาจพบตอนนี้และจากนั้นบอร์นเชลล์ที่นั่นแทน

#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"

1

เมื่อคุณไม่มีบรรทัดใด ๆ#!(เรียกว่าshebang ) shจะถูกใช้ เพื่อตรวจสอบว่าคุณสามารถเรียกใช้สคริปต์ต่อไปนี้

ps -p $$
echo -n "The real shell is: "
realpath /proc/$$/exe

ในคอมพิวเตอร์ของฉันฉันได้รับ

  PID TTY          TIME CMD
13718 pts/16   00:00:00 sh
The real program is: /usr/bin/bash

แม้ว่าเปลือกเริ่มต้นของฉันคือzsh มันใช้ทุบตีตั้งแต่ในเครื่องของฉันที่ดวลจุดโทษคำสั่งดำเนินการโดยการทุบตี


1
ทำไมคุณถึงลงคะแนนนั่น กรุณาอธิบายหรือมันไร้ประโยชน์ ...
rools

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