Apa perbedaan antara "#! / Usr / bin / env bash" dan "#! / Usr / bin / bash"?

372

Di header skrip Bash, apa perbedaan antara kedua pernyataan itu:

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

Ketika saya berkonsultasi env halaman manual , saya mendapatkan definisi ini:

 env - run a program in a modified environment

Apa artinya?

tarrsalah
sumber
7
Lihat pertanyaan ini dan jawaban saya .
Keith Thompson
6
Siapa yang dapat memberi tahu saya mengapa pertanyaan ini ditutup, "terkait dengan pemrograman atau pengembangan perangkat lunak"?
tarrsalah
1
Saya setuju bahwa ini bukan di luar topik, tetapi mungkin merupakan duplikat dari beberapa pertanyaan lain seperti ini .
Keith Thompson
16
Pertanyaan ini seharusnya tidak ditandai sebagai di luar topik. Hanya perlu 5 orang dengan skor di atas 3.000 untuk menandainya sebagai "pada topik" dan dapat dibuka kembali. Ini adalah pertanyaan - khususnya tentang pemrograman.
Danijel-James W
3
Saya terkejut. Terkejut menemukan bahwa dokumentasi Linux dipenuhi dengan tautologi. xkcd.com/703 git-man-page-generator.lokaltog.net
allyourcode

Jawaban:

323

Menjalankan melalui perintah /usr/bin/envmemiliki manfaat mencari apa pun versi default dari program ini adalah di saat env ironment.

Dengan cara ini, Anda tidak perlu mencarinya di tempat tertentu pada sistem, karena jalur tersebut mungkin berada di lokasi yang berbeda di sistem yang berbeda. Selama itu di jalan Anda, itu akan menemukannya.

Salah satu kelemahannya adalah Anda tidak akan dapat melewati lebih dari satu argumen (mis. Anda tidak akan bisa menulis /usr/bin/env awk -f) jika Anda ingin mendukung Linux, karena POSIX tidak jelas tentang bagaimana garis harus ditafsirkan, dan Linux menginterpretasikan semuanya setelah yang pertama. ruang untuk menunjukkan satu argumen. Anda dapat menggunakan /usr/bin/env -Sbeberapa versi envuntuk menyiasatinya, tetapi skrip tersebut akan menjadi semakin portabel dan rusak pada sistem yang cukup baru (mis. Bahkan Ubuntu 16.04 jika tidak lebih baru).

Kelemahan lain adalah bahwa karena Anda tidak memanggil executable eksplisit, itu punya potensi kesalahan, dan pada masalah keamanan sistem multi-pengguna (jika seseorang berhasil mendapatkan executable mereka dipanggil bashdi jalur Anda, misalnya).

#!/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

Dalam beberapa situasi, yang pertama mungkin lebih disukai (seperti menjalankan skrip python dengan beberapa versi python, tanpa harus mengerjakan ulang baris yang dapat dieksekusi). Tetapi dalam situasi di mana keamanan adalah fokus, yang terakhir lebih disukai, karena membatasi kemungkinan injeksi kode.

Alec Bennett
sumber
22
Kelemahan lainnya adalah Anda tidak bisa memberikan argumen tambahan kepada juru bahasa.
Keith Thompson
1
@KeithThompson: Info salah. Anda dapat meneruskan opsi ke juru bahasa yang mendasarinya menggunakan / usr / bin / env!
Gaurav Agarwal
4
@GauravAgarwal: Tidak di sistem saya. Sebuah script yang berisi hanya satu baris ini: #!/usr/bin/env echo Hellomengeluh: /usr/bin/env: echo Hello: No such file or directory. Rupanya itu berlaku echo Hellosebagai argumen tunggal /usr/bin/env.
Keith Thompson
1
@ AndréLaszlo: envPerintah ini tentu saja memungkinkan argumen untuk diteruskan ke perintah. Masalahnya adalah semantik #!baris, dan itu tergantung pada kernel. Kernel Linux terbaru memang memungkinkan hal-hal seperti #!/usr/bin/env command args, tetapi kernel Linux yang lebih tua dan sistem lainnya tidak.
Keith Thompson
1
Mengapa ada backticks di blok kode? Bukankah mereka harus dihapus?
Benjamin W.
64

