Mengapa #! / Usr / bin / env bash lebih unggul dari #! / Bin / bash?

212

Saya telah melihat di sejumlah tempat, termasuk rekomendasi di situs ini ( Apa yang disukai Bash shebang? ), Untuk digunakan #!/usr/bin/env bashsebagai preferensi #!/bin/bash. Saya bahkan telah melihat satu individu yang giat menyarankan penggunaan #!/bin/bashyang salah dan fungsi bash akan hilang dengan melakukannya.

Semua yang dikatakan, saya menggunakan bash dalam lingkungan pengujian yang dikontrol ketat di mana setiap drive yang beredar pada dasarnya adalah tiruan dari master drive tunggal. Saya memahami argumen portabilitas, meskipun tidak selalu berlaku dalam kasus saya. Apakah ada alasan lain untuk memilih #!/usr/bin/env bashdaripada alternatif dan, dengan asumsi portabilitas menjadi perhatian, apakah ada alasan menggunakannya dapat merusak fungsi?

spugm1r3
sumber
7
Belum tentu lebih baik. Lihat pertanyaan ini dan jawaban saya di unix.stackexchange.com. (Saya akan memilih untuk menutup ini sebagai duplikat, tetapi saya tidak berpikir Anda bisa melakukan itu di seluruh situs.)
Keith Thompson
2
Selain jawaban @ zigg, envmungkin tidak ditemukan di /usr/bin. Komentar Shebang sama sekali ide yang buruk IMHO. Jika penerjemah skrip default Anda tidak menangani komentar shebang, itu hanya komentar. Namun, jika Anda tahu juru bahasa skrip dapat menangani komentar shebang, dan Anda tahu jalan menuju bash, tidak ada alasan untuk tidak memintanya menggunakan jalur absolutnya kecuali jika jalurnya terlalu panjang (tidak mungkin), atau Anda mungkin dapat port script ke sistem yang tidak memiliki bash yang terletak di / bin. Kemudian lagi, peringatan yang saya sebutkan sebelumnya berlaku dalam kasus itu karena melibatkan portabilitas.
1
@KeithThompson, terima kasih atas tautannya. Mungkin pencarian saya untuk jawaban sebelum memposting pertanyaan itu agak sempit. Saya mengambil semua ini: (1) linux / unix / posix / etc ... adalah abu-abu, dan (2) siapa pun yang mengklaim benar-benar memiliki jawaban yang benar benar-benar memiliki jawaban yang tepat untuk skenario khusus mereka.
spugm1r3
3
Perilaku banyak hal dalam POSIX / Unix didefinisikan dengan baik. Lokasi tidak selalu begitu jelas dipotong. Sesuatu harus ada seperti /etcatau /bin/sh. bashadalah add-on untuk sebagian besar sistem seperti Unix. Ini hanya Linux di mana bashdijamin berada /bindan kemungkinan besar juga terhubung sebagai /bin/sh. Sejak Linux menjadi Unix de facto modern bagi banyak orang, fakta bahwa sistem selain Linux mungkin ada telah dilupakan. Dalam jawaban saya sendiri di bawah ini saya menganggap Linux karena Anda berkata bash. Banyak kotak BSD yang telah saya kerjakan bahkan belum diinstal.
Sean Perry
2
@Keith - Dalam kasus Bash (berbeda dengan Python di pertanyaan lain) ... OpenBSD tidak memiliki /bin/bash. Bash tidak diinstal secara default. Jika Anda menginginkannya, Anda harus melakukannya pkg install bash. Setelah diinstal itu terletak di /usr/local/bin/bash. Tidak ada yang diinstal di /bin/bashOpenBSD. Kerinduan #!/bin/bashkehendak kesalahan, dan #!/usr/bin/env bashakan berhasil.
jww

Jawaban:

229

#!/usr/bin/envpencarian PATHuntuk bash, dan bashtidak selalu di /bin, terutama pada sistem non-Linux. Sebagai contoh, pada sistem OpenBSD saya, ia masuk /usr/local/bin, karena diinstal sebagai paket opsional.

