ทำไม #! / usr / bin / env ทุบตีดีกว่า #! / bin / bash?


212

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

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


7
มันไม่จำเป็นต้องดีกว่า ดูคำถามนี้และคำตอบของฉันใน unix.stackexchange.com (ฉันจะลงคะแนนเพื่อปิดสิ่งนี้เป็นสิ่งที่ซ้ำกัน แต่ฉันไม่คิดว่าคุณจะทำแบบนั้นได้ในทุกไซต์)
Keith Thompson

2
นอกเหนือไปจากคำตอบของ @ Zigg, อาจจะไม่ได้อยู่ที่env /usr/binความคิดเห็นของ Shebang นั้นเป็นความคิดที่ไม่ดีเลย หากล่ามสคริปต์เริ่มต้นของคุณไม่จัดการกับความคิดเห็น shebang นั่นเป็นเพียงความคิดเห็น อย่างไรก็ตามถ้าคุณรู้ว่าล่ามสคริปต์สามารถจัดการกับความคิดเห็น Shebang และคุณรู้เส้นทางที่จะทุบตีไม่มีเหตุผลที่จะไม่เรียกมันโดยใช้เส้นทางที่แน่นอนของมันเว้นแต่เส้นทางยาวเกินไป (ไม่น่าจะ) หรือคุณอาจจะย้ายสคริปต์ ไปยังระบบที่ไม่มี bash อยู่ใน / bin จากนั้นอีกครั้ง caveats ที่ฉันกล่าวถึงก่อนหน้านี้ใช้ในกรณีนั้นเนื่องจากเกี่ยวข้องกับการพกพา

1
@ KeithThompson ขอบคุณสำหรับลิงค์ บางทีการค้นหาคำตอบก่อนโพสต์คำถามอาจจะแคบไปหน่อย สิ่งที่ฉันได้จากทั้งหมดนี้: (1) linux / unix / posix / etc ... เป็นสีเทาและ (2) ใครก็ตามที่อ้างว่ามีคำตอบที่ถูกต้องมีคำตอบที่ถูกต้องสำหรับสถานการณ์เฉพาะของพวกเขา
spugm1r3

3
พฤติกรรมของหลายสิ่งใน POSIX / Unix นั้นถูกกำหนดไว้อย่างดี สถานที่ไม่ได้ชัดเจนมากนัก somethings มีอยู่เหมือนหรือ/etc เป็นส่วนเสริมสำหรับ Unix ส่วนใหญ่เช่นระบบ มันเป็นเพียงลินุกซ์ที่รับประกันได้ว่าจะในและส่วนใหญ่ยังเชื่อมโยงเป็น เนื่องจากลีนุกซ์ได้กลายเป็นระบบปฏิบัติการยูนิกซ์สมัยใหม่สำหรับคนจำนวนมากความจริงที่ว่าระบบอื่นที่นอกเหนือจากลีนุกซ์อาจถูกลืมไปแล้ว. ในคำตอบของตัวเองดังต่อไปนี้ผมถือว่าลินุกซ์เพราะคุณกล่าวว่า กล่อง BSD จำนวนมากที่ฉันทำงานด้วยไม่ได้ติดตั้งเลย /bin/shbashbash/bin/bin/shbash
Sean Perry

2
@Keith - ในกรณีที่ทุบตี (เมื่อเทียบกับงูใหญ่ในคำถามอื่น ๆ ) ... การ OpenBSD /bin/bashไม่ได้ โดยค่าเริ่มต้น Bash จะไม่ถูกติดตั้ง pkg install bashหากคุณต้องการมันคุณจะต้อง /usr/local/bin/bashเมื่อติดตั้งแล้วมันจะอยู่ที่ ไม่มีสิ่งใดติดตั้ง/bin/bashใน OpenBSD Shebang of #!/bin/bashwill เกิดข้อผิดพลาดและ#!/usr/bin/env bashจะประสบความสำเร็จ
jww

คำตอบ:


229

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

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


29
แล้วenvสถานที่ล่ะ POSIX ไม่ได้บังคับ
Julio Guerra

1
@JulioGuerra เหมือนกับการมีไบนารี/usr/lib/sendmail(หรือเมื่อเร็ว ๆ นี้/usr/sbin/sendmail) พร้อมที่จะประมวลผลจดหมายมันเป็นความสนใจที่ดีที่สุดของระบบ Unix ที่เหมือนกัน/usr/bin/envเพราะ env shebang นั้นเป็นเรื่องธรรมดา มันเป็นอินเทอร์เฟซมาตรฐานโดยแท้จริง
zigg

8
@zigg นั่นเพื่อให้สหประชาชาติ * X ... <-: ฉันหมายความว่ามันอยู่ในความสนใจที่ดีที่สุดของพวกเขาที่จะมีการตั้งมาตรฐานenvแต่อย่างใดไม่ได้ที่จะมีการตั้งมาตรฐาน (ซึ่งก็สามารถจะเชื่อมโยงนิ่ม) bashสำหรับ ไม่ต้องพูดถึงแล้วทำไม hashbang ไม่ยอมรับเพียง#!bashและใช้แทนการที่เราทำสิ่งเดียวกันกับPATH envฉันเดาว่าไม่น่าสับสนพอสำหรับมือใหม่
ddekany