Menggunakan #!/usr/bin/env NAMEmembuat shell mencari kecocokan pertama NAME dalam variabel lingkungan $ PATH. Ini bisa bermanfaat jika Anda tidak mengetahui jalan absolut atau tidak ingin mencarinya.

Dolphiniac
sumber
9
Setidaknya Anda harus tahu di mana env :).
ᐅ devrimbaris
1
Jawaban yang sangat bagus. Jelaskan dengan singkat apa yang dilakukan env shebang, alih-alih mengatakan "pilih program berdasarkan konfigurasi sistem Anda"
De Novo
13

Alih-alih secara eksplisit mendefinisikan jalur ke penerjemah seperti di /usr/bin/bash/, dengan menggunakan perintah env, penerjemah dicari dan diluncurkan dari mana pun pertama kali ditemukan. Ini memiliki kelebihan dan kekurangan

safay
sumber
Pada kebanyakan sistem, mereka akan secara fungsional sama, tetapi itu tergantung pada lokasi executable bash dan env Anda. Namun, tidak yakin bagaimana ini akan memengaruhi variabel lingkungan.
safay
2
"Adalah mungkin untuk menentukan penerjemah tanpa menggunakan env, dengan memberikan path lengkap ke penerjemah. Masalahnya adalah bahwa pada sistem komputer yang berbeda, jalur yang tepat mungkin berbeda. Dengan menggunakan env, penerjemah dicari dan terletak di saat skrip dijalankan. Skrip ini membuat lebih portabel, tetapi juga meningkatkan risiko bahwa penerjemah yang salah dipilih karena mencari kecocokan di setiap direktori di jalur pencarian yang dapat dieksekusi. Skrip ini juga mengalami masalah yang sama yaitu jalur ke biner env juga mungkin berbeda berdasarkan per-mesin. "- Wikipedia
Mike Clark
10

Jika skrip shell mulai dengan #!/bin/bash, mereka akan selalu dijalankan dengan bashdari /bin. Jika mereka Namun mulai dengan #!/usr/bin/env bash, mereka akan mencari bashdi $PATHdan kemudian mulai dengan yang pertama mereka dapat menemukan.

Mengapa ini berguna? Asumsikan Anda ingin menjalankan bashskrip, yang memerlukan bash 4.x atau lebih baru, namun sistem Anda hanya memiliki bash3.x diinstal dan saat ini distribusi Anda tidak menawarkan versi yang lebih baru atau Anda bukan administrator dan tidak dapat mengubah apa yang diinstal pada sistem itu .

Tentu saja, Anda dapat mengunduh kode sumber bash dan membangun bash Anda sendiri dari awal, ~/binmisalnya sebagai contoh. Dan Anda juga dapat memodifikasi $PATHvariabel Anda di .bash_profilefile Anda untuk dimasukkan ~/binsebagai entri pertama ( PATH=$HOME/bin:$PATHkarena ~tidak akan diperluas dalam $PATH). Jika sekarang Anda menelepon bash, shell pertama-tama akan mencarinya secara $PATHberurutan, jadi itu dimulai dengan ~/bin, di mana ia akan menemukan bash. Hal yang sama terjadi jika skrip mencari bashmenggunakan #!/usr/bin/env bash, jadi skrip ini sekarang akan bekerja pada sistem Anda menggunakan custom bashbuild Anda.

Salah satu kelemahannya adalah, hal ini dapat menyebabkan perilaku yang tidak terduga, misalnya skrip yang sama pada mesin yang sama dapat berjalan dengan penerjemah berbeda untuk lingkungan yang berbeda atau pengguna dengan jalur pencarian yang berbeda, menyebabkan semua jenis sakit kepala.

