Saya perhatikan bahwa beberapa skrip yang saya peroleh dari orang lain memiliki shebang #!/path/to/NAME
sementara yang lain (menggunakan alat yang sama, NAME) memiliki shebang #!/usr/bin/env NAME
.
Keduanya tampaknya berfungsi dengan baik. Dalam tutorial (pada Python, misalnya), tampaknya ada saran bahwa shebang yang terakhir lebih baik. Tapi, saya tidak begitu mengerti mengapa demikian.
Saya menyadari bahwa, untuk menggunakan shebang yang terakhir, NAME harus di PATH sedangkan shebang pertama tidak memiliki batasan ini.
Selain itu, tampaknya (bagi saya) bahwa yang pertama akan menjadi shebang yang lebih baik, karena ia menentukan dengan tepat di mana NAME berada. Jadi, dalam hal ini, jika ada beberapa versi NAME (mis., / Usr / bin / NAME, / usr / local / bin / NAME), case pertama menentukan mana yang akan digunakan.
Pertanyaan saya adalah mengapa shebang pertama lebih disukai daripada yang kedua?
sumber
Jawaban:
Belum tentu lebih baik.
Keuntungannya
#!/usr/bin/env python
adalah bahwa ia akan menggunakan apa pun yangpython
dapat dieksekusi muncul pertama kali di pengguna$PATH
.The Kerugian dari
#!/usr/bin/env python
adalah bahwa ia akan menggunakan apa pun yangpython
dieksekusi muncul pertama kali di pengguna$PATH
.Itu berarti bahwa skrip dapat berperilaku berbeda tergantung pada siapa yang menjalankannya. Untuk satu pengguna, mungkin menggunakan
/usr/bin/python
yang diinstal dengan OS. Untuk yang lain, mungkin menggunakan eksperimen/home/phred/bin/python
yang tidak berfungsi dengan benar.Dan jika
python
hanya dipasang di/usr/local/bin
, pengguna yang tidak memiliki/usr/local/bin
di$PATH
bahkan tidak akan dapat menjalankan script. (Itu mungkin tidak terlalu mungkin pada sistem modern, tetapi itu bisa dengan mudah terjadi untuk penerjemah yang lebih tidak jelas.)Dengan menentukan
#!/usr/bin/python
Anda menentukan penerjemah mana yang akan digunakan untuk menjalankan skrip pada sistem tertentu .Masalah potensial lainnya adalah bahwa
#!/usr/bin/env
triknya tidak memungkinkan Anda meneruskan argumen ke intrepreter (selain nama skrip, yang diteruskan secara implisit). Ini biasanya bukan masalah, tetapi bisa saja. Banyak skrip Perl yang ditulis#!/usr/bin/perl -w
, tetapiuse warnings;
merupakan pengganti yang disarankan akhir-akhir ini. Skrip Csh harus digunakan#!/bin/csh -f
- tetapi skrip csh tidak disarankan sejak awal. Tapi mungkin ada contoh lain.Saya memiliki sejumlah skrip Perl dalam sistem kontrol sumber pribadi yang saya instal ketika saya membuat akun pada sistem baru. Saya menggunakan skrip penginstal yang mengubah
#!
baris setiap skrip saat menginstalnya di saya$HOME/bin
. (Saya tidak harus menggunakan apa pun selain#!/usr/bin/perl
akhir - akhir ini; ini kembali ke masa ketika Perl sering tidak diinstal secara default.)Poin minor:
#!/usr/bin/env
trik ini bisa dibilang merupakan penyalahgunaanenv
perintah, yang semula dimaksudkan (sesuai namanya) untuk memohon perintah dengan lingkungan yang diubah. Selain itu, beberapa sistem yang lebih lama (termasuk SunOS 4, jika saya ingat dengan benar) tidak memilikienv
perintah/usr/bin
. Tak satu pun dari ini cenderung menjadi perhatian yang signifikan.env
tidak bekerja dengan cara ini, banyak skrip yang menggunakan#!/usr/bin/env
trik, dan penyedia OS tidak akan melakukan apa pun untuk memecahkannya. Ini mungkin menjadi masalah jika Anda ingin naskah Anda untuk berjalan pada sistem benar-benar tua, tapi kemudian Anda mungkin perlu mengubah pula.Masalah lain yang mungkin, (terima kasih kepada Sopalajo de Arrierez untuk menunjukkannya dalam komentar) adalah bahwa pekerjaan cron dijalankan dengan lingkungan terbatas. Secara khusus,
$PATH
biasanya sesuatu seperti/usr/bin:/bin
. Jadi jika direktori yang berisi penerjemah tidak kebetulan berada di salah satu direktori itu, bahkan jika itu di default Anda$PATH
di shell pengguna, maka/usr/bin/env
triknya tidak akan berhasil. Anda dapat menentukan jalur yang tepat, atau Anda dapat menambahkan baris ke crontab Anda untuk mengatur$PATH
(man 5 crontab
untuk detail).sumber
use v5.12;
melayani beberapa tujuan itu. Dan#!/usr/bin/env perl5.12
akan gagal jika sistem memiliki Perl 5.14 tetapi tidak 5.12. Untuk Python 2 vs. 3,#!/usr/bin/python2
dan#!/usr/bin/python3
kemungkinan akan berfungsi./usr/local/bin
. Saya kebetulan tahu bahwa itu berfungsi dengan benar/usr/bin/perl
. Saya tidak tahu apakah itu berfungsi dengan apa pun yangperl
dapat dieksekusi yang dilakukan oleh beberapa pengguna acak dalam dirinya$PATH
. Mungkin seseorang bereksperimen dengan beberapa versi Perl kuno; karena saya tentukan#!/usr/bin/perl
, skrip saya (yang pengguna bahkan tidak tahu atau peduli adalah skrip Perl) tidak akan berhenti bekerja./usr/bin/perl
, saya akan mengetahuinya dengan sangat cepat, dan merupakan tanggung jawab pemilik / administrator sistem untuk selalu memperbaruinya. Jika Anda ingin menjalankan skrip saya dengan perl Anda sendiri, jangan ragu untuk mengambil dan memodifikasi salinan atau memintanya melaluiperl foo
. (Dan Anda mungkin mempertimbangkan kemungkinan bahwa 55 orang yang mengangkat jawaban ini juga mengetahui satu atau dua hal. Tentu saja mungkin Anda benar dan mereka semua salah, tetapi itu bukan cara saya bertaruh.)Karena / usr / bin / env dapat menafsirkan Anda
$PATH
, yang membuat skrip lebih portabel.Hanya akan menjalankan skrip Anda jika python diinstal di / usr / local / bin.
Akan menginterpretasikan Anda
$PATH
, dan menemukan python di direktori mana pun di Anda$PATH
.Jadi skrip Anda lebih portabel, dan akan berfungsi tanpa modifikasi pada sistem tempat python diinstal
/usr/bin/python
, atau/usr/local/bin/python
, atau bahkan direktori khusus (yang telah ditambahkan ke$PATH
), seperti/opt/local/bin/python
.Portabilitas adalah satu-satunya alasan penggunaan
env
lebih disukai daripada jalur kode keras.sumber
python
file yang dapat dieksekusi sangat umum karenavirtualenv
penggunaan meningkat.#! python
, mengapa itu tidak digunakan?#! python
tidak digunakan karena Anda harus berada di direktori yang sama dengan biner python, karena barewordpython
ditafsirkan sebagai path lengkap ke file. Jika Anda tidak memiliki biner python di direktori saat ini, Anda akan mendapatkan error sepertibash: ./script.py: python: bad interpreter: No such file or directory
. Sama seperti jika Anda menggunakan#! /not/a/real/path/python
Menentukan jalur absolut lebih tepat pada sistem yang diberikan. Kelemahannya adalah terlalu tepat. Misalkan Anda menyadari bahwa instalasi sistem Perl terlalu lama untuk skrip Anda dan Anda ingin menggunakan skrip Anda sendiri: maka Anda harus mengedit skrip dan mengubahnya
#!/usr/bin/perl
menjadi#!/home/myname/bin/perl
. Lebih buruk lagi, jika Anda memiliki Perl/usr/bin
pada beberapa mesin,/usr/local/bin
pada yang lain, dan/home/myname/bin/perl
pada mesin lain, maka Anda harus mempertahankan tiga salinan skrip yang terpisah dan menjalankan yang sesuai pada masing-masing mesin.#!/usr/bin/env
istirahat jikaPATH
buruk, tetapi begitu juga hampir semua hal. Mencoba untuk beroperasi dengan yang burukPATH
sangat jarang berguna, dan menunjukkan bahwa Anda tahu sedikit tentang sistem yang sedang dijalankan skrip, jadi Anda tidak dapat mengandalkan jalur absolut apa pun.Ada dua program yang lokasinya dapat Anda andalkan di hampir setiap varian unix:
/bin/sh
dan/usr/bin/env
. Beberapa varian Unix yang tidak jelas dan sebagian besar sudah/bin/env
tidak ada/usr/bin/env
, tetapi Anda tidak akan menemukannya. Sistem modern/usr/bin/env
justru karena penggunaannya yang luas di shebangs./usr/bin/env
adalah sesuatu yang dapat Anda andalkan.Selain itu
/bin/sh
, satu-satunya waktu Anda harus menggunakan jalur absolut di shebang adalah ketika skrip Anda tidak dimaksudkan untuk dibawa-bawa, sehingga Anda dapat mengandalkan lokasi yang diketahui untuk penerjemah. Misalnya, skrip bash yang hanya berfungsi di Linux dapat digunakan dengan aman#!/bin/bash
. Sebuah skrip yang hanya dimaksudkan untuk digunakan di rumah dapat bergantung pada konvensi lokasi juru bahasa.#!/usr/bin/env
memang memiliki kelemahan. Ini lebih fleksibel daripada menentukan jalur absolut tetapi masih membutuhkan mengetahui nama juru bahasa. Kadang-kadang Anda mungkin ingin menjalankan juru bahasa yang tidak ada dalam$PATH
, misalnya di lokasi yang relatif terhadap skrip. Dalam kasus seperti itu, Anda sering dapat membuat skrip polyglot yang dapat diartikan baik oleh shell standar maupun oleh juru bahasa yang Anda inginkan. Misalnya, untuk membuat skrip Python 2 portabel untuk sistem di manapython
Python 3 danpython2
Python 2, dan ke sistem di manapython
Python 2 danpython2
tidak ada:sumber
Khusus untuk perl, menggunakan
#!/usr/bin/env
adalah ide yang buruk karena dua alasan.Pertama, ini tidak portabel. Pada beberapa platform tidak jelas, env tidak ada di / usr / bin. Kedua, seperti yang dicatat Keith Thompson , ini dapat menyebabkan masalah dengan melewatinya argumen di jalur shebang. Solusi portabel maksimal adalah ini:
Untuk detail tentang cara kerjanya, lihat 'perldoc perlrun' dan apa yang dikatakannya tentang argumen -x.
sumber
Alasan ada perbedaan antara keduanya adalah karena bagaimana skrip dieksekusi.
Menggunakan
/usr/bin/env
(yang, sebagaimana disebutkan dalam jawaban lain, tidak/usr/bin
aktif pada beberapa OS) diperlukan karena Anda tidak bisa hanya menempatkan nama yang dapat dieksekusi setelah#!
- itu harus menjadi jalur absolut. Ini karena#!
mekanismenya bekerja pada level yang lebih rendah daripada shell. Itu bagian dari pemuat biner kernel. Ini bisa diuji. Masukkan ini ke dalam file dan tandai itu dapat dieksekusi:Anda akan menemukannya mencetak kesalahan seperti ini ketika Anda mencoba menjalankannya:
Jika file ditandai executable dan dimulai dengan
#!
, kernel (yang tidak tahu tentang$PATH
atau direktori saat ini: ini adalah konsep pengguna-tanah) akan mencari file menggunakan path absolut. Karena menggunakan jalur absolut bermasalah (seperti yang disebutkan dalam jawaban lain), seseorang datang dengan sebuah trik: Anda dapat menjalankan/usr/bin/env
(yang hampir selalu di lokasi itu) untuk menjalankan sesuatu menggunakan$PATH
.sumber
Ada dua masalah lagi dengan penggunaan
#!/usr/bin/env
Itu tidak memecahkan masalah menentukan path lengkap ke penerjemah, itu hanya memindahkannya ke
env
.env
tidak lebih dijamin akan di/usr/bin/env
daribash
dijamin di/bin/bash
atau python di/usr/bin/python
.env
menimpa ARGV [0] dengan nama penerjemah (e..g bash atau python).Ini mencegah nama skrip Anda muncul di, misalnya,
ps
output (atau mengubah bagaimana / di mana ia muncul) dan membuatnya tidak mungkin untuk menemukannya dengan, misalnya,ps -C scriptname.sh
[perbarui 2016-06-04]
Dan masalah ketiga:
Mengubah PATH Anda lebih berfungsi daripada hanya mengedit baris pertama skrip, terutama ketika skrip pengeditan tersebut sepele. misalnya:
printf "%s\n" 1 i '#!'$(type -P python2) . w | ed foo.py
Menambahkan atau menunda pendahuluan direktori ke $ PATH cukup mudah (walaupun Anda masih harus mengedit file untuk membuatnya permanen - Anda
~/.profile
atau apa pun - dan itu jauh dari mudah untuk skrip BAHWA edit karena PATH dapat diatur di mana saja di skrip , tidak di baris pertama).Mengubah urutan direktori PATH secara signifikan lebih sulit .... dan jauh lebih sulit daripada hanya mengedit
#!
baris.Dan Anda masih memiliki semua masalah lain yang menggunakan
#!/usr/bin/env
memberi Anda.@jlliagre menyarankan dalam komentar yang
#!/usr/bin/env
berguna untuk menguji skrip Anda dengan beberapa versi juru bahasa, "dengan hanya mengubah urutan PATH / PATH mereka"Jika Anda perlu melakukannya, jauh lebih mudah untuk hanya memiliki beberapa
#!
baris di bagian atas skrip Anda (mereka hanya komentar di mana saja tetapi baris pertama) dan memotong / menyalin-rekatkan yang ingin Anda gunakan sekarang untuk baris pertama.Di
vi
, itu akan sesederhana memindahkan kursor ke#!
baris yang Anda inginkan, lalu mengetikdd1GP
atauY1GP
. Bahkan dengan editor yang sepelenano
, butuh beberapa detik untuk menggunakan mouse untuk menyalin dan menempel.Secara keseluruhan, keuntungan menggunakan
#!/usr/bin/env
minimal, dan tentu saja tidak mendekati kerugiannya. Bahkan keuntungan "kenyamanan" sebagian besar adalah ilusi.IMO, itu adalah ide konyol yang dipromosikan oleh jenis programmer tertentu yang berpikir bahwa sistem operasi bukanlah sesuatu untuk dikerjakan, mereka adalah masalah yang harus dikerjakan (atau diabaikan sama sekali).
PS: inilah skrip sederhana untuk mengubah penerjemah beberapa file sekaligus.
change-shebang.sh
:Jalankan sebagai, misalnya,
change-shebang.sh python2.7 *.py
atauchange-shebang.sh $HOME/bin/my-experimental-ruby *.rb
sumber
csh
scripting mempromosikan solusi yang buruk: itu bekerja tetapi ada alternatif yang jauh lebih baik.#!
baris./usr/bin/env
dan saya harus menyingkirkannya secara lokal, atau jika mereka tidak menggunakannya dan saya perlu menambahkannya. Kedua kasus dapat terjadi, dan karena itu skrip yang disediakan dalam jawaban ini adalah alat yang berpotensi berguna untuk dimiliki dalam kotak alat.Menambahkan contoh lain di sini:
Penggunaan
env
juga berguna ketika Anda ingin berbagi skrip antara beberaparvm
lingkungan, misalnya.Menjalankan ini pada baris cmd, menunjukkan versi ruby mana yang akan digunakan saat
#!/usr/bin/env ruby
digunakan di dalam skrip:env ruby --version
Karena itu, ketika Anda menggunakan
env
, Anda dapat menggunakan versi ruby yang berbeda melalui rvm, tanpa mengubah skrip Anda.sumber
Jika Anda menulis murni untuk diri sendiri atau untuk pekerjaan Anda dan lokasi juru bahasa yang Anda panggil selalu berada di tempat yang sama, tentu saja gunakan jalur langsung. Dalam semua kasus lain gunakan
#!/usr/bin/env
.Inilah alasannya: dalam situasi Anda,
python
juru bahasa berada di tempat yang sama terlepas dari sintaksis mana yang Anda gunakan, tetapi bagi banyak orang itu bisa dipasang di tempat yang berbeda. Meskipun sebagian besar penerjemah pemrograman utama berada di/usr/bin/
banyak perangkat lunak yang lebih baru secara default/usr/local/bin/
.Saya bahkan berpendapat untuk selalu menggunakan
#!/usr/bin/env
karena jika Anda memiliki beberapa versi dari juru bahasa yang sama diinstal dan Anda tidak tahu apa shell Anda akan default, maka Anda mungkin harus memperbaikinya.sumber
Untuk alasan portabilitas dan kompatibilitas, lebih baik digunakan
dari pada
Ada beberapa kemungkinan, di mana biner mungkin berada pada sistem Linux / Unix. Periksa halaman manual hier (7) untuk deskripsi terperinci dari hierarki sistem file.
FreeBSD misalnya menginstal semua perangkat lunak, yang bukan bagian dari sistem dasar, di / usr / local / . Karena bash bukan bagian dari sistem dasar, bash binary diinstal di / usr / local / bin / bash .
Ketika Anda menginginkan skrip bash / csh / perl / portable, yang berjalan di sebagian besar Distribusi Linux dan FreeBSD, Anda harus menggunakan #! / Usr / bin / env .
Juga perhatikan, bahwa sebagian besar instalasi Linux juga (keras) menautkan binary env ke / bin / env atau softlinked / usr / bin ke / bin yang tidak boleh digunakan di shebang. Jadi jangan gunakan #! / Bin / env .
sumber