1
@JulioGuerra ตามวิกิพีเดียคุณเป็นจริง: ไม่ใช่ "รับประกัน" ดูเหมือนว่า "น่าจะเป็นไปได้มากกว่า" แทน Wikipedia บอกว่าThis mostly works because the path /usr/bin/env is commonly used for the env utilityที่นี่en.wikipedia.org/wiki/Shebang_(Unix) - เราต้องเชื่อมั่นในenvการมี "อาจ" ในทุกระบบ
Xavi Montero

2
@ XaviMontero: ฉันเคยใช้ระบบที่envอยู่ใน/binไม่ได้อยู่ใน/usr/bin(ไม่แน่ใจว่าจะเป็นหนึ่งใน SunOS 4) ทุกวันนี้มีโอกาสมากที่/usr/bin/envจะมีเพียงเพราะความนิยมของ#!/usr/bin/envแฮ็ค
Keith Thompson

39

ตำแหน่งมาตรฐานของ bash คือ/binและฉันสงสัยว่าเป็นจริงในทุกระบบ อย่างไรก็ตามถ้าคุณไม่ชอบ bash รุ่นนั้นล่ะ ตัวอย่างเช่นฉันต้องการใช้ bash 4.2 แต่ bash บน Mac ของฉันอยู่ที่ 3.2.5

ฉันลองติดตั้งทุบตีใหม่ได้/binแต่นั่นอาจเป็นความคิดที่แย่ หากฉันอัปเดตระบบปฏิบัติการของฉันจะมีการเขียนทับ

อย่างไรก็ตามฉันสามารถติดตั้ง bash ใน/usr/local/bin/bashและตั้งค่า PATH ของฉันเป็น:

PATH="/usr/local/bin:/bin:/usr/bin:$HOME/bin"

ตอนนี้ถ้าฉันระบุbashฉันไม่ได้รับหนึ่ง cruddy เก่าแต่ใหม่กว่าหนึ่งที่เงางาม/bin/bash /usr/local/binดี!

ยกเว้นสคริปต์เปลือกของฉันมี!# /bin/bashshebang ดังนั้นเมื่อฉันเรียกใช้เชลล์สคริปต์ของฉันฉันได้รับทุบตีรุ่นเก่าและมีหมัดที่ไม่ได้มีอาร์เรย์เชื่อมโยง

การใช้/usr/bin/env bashจะใช้เวอร์ชันของ bash ที่พบใน PATH ของฉัน หากฉันตั้งค่า PATH ของฉันเพื่อที่/usr/local/bin/bashจะดำเนินการนั่นคือทุบตีที่สคริปต์ของฉันจะใช้

เป็นเรื่องยากที่จะเห็นสิ่งนี้ด้วย bash แต่มันเป็นเรื่องธรรมดามากกับ Perl และ Python:

  • บางรุ่น Unix / Linux ซึ่งมุ่งเน้นไปที่ความมั่นคงในบางครั้งวิธีที่มีการเปิดตัวของสองภาษาสคริปต์ ไม่นานมานี้ Perl ของ RHEL อยู่ที่ 5.8.8 - Perl รุ่นเก่าแปดปี! หากมีคนต้องการใช้คุณสมบัติที่ทันสมัยกว่านี้คุณต้องติดตั้งเวอร์ชันของคุณเอง
  • โปรแกรมเช่น Perlbrew และ Pythonbrew ช่วยให้คุณสามารถติดตั้งภาษาเหล่านี้ได้หลายเวอร์ชัน พวกเขาขึ้นอยู่กับสคริปต์ที่จัดการ PATH ของคุณเพื่อรับเวอร์ชันที่คุณต้องการ การเขียนโค้ดที่ยากหมายความว่าฉันไม่สามารถเรียกใช้สคริปต์ภายใต้การชงได้
  • ไม่นานมานี้ (โอเคนานมาแล้ว) ว่า Perl และ Python ไม่ใช่แพ็คเกจมาตรฐานที่รวมอยู่ในระบบ Unix ส่วนใหญ่ นั่นหมายความว่าคุณไม่รู้ว่าติดตั้งโปรแกรมทั้งสองนี้ไว้ที่ไหน มันอยู่ภายใต้/bin? /usr/bin? /opt/bin? ใครจะรู้? ใช้#! /usr/bin/env perlหมายความว่าฉันไม่จำเป็นต้องรู้

และตอนนี้ทำไมคุณไม่ควรใช้ #! /usr/bin/env bash

