Jika skrip shell dimulai dengan #!/bin/bash
, mereka akan selalu berjalan dengan bash
dari /bin
. Namun jika mereka mulai dengan #!/usr/bin/env bash
, mereka akan mencari bash
di $PATH
dan kemudian mulai dengan yang pertama yang dapat mereka temukan.
Mengapa ini berguna? Asumsikan Anda ingin menjalankan bash
skrip, yang memerlukan bash 4.x atau yang lebih baru, namun sistem Anda hanya memiliki bash
3.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 membuat bash Anda sendiri dari awal, menempatkannya di ~/bin
Misalnya. Dan Anda juga dapat memodifikasi $PATH
Anda variabel di .bash_profile
Anda file untuk menyertakan ~/bin
sebagai entri pertama (PATH=$HOME/bin:$PATH
sebagai ~
tidak akan diperluas di $PATH
). Jika sekarang Anda memanggil bash
, pertama-tama shell akan mencarinya di $PATH
berurutan, jadi dimulai dengan ~/bin
, di mana ia akan menemukan bash
Anda . Hal yang sama terjadi jika skrip mencari bash
menggunakan #!/usr/bin/env bash
, jadi skrip ini sekarang akan bekerja di sistem Anda menggunakan bash
khusus Anda membangun.
Satu kelemahannya adalah, hal ini dapat menyebabkan perilaku yang tidak terduga, mis. skrip yang sama pada mesin yang sama dapat berjalan dengan juru bahasa yang berbeda untuk lingkungan atau pengguna yang berbeda dengan jalur pencarian yang berbeda, menyebabkan semua jenis sakit kepala.
Kelemahan terbesar dengan env
adalah bahwa beberapa sistem hanya 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 ekspresi dikutip) dan dengan demikian env
akan mencari juru bahasa bernama <interpreter> <arg>
. Perhatikan bahwa ini bukan masalah dari env
perintah itu sendiri, yang selalu mengizinkan beberapa parameter untuk dilewati tetapi dengan parser shebang dari sistem yang mem-parsing baris ini bahkan sebelum memanggil env
. Sementara ini telah diperbaiki pada sebagian besar sistem tetapi jika skrip Anda ingin sangat portabel, Anda tidak dapat mengandalkan bahwa ini telah diperbaiki pada sistem yang akan Anda jalankan.
Itu bahkan dapat memiliki implikasi keamanan, mis. jika sudo
tidak dikonfigurasi untuk membersihkan lingkungan atau $PATH
dikeluarkan dari pembersihan. Izinkan saya mendemonstrasikan ini:
Biasanya /bin
adalah tempat yang terlindungi dengan baik, hanya root
mampu mengubah apa pun di sana. Namun, direktori home Anda tidak, program apa pun yang Anda jalankan dapat mengubahnya. Itu berarti kode berbahaya dapat menempatkan bash
palsu ke beberapa direktori tersembunyi, ubah .bash_profile
Anda untuk memasukkan direktori itu ke dalam $PATH
Anda , jadi semua skrip menggunakan #!/usr/bin/env bash
akan berakhir dengan bash
palsu itu . Jika sudo
menyimpan $PATH
, Anda berada dalam masalah besar.
Misalnya. pertimbangkan alat untuk membuat file ~/.evil/bash
dengan 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 "[email protected]"
Mari kita membuat skrip sederhana sample.sh
:
#!/usr/bin/env bash
echo "Hello World"
Bukti konsep (pada sistem di mana sudo
menyimpan $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 terletak di /bin
dan jika Anda tidak ingin menempatkannya di sana karena alasan apa pun, sebenarnya bukan masalah untuk menempatkan symlink di /bin
yang menunjuk ke lokasi aslinya (atau mungkin /bin
itu sendiri adalah symlink), jadi saya akan selalu menggunakan #!/bin/sh
dan #!/bin/bash
. Terlalu banyak yang akan rusak jika ini tidak berfungsi lagi. Bukannya POSIX akan membutuhkan posisi ini (POSIX tidak membakukan nama jalur dan karenanya bahkan tidak membakukan fitur shebang sama sekali) tetapi mereka sangat umum, bahkan jika sistem tidak akan menawarkan /bin/sh
, mungkin masih mengerti #!/bin/sh
dan tahu apa yang harus dilakukan dengannya dan semoga itu hanya untuk kompatibilitas dengan kode yang ada.
Tetapi untuk interpreter opsional yang lebih modern, tidak standar, seperti Perl, PHP, Python, atau Ruby, itu tidak benar-benar ditentukan di mana pun mereka seharusnya berada. Mereka mungkin ada di /usr/bin
tetapi mereka mungkin juga berada di /usr/local/bin
atau di cabang hierarki yang sama sekali berbeda (/opt/...
, /Applications/...
, dll.). Itu sebabnya ini sering menggunakan #!/usr/bin/env xxx
sintaks shebang.
Menggunakan #!/usr/bin/env NAME
membuat shell mencari kecocokan pertama NAME dalam variabel lingkungan $PATH. Ini bisa berguna jika Anda tidak mengetahui jalur absolut atau tidak ingin menelusurinya.
Menjalankan perintah melalui /usr/bin/env
memiliki keuntungan mencari apa pun versi default program di env Anda saat ini penyetrikaan.
Dengan cara ini, Anda tidak perlu mencarinya di tempat tertentu di sistem, karena jalur tersebut mungkin berada di lokasi yang berbeda di sistem yang berbeda. Selama berada di jalur Anda, ia akan menemukannya.
Satu kelemahannya adalah Anda tidak akan dapat memberikan lebih dari satu argumen (mis. Anda tidak akan dapat menulis /usr/bin/env awk -f
) jika Anda ingin mendukung Linux, karena POSIX tidak jelas tentang bagaimana garis harus ditafsirkan, dan Linux menafsirkan semuanya setelah spasi pertama untuk menunjukkan satu argumen. Anda dapat menggunakan /usr/bin/env -S
pada beberapa versi env
untuk mengatasi ini, tetapi kemudian skrip akan menjadi kurang portabel dan rusak pada sistem yang cukup baru (mis. bahkan Ubuntu 16.04 jika tidak lebih baru).
Kelemahan lainnya adalah karena Anda tidak memanggil executable eksplisit, ada potensi kesalahan, dan pada masalah keamanan sistem multipengguna (jika seseorang berhasil membuat executable mereka disebut bash
di 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). Namun dalam situasi di mana keamanan menjadi fokus, yang terakhir lebih disukai, karena membatasi kemungkinan injeksi kode.