อะไรคือความแตกต่างระหว่าง“ #! / usr / bin / env bash” และ“ #! / usr / bin / bash”


372

ในส่วนหัวของสคริปต์ Bash ความแตกต่างระหว่างสองข้อความเหล่านี้คืออะไร:

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

เมื่อฉันปรึกษาenv หน้า manฉันได้คำจำกัดความนี้:

 env - run a program in a modified environment

มันหมายความว่าอะไร?


7
ดูคำถามนี้และคำตอบของฉัน
Keith Thompson

6
ใครสามารถบอกฉันได้ว่าทำไมคำถามนี้จึงถูกปิด "ไม่เกี่ยวข้องกับการเขียนโปรแกรมหรือการพัฒนาซอฟต์แวร์"
tarrsalah

1
ผมยอมรับว่ามันไม่ได้ปิดหัวข้อ แต่มันอาจจะซ้ำกับคำถามอื่น ๆ อีกหลายอย่างเช่นนี้
Keith Thompson

16
คำถามนี้ไม่ควรถูกทำเครื่องหมายว่าไม่อยู่ในหัวข้อ ต้องการเพียง 5 คนที่มีคะแนนมากกว่า 3,000 คะแนนเพื่อทำเครื่องหมายว่าเป็น "ในหัวข้อ" และสามารถเปิดใหม่ได้ มันเป็นคำถาม - โดยเฉพาะเกี่ยวกับการเขียนโปรแกรม
Danijel-James W

3
ฉันตกใจ ตกใจที่พบว่าเอกสารของ Linux นั้นเต็มไปด้วยความซ้ำซาก xkcd.com/703 git-man-page-generator.lokaltog.net
allyourcode

คำตอบ:


323

การเรียกใช้คำสั่งผ่าน/usr/bin/envมีประโยชน์ในการค้นหาสิ่งที่รุ่นเริ่มต้นของโปรแกรมอยู่ในการรีดenvปัจจุบันของคุณ

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

ข้อเสียอย่างหนึ่งคือคุณจะไม่สามารถผ่านการโต้แย้งมากกว่าหนึ่งข้อ (เช่นคุณจะไม่สามารถเขียนได้/usr/bin/env awk -f) หากคุณต้องการสนับสนุน Linux เนื่องจากPOSIX นั้นคลุมเครือเกี่ยวกับการตีความบรรทัดและ Linux ตีความทุกอย่างหลังจากแรก ช่องว่างเพื่อแสดงถึงอาร์กิวเมนต์เดียว คุณสามารถใช้/usr/bin/env -Sในบางรุ่นenvเพื่อแก้ไขปัญหานี้ แต่สคริปต์จะกลายเป็นแบบพกพาที่น้อยลงและทำลายระบบที่ค่อนข้างเป็นปัจจุบัน (เช่นแม้กระทั่ง Ubuntu 16.04 หากไม่ใช่ในภายหลัง)

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

#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash     #gives you explicit control on a given system of what executable is called

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


22
ข้อเสียเปรียบอีกประการหนึ่งคือคุณไม่สามารถส่งผ่านข้อโต้แย้งเพิ่มเติมไปยังล่ามได้
Keith Thompson

1
@KeithThompson: ข้อมูลไม่ถูกต้อง .. คุณสามารถส่งตัวเลือกไปยังล่ามพื้นฐานโดยใช้ / usr / bin / env!
Gaurav Agarwal

4
@GauravAgarwal: ไม่ได้อยู่ในระบบของฉัน สคริปต์ที่มีเพียงแค่นี้บรรทัดเดียว: บ่น:#!/usr/bin/env echo Hello /usr/bin/env: echo Hello: No such file or directoryเห็นได้ชัดว่ามันถือว่าเป็นอาร์กิวเมนต์เดียวเพื่อecho Hello /usr/bin/env
Keith Thompson

1
@ AndréLaszlo: envคำสั่งอนุญาตให้ส่งผ่านอาร์กิวเมนต์ไปยังคำสั่งได้อย่างแน่นอน ปัญหาคือความหมายของ#!บรรทัดและขึ้นอยู่กับเคอร์เนล เคอร์เนล Linux ล่าสุดอนุญาตให้ใช้สิ่งต่าง ๆ เช่น#!/usr/bin/env command argsกัน แต่เมล็ด Linux รุ่นเก่าและระบบอื่น ๆ ไม่สามารถทำได้
Keith Thompson

1
ทำไมถึงมี backticks ในบล็อคโค้ด? พวกเขาไม่ควรถูกลบ?
Benjamin W.

64

การใช้#!/usr/bin/env NAMEทำให้เชลล์ค้นหาคู่แรกของ NAME ในตัวแปรสภาพแวดล้อม $ PATH มันจะมีประโยชน์หากคุณไม่ทราบเส้นทางที่แน่นอนหรือไม่ต้องการค้นหา


9
อย่างน้อยคุณต้องรู้ว่า env คืออะไร :)
ᐅ devrimbaris

1
คำตอบที่ยอดเยี่ยม อธิบายอย่างชัดเจนว่า env shebang ทำอะไรมากกว่าพูดว่า "เลือกโปรแกรมตามการกำหนดค่าระบบของคุณ"
De Novo