เมื่อเส้นทางถูก hardcoded ใน shebang ฉันต้องทำงานกับล่ามนั้น ดังนั้น#! /bin/bashบังคับให้ฉันใช้ bash รุ่นเริ่มต้นที่ติดตั้งไว้ เนื่องจากคุณสมบัติทุบตีมีความเสถียรมาก (ลองใช้สคริปต์ Python เวอร์ชัน 2.x ภายใต้ Python 3.x) จึงไม่น่าเป็นไปได้มากที่สคริปต์ BASH ของฉันจะไม่ทำงานและเนื่องจาก bash script ของฉันอาจถูกใช้โดยระบบนี้และระบบอื่น ๆ การใช้ bash รุ่นที่ไม่ได้มาตรฐานอาจมีผลกระทบที่ไม่พึงประสงค์ มีโอกาสมากที่ฉันต้องการตรวจสอบให้แน่ใจว่ามีการใช้ทุบตีรุ่นมาตรฐานที่เสถียรกับเชลล์สคริปต์ของฉัน ดังนั้นฉันอาจต้องการรหัสยากเส้นทางใน shebang ของฉัน


5
สิ่งนี้ไม่เป็นความจริง: "ตำแหน่งมาตรฐานของ bash คือ / bin" (เว้นแต่คุณจะสามารถอ้างอิงเอกสารมาตรฐานได้) อาจแม่นยำกว่าว่าเป็นตำแหน่ง "ปกติ" ใน Linux และ Macos ส่วนใหญ่ แต่ไม่ใช่มาตรฐานสำหรับระบบยูนิกซ์ทั่วไป (และไม่ใช่ตำแหน่งที่อยู่ใน * bsds ส่วนใหญ่)
tesch1

13

สำหรับการเรียกใช้bashมันเป็น overkill เล็กน้อย ถ้าคุณไม่มีbashไบนารีหลายตัวเหมือนของคุณเองใน ~ / bin แต่นั่นก็หมายความว่ารหัสของคุณขึ้นอยู่กับ $ PATH ที่มีสิ่งที่ถูกต้องอยู่

มันมีประโยชน์สำหรับสิ่งต่าง ๆ เช่นpythonว่า มีสคริปต์ตัวตัดและสภาพแวดล้อมที่นำไปสู่pythonไบนารีทางเลือกที่ใช้อยู่

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


pythonจุดที่สำหรับ ฉันสูญเสียการนับจำนวนสถานที่แล้วที่pythonสามารถพบได้😀
zigg

2
ตกลงว่า/usr/bin/envมีประโยชน์มากสำหรับ Python โดยเฉพาะถ้าคุณใช้ virtualenv
เดนนิส

10

มีหลายระบบที่ไม่มี Bash ใน/bin, FreeBSD และ OpenBSD เพื่อตั้งชื่อไม่กี่ หากสคริปต์ของคุณจะหมายถึงการจะพกพาไป Unices ที่แตกต่างกันคุณอาจต้องการที่จะใช้แทน#!/usr/bin/env bash#!/bin/bash

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


ใน Ubuntu 18.04 /bindir sh -> dashฉันเห็น เชื่อมโยงด้วยสัญลักษณ์เพื่อdashเผยให้เห็นลักษณะ Debian ของ Ubuntu เรียกทั้งสามสายคำสั่งที่จะตระหนักถึงมันทั้งหมดเดือดลงไปความชอบของแต่ละบุคคล: which bashแล้วนั้นwhich sh which dash
noobninja

0

ฉันต้องการห่อโปรแกรมหลักในสคริปต์ด้านล่างเพื่อตรวจสอบทั้งหมดที่bashมีอยู่ในระบบ ดีกว่าที่จะมีการควบคุมเพิ่มเติมเกี่ยวกับรุ่นที่ใช้

#! /usr/bin/env bash

# This script just chooses the appropriate bash
# installed in system and executes testcode.main

readonly DESIRED_VERSION="5"

declare all_bash_installed_on_this_system
declare bash

if [ "${BASH_VERSINFO}" -ne "${DESIRED_VERSION}" ]
then
    found=0

    all_bash_installed_on_this_system="$(\
        awk -F'/' '$NF == "bash"{print}' "/etc/shells"\
        )"

    for bash in $all_bash_installed_on_this_system
    do
        versinfo="$( $bash -c 'echo ${BASH_VERSINFO}' )"
        [ "${versinfo}" -eq "${DESIRED_VERSION}" ] && { found=1 ; break;}
    done
    if [ "${found}" -ne 1 ]
    then
        echo "${DESIRED_VERSION} not available"
        exit 1
    fi
fi

$bash main_program "$@"

0
 #!/usr/bin/env bash

ดีกว่าอย่างแน่นอนเพราะพบเส้นทางการทำงานของ bash จากตัวแปรสภาพแวดล้อมระบบของคุณ

ไปที่เชลล์ Linux ของคุณและพิมพ์

env

มันจะพิมพ์ตัวแปรสภาพแวดล้อมทั้งหมดของคุณ

ไปที่เชลล์สคริปต์และประเภทของคุณ

echo $BASH

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

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