Kelemahan terbesar envadalah bahwa beberapa sistem hanya akan mengizinkan satu argumen, jadi Anda tidak dapat melakukan ini #!/usr/bin/env <interpreter> <arg>, karena sistem akan melihat <interpreter> <arg>sebagai satu argumen (mereka akan memperlakukannya seolah-olah ungkapan itu dikutip) dan dengan demikian envakan mencari nama juru bahasa <interpreter> <arg>. Perhatikan bahwa ini bukan masalah envperintah itu sendiri, yang selalu memungkinkan beberapa parameter dilewati tetapi dengan parser shebang dari sistem yang mem-parsing baris ini sebelum bahkan memanggil env. Sementara ini telah diperbaiki pada sebagian besar sistem tetapi jika skrip Anda ingin menjadi sangat portabel, Anda tidak dapat mengandalkan bahwa ini telah diperbaiki pada sistem yang akan Anda jalankan.

Ia bahkan dapat memiliki implikasi keamanan, misalnya jika sudotidak dikonfigurasi untuk membersihkan lingkungan atau $PATHdikeluarkan dari pembersihan. Biarkan saya menunjukkan ini:

Biasanya /binadalah tempat yang terlindungi dengan baik, hanya rootdapat mengubah apa pun di sana. Direktori rumah Anda tidak, bagaimanapun, program apa pun yang Anda jalankan dapat membuat perubahan padanya. Itu berarti kode jahat dapat menempatkan palsu bashke beberapa direktori tersembunyi, memodifikasi Anda .bash_profileuntuk memasukkan direktori itu di Anda $PATH, sehingga semua skrip yang menggunakan #!/usr/bin/env bashakhirnya akan berjalan dengan palsu itu bash. Jika sudoterus $PATH, Anda dalam masalah besar.

Misalnya, pertimbangkan alat membuat file ~/.evil/bashdengan konten berikut:

#!/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 "$@"

Mari kita membuat skrip sederhana sample.sh:

#!/usr/bin/env bash

echo "Hello World"

Bukti konsep (pada sistem tempat sudomenyimpan $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

Biasanya semua shell klasik harus ditempatkan di /bindan jika Anda tidak ingin menempatkannya di sana untuk alasan apa pun, itu benar-benar bukan masalah untuk menempatkan symlink di /binyang menunjuk ke lokasi mereka yang sebenarnya (atau mungkin /binitu sendiri adalah symlink), jadi Saya akan selalu pergi dengan #!/bin/shdan #!/bin/bash. Ada terlalu banyak yang akan rusak jika ini tidak berfungsi lagi. Bukan berarti POSIX akan membutuhkan posisi ini (POSIX tidak membakukan nama jalur dan dengan demikian bahkan tidak menstandardisasi fitur shebang sama sekali) tetapi mereka sangat umum, bahwa bahkan jika suatu sistem tidak menawarkan /bin/sh, mungkin masih akan mengerti #!/bin/shdan tahu apa yang harus dilakukan dengannya dan mungkin hanya untuk kompatibilitas dengan kode yang ada.

Tetapi untuk penerjemah yang lebih modern, non standar, opsional seperti Perl, PHP, Python, atau Ruby, itu tidak benar-benar ditentukan di mana pun di mana mereka seharusnya berada. Mereka mungkin dalam /usr/bintetapi mereka mungkin juga berada di /usr/local/binatau di cabang hirarki yang sama sekali berbeda ( /opt/..., /Applications/..., dll). Itu sebabnya ini sering menggunakan #!/usr/bin/env xxxsintaks shebang.

Mecki
sumber
4

Saya merasa ini berguna, karena ketika saya tidak tahu tentang env, sebelum saya mulai menulis skrip saya melakukan ini:

type nodejs > scriptname.js #or any other environment

dan kemudian saya memodifikasi baris dalam file ke shebang.
Saya melakukan ini, karena saya tidak selalu ingat di mana nodejs di komputer saya - / usr / bin / atau / bin /, jadi bagi saya envsangat berguna. Mungkin ada detail dengan ini, tapi ini alasan saya

sudo97
sumber