13

แทนที่จะกำหนดเส้นทางไปยังล่ามอย่างชัดเจน/usr/bin/bash/โดยใช้คำสั่ง env ล่ามจะค้นหาและเรียกใช้จากที่ใดก็ตามที่พบครั้งแรก สิ่งนี้มีทั้งคว่ำและลง


ในระบบส่วนใหญ่มันจะทำงานเหมือนกัน แต่ขึ้นอยู่กับตำแหน่งของไฟล์ bash และ env ไม่แน่ใจว่าสิ่งนี้จะส่งผลกระทบต่อตัวแปรสภาพแวดล้อมอย่างไร
13:

2
"เป็นไปได้ที่จะระบุล่ามโดยไม่ใช้ env โดยให้เส้นทางแบบเต็มไปยังล่ามปัญหาคือว่าในระบบคอมพิวเตอร์ที่แตกต่างกันเส้นทางที่แน่นอนอาจแตกต่างกันแทนที่จะใช้ env ล่ามจะค้นหาและอยู่ที่ เวลาที่สคริปต์ทำงานซึ่งทำให้สคริปต์พกพาได้มากขึ้น แต่ยังเพิ่มความเสี่ยงที่ล่ามที่ไม่ถูกต้องจะถูกเลือกเพราะมันจะค้นหาคู่ที่ตรงกันในทุกไดเรกทอรีบนพา ธ การค้นหาที่เรียกทำงานได้นอกจากนี้ยังมีปัญหาเดียวกันกับ เส้นทางสู่ไบนารี env อาจแตกต่างกันไปในแต่ละเครื่อง "- Wikipedia
Mike Clark

10

ถ้าเชลล์สคริปต์เริ่มต้นด้วยการ#!/bin/bashที่พวกเขามักจะทำงานด้วยจากbash /binหากพวกเขาเริ่มต้นด้วย#!/usr/bin/env bashพวกเขาจะค้นหาbashใน$PATHแล้วเริ่มต้นด้วยคนแรกที่พวกเขาสามารถหา

ทำไมสิ่งนี้ถึงมีประโยชน์ สมมติว่าคุณต้องการรันbashสคริปต์ที่ต้องการ bash 4.x หรือใหม่กว่า แต่ระบบของคุณมีเพียงbashติดตั้ง 3.xและในปัจจุบันการแจกจ่ายของคุณไม่ได้ให้รุ่นที่ใหม่กว่าหรือคุณไม่ใช่ผู้ดูแลระบบและไม่สามารถเปลี่ยนสิ่งที่ติดตั้งบนระบบนั้น .

แน่นอนคุณสามารถดาวน์โหลดซอร์สโค้ด bash และสร้าง bash ของคุณเองได้จากศูนย์โดยวาง~/binตัวอย่างไว้ และคุณยังสามารถแก้ไข$PATHตัวแปรของคุณใน.bash_profileไฟล์เพื่อรวม~/binเป็นรายการแรก ( PATH=$HOME/bin:$PATHเช่นที่~จะไม่ขยาย$PATH) ถ้าคุณเรียกตอนนี้bashเชลล์จะค้นหามัน$PATHตามลำดับก่อนดังนั้นจึงเริ่มต้นด้วย~/binมันจะหาตำแหน่งของคุณbashที่ไหน สิ่งเดียวกันที่เกิดขึ้นถ้าค้นหาสคริปต์สำหรับbashการใช้#!/usr/bin/env bashเพื่อให้สคริปต์เหล่านี้จะได้รับการทำงานในระบบของคุณโดยใช้กำหนดเองของคุณbashสร้าง

ข้อเสียอย่างหนึ่งคือสิ่งนี้สามารถนำไปสู่พฤติกรรมที่ไม่คาดคิดเช่นสคริปต์เดียวกันบนเครื่องเดียวกันอาจทำงานกับล่ามที่แตกต่างกันสำหรับสภาพแวดล้อมที่แตกต่างกันหรือผู้ใช้ที่มีเส้นทางการค้นหาที่แตกต่างกันทำให้ปวดหัวทุกชนิด

ข้อเสียที่ใหญ่ที่สุดกับenvคือว่าบางระบบจะช่วยให้หนึ่งอาร์กิวเมนต์ดังนั้นคุณจึงไม่สามารถทำเช่นนี้#!/usr/bin/env <interpreter> <arg>เป็นระบบที่จะเห็น<interpreter> <arg>ว่าเป็นหนึ่งในข้อโต้แย้ง (พวกเขาจะปฏิบัติต่อมันราวกับว่าการแสดงออกถูกยกมา) จึงจะค้นหาล่ามที่มีชื่อว่าenv <interpreter> <arg>โปรดทราบว่านี้ไม่ได้เป็นปัญหาของการที่envคำสั่งตัวเองซึ่งได้รับอนุญาตเสมอพารามิเตอร์หลายที่จะผ่าน แต่มีการแยกวิเคราะห์ shebang envของระบบที่แยกบรรทัดนี้ก่อนที่จะโทรแม้กระทั่ง ในขณะนี้สิ่งนี้ได้รับการแก้ไขในระบบส่วนใหญ่ แต่ถ้าสคริปต์ของคุณต้องการพกพาสะดวกคุณไม่สามารถวางใจได้ว่าสิ่งนี้ได้รับการแก้ไขแล้วในระบบที่คุณจะทำงาน