Jika Anda benar-benar yakin bashada /bindan akan selalu ada, tidak ada salahnya untuk meletakkannya langsung di jendela Anda — tetapi saya akan merekomendasikannya karena skrip dan program semua memiliki kehidupan di luar apa yang awalnya kami yakini akan mereka miliki.

zigg
sumber
29
bagaimana dengan envlokasi? POSIX tidak memaksanya.
Julio Guerra
1
@JulioGuerra Seperti halnya memiliki /usr/lib/sendmail(atau, baru-baru ini, /usr/sbin/sendmail) biner tersedia untuk memproses surat, itu merupakan kepentingan terbaik sistem Unix untuk dimiliki /usr/bin/envkarena env shebang adalah praktik umum yang demikian. Ini adalah antarmuka standar de facto.
zigg
8
@zigg Begitu UN * X ... <-: Maksud saya, demi kepentingan terbaik mereka memiliki lokasi standar env, tetapi entah bagaimana tidak memiliki lokasi standar (yang hanya bisa menjadi tautan lunak) untuk bash. Belum lagi, mengapa hashbang tidak menerima adil #!bash, dan menggunakan PATH, bukannya kita melakukan hal yang persis sama dengannya env. Kurasa tidak cukup membingungkan untuk pemula.
ddekany
1
@JulioGuerra Menurut wikipedia Anda benar: Ini bukan "dijamin". Sebaliknya, tampaknya "lebih mungkin". Wikipedia mengatakan di This mostly works because the path /usr/bin/env is commonly used for the env utilitysini en.wikipedia.org/wiki/Shebang_(Unix) - Kita harus curhat envberada di sana "mungkin" di semua sistem.
Xavi Montero
2
@XaviMontero: Saya telah menggunakan sistem di mana envberada /bin, tidak dalam /usr/bin(tidak yakin yang mana, kemungkinan SunOS 4). Hari-hari ini sangat mungkin /usr/bin/envakan tersedia, hanya karena popularitas #!/usr/bin/envperetasan.
Keith Thompson
39

Lokasi standar bash adalah /bin, dan saya menduga itu benar pada semua sistem. Namun, bagaimana jika Anda tidak menyukai versi bash itu? Misalnya, saya ingin menggunakan bash 4.2, tetapi bash di Mac saya ada di 3.2.5.

Aku bisa mencoba menginstal ulang pesta di /bintapi itu mungkin ide yang buruk. Jika saya memperbarui OS saya, itu akan ditimpa.

Namun, saya dapat menginstal bash in /usr/local/bin/bash, dan mengatur PATH saya untuk:

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

Sekarang, jika saya tentukan bash, saya tidak mendapatkan yang lama kasar /bin/bash, tapi yang lebih baru, lebih bersinar di /usr/local/bin. Bagus!

Kecuali skrip shell saya punya !# /bin/bashshebang itu. Jadi, ketika saya menjalankan skrip shell saya, saya mendapatkan versi bash yang lama dan buruk yang bahkan tidak memiliki array asosiatif.

Menggunakan /usr/bin/env bashakan menggunakan versi bash yang ditemukan di PATH saya. Jika saya mengatur PATH saya, sehingga /usr/local/bin/bashdieksekusi, itu adalah bash yang akan digunakan skrip saya.

Jarang melihat ini dengan bash, tetapi jauh lebih umum dengan Perl dan Python:

  • Rilis Unix / Linux tertentu yang berfokus pada stabilitas kadang-kadang jauh di belakang dengan rilis kedua bahasa scripting ini. Belum lama ini, Perl RHEL berada di 5.8.8 - versi lama dari Perl! Jika seseorang ingin menggunakan lebih banyak fitur modern, Anda harus menginstal versi Anda sendiri.
  • Program seperti Perlbrew dan Pythonbrew memungkinkan Anda untuk menginstal beberapa versi dari bahasa-bahasa ini. Mereka bergantung pada skrip yang memanipulasi PATH Anda untuk mendapatkan versi yang Anda inginkan. Hard coding the path berarti saya tidak dapat menjalankan skrip saya di bawah minuman .
  • Belum lama (oke, sudah lama) bahwa Perl dan Python bukan paket standar yang disertakan dalam sebagian besar sistem Unix. Itu berarti Anda tidak tahu di mana kedua program ini diinstal. Apakah di bawah /bin? /usr/bin? /opt/bin? Siapa tahu? Menggunakan #! /usr/bin/env perlberarti saya tidak perlu tahu.

Dan Sekarang Mengapa Anda Tidak Harus Menggunakan #! /usr/bin/env bash

Ketika jalurnya di-hardcode di shebang, saya harus menjalankannya dengan penerjemah itu. Jadi, #! /bin/bashmemaksa saya untuk menggunakan versi default bash yang diinstal. Karena fitur bash sangat stabil (coba jalankan versi 2.x skrip Python di bawah Python 3.x), sangat tidak mungkin skrip BASH khusus saya tidak akan berfungsi, dan karena skrip bash saya mungkin digunakan oleh sistem ini dan sistem lain , menggunakan versi bash non-standar mungkin memiliki efek yang tidak diinginkan. Sangat mungkin saya ingin memastikan bahwa versi standar stabil bash digunakan dengan skrip shell saya. Jadi, saya mungkin ingin mengkodekan jalur di shebang saya.

David W.
sumber
5
Ini tidak benar: "Lokasi standar bash adalah / bin," (kecuali Anda dapat mengutip dokumen standar) mungkin lebih akurat bahwa itu adalah lokasi "biasa" di sebagian besar distribusi dan makro Linux, tetapi ini bukan standar pada sistem unix secara umum (dan terutama bukan lokasi pada sebagian besar * bsds).
tesch1
13

Untuk memohon bashitu sedikit berlebihan. Kecuali Anda memiliki banyak bashbinari seperti di ~ / bin Anda tetapi itu juga berarti kode Anda bergantung pada $ PATH yang memiliki hal-hal yang benar di dalamnya.

Ini berguna untuk hal-hal seperti itu python. Ada skrip dan lingkungan pembungkus yang mengarah ke pythonbinari alternatif yang digunakan.

Tapi tidak ada yang hilang dengan menggunakan path yang tepat ke biner selama Anda yakin itu adalah biner yang Anda inginkan.

Sean Perry
sumber
Tepat untuk python. Saya telah kehilangan hitungan jumlah tempat yang pythondapat ditemukan 😀
zigg
2
Setuju itu /usr/bin/envlebih berguna untuk Python, terutama jika Anda menggunakan virtualenv.
Dennis
10

Ada banyak sistem yang tidak memiliki Bash /bin, FreeBSD dan OpenBSD hanya untuk beberapa nama. Jika skrip Anda dimaksudkan untuk dibawa-bawa ke banyak Unices yang berbeda, Anda mungkin ingin menggunakannya #!/usr/bin/env bashsebagai gantinya#!/bin/bash .

Perhatikan bahwa ini tidak berlaku untuk sh; untuk script Bourne-compliant saya secara eksklusif menggunakan #!/bin/sh, karena saya pikir cukup banyak setiap Unix yang ada memiliki shdi /bin.

James Ko
sumber
Di Ubuntu 18,04 /bindir, saya mengerti sh -> dash. Tertaut secara simbolis dengan dash, mengungkapkan sifat Debian Ubuntu. Jalankan ketiga string perintah ini untuk menyadari semuanya bermuara pada preferensi individu: which bashlalu which shlalu which dash.
noobninja
0

Saya lebih suka membungkus program utama dalam skrip seperti di bawah ini untuk memeriksa semua yang bashtersedia di sistem. Lebih baik memiliki lebih banyak kontrol pada versi yang digunakannya.

#! /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 "$@"
Mihir
sumber
0
 #!/usr/bin/env bash

jelas lebih baik karena ia menemukan bash executable path dari variabel lingkungan sistem Anda.

Buka Linux shell Anda dan ketik

env

Ini akan mencetak semua variabel lingkungan Anda.

Buka skrip dan ketik shell Anda

echo $BASH

Ini akan mencetak bash path Anda (sesuai dengan daftar variabel lingkungan) yang harus Anda gunakan untuk membangun jalur shebang yang benar dalam skrip Anda.

kta
sumber