มันสามารถมีผลกระทบด้านความปลอดภัยเช่นหากsudoไม่ได้กำหนดค่าสภาพแวดล้อมที่สะอาดหรือ$PATHถูกแยกออกจากการล้างข้อมูล ให้ฉันสาธิตสิ่งนี้:

โดยปกติแล้ว/binจะเป็นสถานที่ที่ได้รับการป้องกันเป็นอย่างดีrootสามารถเปลี่ยนแปลงอะไรก็ได้ โฮมไดเร็กตอรี่ของคุณไม่ใช่, โปรแกรมใด ๆ ที่คุณรันสามารถทำการเปลี่ยนแปลงได้. นั่นหมายความว่ารหัสที่เป็นอันตรายอาจวางของปลอมbashลงในไดเรกทอรีที่ซ่อนอยู่บางส่วนปรับเปลี่ยนของคุณ.bash_profileเพื่อรวมไดเรกทอรีนั้นไว้ในของคุณ$PATHดังนั้นสคริปต์ทั้งหมดที่ใช้#!/usr/bin/env bashจะจบลงด้วยการทำงานของปลอมbashนั้น ถ้าsudoเก็บ$PATHไว้คุณกำลังมีปัญหาใหญ่

เช่นพิจารณาเครื่องมือสร้างไฟล์ที่~/.evil/bashมีเนื้อหาดังต่อไปนี้:

#!/bin/bash

if [ $EUID -eq 0 ]; then
  echo "All your base are belong to us..."
  # We are root - do whatever you want to do
fi

/bin/bash "$@"

มาสร้างสคริปต์ง่ายๆsample.shกันเถอะ:

#!/usr/bin/env bash

echo "Hello World"

หลักฐานของแนวคิด (ในระบบที่sudoเก็บ$PATH):

$ ./sample.sh
Hello World

$ sudo ./sample.sh
Hello World

$ export PATH="$HOME/.evil:$PATH"

$ ./sample.sh
Hello World

$ sudo ./sample.sh
All your base are belong to us...
Hello World

โดยปกติแล้วกระสุนคลาสสิคควรจะอยู่ในนั้น/binและถ้าคุณไม่ต้องการวางไว้ที่นั่นไม่ว่าด้วยเหตุผลใดก็ตามมันไม่ใช่ปัญหาที่จะต้องวาง symlink ใน/binจุดนั้นไปยังตำแหน่งจริง (หรืออาจ/binเป็น symlink) ผมมักจะไปกับและ#!/bin/sh #!/bin/bashมีมากเกินไปที่จะทำลายหากสิ่งเหล่านี้จะไม่ทำงานอีกต่อไป ไม่ใช่ว่า POSIX จะต้องใช้ตำแหน่งเหล่านี้ (POSIX ไม่ได้ทำให้ชื่อพา ธ เป็นมาตรฐานและดังนั้นจึงไม่ได้ทำให้ฟีเจอร์ Shebang เป็นมาตรฐานเลย) แต่มันก็เป็นเรื่องธรรมดามากแม้ว่าระบบจะไม่เสนอให้/bin/shก็ตาม#!/bin/shและรู้ว่าต้องทำอะไรกับมันและมันอาจจะเป็นความเข้ากันได้กับรหัสที่มีอยู่เท่านั้น

แต่สำหรับล่ามที่เป็นทางเลือกที่ทันสมัยและไม่เป็นมาตรฐานเช่น Perl, PHP, Python หรือ Ruby มันไม่ได้ระบุที่ใดก็ตามที่พวกเขาควรจะอยู่ พวกเขาอาจจะมา/usr/binแต่พวกเขาได้เป็นอย่างดีอาจจะอยู่ใน/usr/local/binหรือในสาขาลำดับชั้นที่แตกต่างกันอย่างสิ้นเชิง ( /opt/..., /Applications/...ฯลฯ ) นั่นเป็นเหตุผลที่สิ่งเหล่านี้มักจะใช้#!/usr/bin/env xxxรูปแบบ shebang


4

ฉันพบว่ามีประโยชน์เพราะเมื่อฉันไม่รู้ env ก่อนที่ฉันจะเริ่มเขียนสคริปต์ฉันทำสิ่งนี้:

type nodejs > scriptname.js #or any other environment

แล้วฉันก็แก้ไขบรรทัดนั้นในไฟล์เป็น shebang
ฉันทำสิ่งนี้เพราะฉันจำไม่ได้ว่าอยู่ที่ไหน nodejs บนคอมพิวเตอร์ของฉัน - / usr / bin / หรือ / bin / ดังนั้นสำหรับฉันenvจึงมีประโยชน์มาก อาจมีรายละเอียดเกี่ยวกับสิ่งนี้ แต่นี่คือเหตุผลของฉัน

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