Shebang olarak “#! / Path / to / NAME” yerine “#! / Usr / bin / env NAME” kullanmak neden daha iyi?


458

Başkalarından edindiğim bazı betiklerin shebang'a sahip olduğunu, #!/path/to/NAMEdiğerleri ise (aynı aracı kullanan NAME) shebang'ın olduğunu fark ettim #!/usr/bin/env NAME.

Her ikisi de düzgün çalışıyor gibi görünüyor. Derslerde (örneğin Python'da), ikinci shebang'ın daha iyi olduğu yönünde bir öneri var gibi görünüyor. Ancak bunun neden böyle olduğunu tam olarak anlamadım.

İkinci shebang'ı kullanabilmek için, NAME'in PATH içinde olması gerekirken, ilk shebang'ın bu kısıtlamaya sahip olmadığını fark ettim.

Ayrıca, (bana), NAME'in tam olarak nerede olduğunu belirttiğinden, ilkinin daha iyi shebang olacağı anlaşılıyor. Bu nedenle, bu durumda, birden fazla NAME sürümü varsa (örneğin, / usr / bin / NAME, / usr / local / bin / NAME), ilk durum hangisinin kullanılacağını belirtir.

Sorum şu: neden ilk shebang ikinciye tercih ediliyor?


12
Bu cevabı gör ...
jasonwryan

@ TheGeeko61: Benim durumumda kırılmış bir şey vardı ve bazı değişkenler env'de değildi. Bu yüzden env'nin doğru yüklenip yüklenmediğini doğrulamak için bu shebang'ı kullanmanızı öneririm.
Gigamegs

Yanıtlar:


462

Bu mutlaka daha iyi değil.

Bunun avantajı , kullanıcının ilk önce göründüğü #!/usr/bin/env pythonher şeyi kullanabilmesidir .python$PATH

Dezavantaj ait #!/usr/bin/env pythonne olursa olsun kullanmak olacaktır pythonkullanıcının ilk görünen yürütülebilir $PATH.

Bu, senaryoyu kimin çalıştırdığına bağlı olarak farklı davranabileceği anlamına gelir. Bir kullanıcı /usr/bin/pythoniçin işletim sistemi ile kurulmuş olanı kullanabilir . Bir başkası için, /home/phred/bin/pythontam olarak düzgün çalışmayan bir deney kullanabilir .

Ve eğer pythonsadece yüklenir /usr/local/bin, olmayan bir kullanıcı /usr/local/biniçinde $PATHbile komut dosyasını çalıştırmak mümkün olmayacaktır. (Bu muhtemelen modern sistemlerde pek olası değildir, ancak daha belirsiz bir tercüman için kolayca olabilir.)

Belirterek #!/usr/bin/python, komut dosyasını belirli bir sistemde çalıştırmak için tam olarak hangi tercümanın kullanılacağını belirtin .

Bir diğer potansiyel sorun olduğunu #!/usr/bin/envhüner Eğer intrepreter argümanlar geçmesine izin vermez (örtük geçirilen komut, adı başka). Bu genellikle bir sorun değil, olabilir. Birçok Perl betiği ile yazılmıştır #!/usr/bin/perl -w, ancak use warnings;bugünlerde önerilen değiştirmedir. Csh scriptleri kullanmalı #!/bin/csh -f- fakat csh scriptleri her şeyden önce önerilmez . Ancak başka örnekler olabilir.

Yeni bir sistemde bir hesap kurduğumda kurduğum kişisel bir kaynak kontrol sisteminde bir dizi Perl betiği var. #!Her betiğin satırını benim yerime koyarken değiştiren bir yükleyici betiği kullanıyorum $HOME/bin. ( #!/usr/bin/perlSon zamanlarda başka bir şey kullanmak zorunda kalmamıştım ; Perl sık sık varsayılan olarak yüklenmediği zamanlara dayanıyor.)

Küçük bir nokta: #!/usr/bin/envpüf nokta , tartışmalı bir envşekilde, değiştirilmiş bir çevreye sahip bir komutu çağırmak için esasen amaçlanan komutun kötüye kullanılmasıdır . Ayrıca, bazı eski sistemler (doğru hatırlıyorsam SunOS 4 dahil) bu envkomutlara sahip değildi /usr/bin. Bunların hiçbirinin önemli bir endişe olması muhtemel değildir. envbu şekilde çalışırsa, birçok senaryo bu #!/usr/bin/envnumarayı kullanır ve işletim sistemi sağlayıcılarının bu sorunu çözecek bir şey yapması beklenmez. Bu belki size komut gerçekten eski sistemde çalıştırmak istiyorum, ama o zaman yine de değiştirmeniz gereken muhtemel iseniz bir sorun olabilir.

Bir diğer olası sorun (Sopalajo de Arrierez'in yorumlarda gösterdiği için teşekkür ederiz), cron işlerinin sınırlı bir ortamda yürüdüğüdür. Özellikle, $PATHtipik olarak bir şeydir /usr/bin:/bin. Bu nedenle, tercümanı içeren dizin bu dizinlerden birinde bulunmuyorsa $PATH, bir kullanıcı kabuğundaki varsayılan ayarlarınızda olsa bile , /usr/bin/envhile işe yaramaz. Tam yolu belirtebilir veya ayarlamak için crontab'ınıza bir çizgi ekleyebilirsiniz $PATH( man 5 crontabayrıntılar için).


4
Eğer / usr / bin / perl 5.8 perl ise, $ HOME / bin / perl 5.12 ise ve shebang'larda 5.12 hardcode / usr / bin / perl gerektiren bir script ise, betiği çalıştırmak büyük bir acı olabilir. Ben nadiren PATH / usr / bin / env perl kapmak perl bir sorun olması gördüm, ama genellikle çok yararlı olur. Ve bu icra kesmek daha güzel!
William Pursell

8
Belirli bir tercüman sürümü kullanmak istiyorsanız, / usr / bin / env'nin daha iyi olduğunu belirtmekte fayda var. Genellikle orada çünkü vardır vs. perl5, perl5.12, perl5.10, python3.3 adlı makinenizde, python3.32, açık ve uygulama sadece o sürümü üzerinde test edilmiştir eğer yüklü birden tercüman versiyonları, hala belirtebilirsiniz #! / usr / bin / env perl5.12 ve kullanıcı olağandışı bir yere kurulsa bile sorun yok. Tecrübelerime göre, 'python' genellikle sadece standart sistem sürümüne bir bağlantı (sistemdeki en son sürüm değil).
Kök

6
@root: Perl için use v5.12;hizmet veren bazı o amaç. Ve #!/usr/bin/env perl5.12sistem 5.12 Perl 5.14 var ama değilse başarısız olur. Python 2 - 3 #!/usr/bin/python2ve #!/usr/bin/python3işe yarama olasılığı var.
Keith Thompson

3
@GoodPerson: Yüklenecek bir Perl betiği yazdığımı varsayalım /usr/local/bin. Doğru çalıştığını biliyorum /usr/bin/perl. Ben hiçbir fikri ne olursa olsun birlikte çalışıp çalışmadığını perlkendi içinde olmasını rastgele kullanıcı olur çalıştırılabilir $PATH. Belki birileri Perl’in eski bir versiyonunu deniyordur; Çünkü belirttiğim #!/usr/bin/perlgibi betiğim (kullanıcının mutlaka bilmesi veya umrunda bir Perl betiği olması gerekmiyor) çalışmaz.
Keith Thompson

5
Eğer işe yaramazsa /usr/bin/perl, çok hızlı bir şekilde öğreneceğim ve güncel tutulması sistem sahibinin / yöneticisinin sorumluluğudur. Senaryomu kendi perl'inizle çalıştırmak istiyorsanız, bir kopyasını alıp değiştirmek veya onu kullanmaktan çekinmeyin perl foo. (Ve bu cevabı kaldıran 55 kişinin de bir iki şey bilmesi ihtimalini düşünebilirsiniz. Haklısınız ve hepsinin yanlış olması kesinlikle mümkün, ama bu benim iddia ettiğim yol değil.)
Keith Thompson

101

Çünkü / usr / bin / env, $PATHkomut dosyalarını daha taşınabilir hale getiren yorumunuzu yapabilir .

#!/usr/local/bin/python

Komut dosyanızı yalnızca python / usr / local / bin içine yüklüyse çalıştırır.

#!/usr/bin/env python

Senin yorumlayacak $PATHve herhangi bir dizinde python bulacaksınız $PATH.

Yani betiğinizi taşınabilir ve piton olarak yüklenir sistemlerde değişiklik yapılmadan çalışır /usr/bin/python, ya da /usr/local/bin/python, ya da (eklenmiştir bile özel dizinlere $PATHgibi) /opt/local/bin/python.

Taşınabilirlik, envkodlanmış yollara tercih edilmesinin tek nedenidir .


19
pythonÇalıştırılabilir dosyalar için özel dizinler , virtualenvkullanım arttıkça özellikle yaygındır .
Xiong Chiamiov

1
Peki ya #! pythonneden kullanılmıyor?
kristianp

6
#! pythonkullanılmaz çünkü python binary ile aynı dizinde olmanız gerekir, çünkü bareword pythondosyanın tam yolu olarak yorumlanır. Geçerli dizinde bir python ikili yoksa, gibi bir hata alırsınız bash: ./script.py: python: bad interpreter: No such file or directory. Bu senin kullandığınla aynı#! /not/a/real/path/python
Tim Kennedy

47

Mutlak yolun belirtilmesi, belirli bir sistemde daha kesindir. Olumsuz tarafı çok kesin olmasıdır. Eğer Perl sistem kurulumu betiklerinizden için çok yaşlı olduğunu fark ve bunun yerine kendi kullanmak istediğinizi varsayalım: o zaman komut dosyalarını düzenlemek ve değiştirmek zorunda #!/usr/bin/perliçin #!/home/myname/bin/perl. Daha kötüsü, eğer /usr/binbazı makinelerde, /usr/local/bindiğerlerinde ve /home/myname/bin/perlhatta diğer makinelerde Perl varsa , komut dosyalarının üç ayrı kopyasını saklamanız ve uygun olanı her makinede yürütmeniz gerekir.

#!/usr/bin/envPATHkötü ise kırılır , ancak neredeyse her şey yapar. Kötü bir şekilde çalışmayı denemek PATHçok nadiren yararlıdır ve betiğin çalıştığı sistem hakkında çok az şey bildiğinizi gösterir, bu nedenle hiçbir şekilde mutlak bir yola güvenemezsiniz.

Hemen hemen her unix varyantına güvenebileceğiniz iki program var: /bin/shve /usr/bin/env. Bazı karanlık ve çoğunlukla emekli Unix türevleri vardı /bin/envkalmadan /usr/bin/env, ancak bunları karşılaşmak muhtemel. Modern sistemler, /usr/bin/envshebang'lardaki yaygın kullanımı nedeniyle tam olarak var . /usr/bin/envgüvenebileceğin bir şey.

Bunun dışında /bin/sh, bir shebang'da mutlak bir yol kullanmanız gereken tek zaman senaryonuzun taşınabilir olması gerekmediği içindir, bu yüzden tercüman için bilinen bir yere güvenebilirsiniz. Örneğin, yalnızca Linux üzerinde çalışan bir bash betiği güvenle kullanabilirsiniz #!/bin/bash. Sadece evde kullanılması amaçlanan bir senaryo, ev tercümanı konum sözleşmelerine dayanabilir.

#!/usr/bin/envolumsuz tarafları var. Mutlak bir yol belirtmekten daha esnektir ancak yine de tercüman adını bilmeyi gerektirir. Bazen, içinde bulunmayan bir tercümanı, $PATHörneğin betiğe göre bir konumda çalıştırmak isteyebilirsiniz . Bu gibi durumlarda, genellikle hem standart kabuk hem de istediğiniz tercüman tarafından yorumlanabilen çokgenli bir komut dosyası oluşturabilirsiniz. Örneğin, sistemlere hem taşınabilir bir Python 2 senaryo yapmak için pythonPython 3 ve python2sistemlere Python 2'dir ve burada pythonPython 2 ve python2yok:

#!/bin/sh
''':'
if type python2 >/dev/null 2>/dev/null; then
  exec python2 "$0" "$@"
else
  exec python "$0" "$@"
fi
'''
# real Python script starts here
def …

22

Özellikle perl #!/usr/bin/enviçin, iki nedenden dolayı kullanmak kötü bir fikirdir.

İlk olarak, taşınabilir değil. Bazı karanlık platformlarda, env / usr / bin konumunda değildir. İkincisi, Keith Thompson'ın belirttiği gibi, shebang hattında tartışmaları geçmekte sorun yaşayabilir. Maksimum taşınabilir çözüm şudur:

#!/bin/sh
exec perl -x "$0" "$@"
#!perl

Nasıl çalıştığı hakkında ayrıntılar için, 'perldoc perlrun' ve -x argümanı hakkında söylediklerini görün.


Tam olarak aradığım cevap: perl komutuna perl komutuna izin verilecek perl betiği için "shebang" yazılabilir (örneğin, son satır ek argümanlar kabul ediyor).
AmokHuginnsson

15

İkisi arasında bir ayrım olmasının nedeni, komut dosyalarının nasıl yürütüldüğünden kaynaklanmaktadır.

Kullanılması /usr/bin/env(diğer cevaplarda da belirtildiği gibi /usr/bin, bazı işletim sistemlerinde bulunmuyor ) gereklidir, çünkü sonra çalıştırılabilir bir ad koyamazsınız #!- mutlak bir yol olmalıdır. Bunun nedeni, #!mekanizmanın kabuktan daha düşük bir seviyede çalışmasıdır. Çekirdeğin ikili yükleyicisinin bir parçası. Bu test edilebilir. Bunu bir dosyaya koyun ve çalıştırılabilir olarak işaretleyin:

#!bash

echo 'foo'

Çalıştırmaya çalıştığınızda böyle bir hata yazdırdığını göreceksiniz:

Failed to execute process './test.sh'. Reason:
The file './test.sh' does not exist or could not be executed.

Bir dosya çalıştırılabilir olarak işaretlenmişse ve a ile başlıyorsa #!, çekirdek (ki bunlar $PATHveya şu anki dizini bilmeyenler : bunlar kullanıcı-arazi kavramlarıdır) mutlak bir yol kullanarak bir dosya arayacaktır. Mutlak bir yol kullanmak sorunlu olduğu için (diğer cevaplarda belirtildiği gibi), birileri bir numara buldu: Bir /usr/bin/envşeyi kullanarak çalıştırmak için koşabilirsiniz (neredeyse her zaman bu konumdadır) $PATH.


11

Kullanmanın iki problemi daha var #!/usr/bin/env

  1. Tercümana giden tam yolu belirtme problemini çözmez, sadece ona gider env.

    enviçinde olması veya piton içinde olması garanti edildiğinden /usr/bin/envdaha fazla bashgarantili değildir ./bin/bash/usr/bin/python

  2. env tercümanın adıyla ARGV [0] üzerine yazar (e..g bash veya python).

    Bu, komut dosyanızın adının psçıktısını almasını engeller, örneğin, çıktı (veya nasıl göründüğü / nerede değiştiğini gösterir) ve örneğin;ps -C scriptname.sh

[güncelleme 2016-06-04]

Ve üçüncü bir problem:

  1. PATH’nizi değiştirmek , özellikle bu tür komut dosyaları yazarken önemsiz olan bir komut dosyasının ilk satırını düzenlemekten daha fazla iştir. Örneğin:

    printf "%s\n" 1 i '#!'$(type -P python2) . w | ed foo.py

    Bir dizini $ PATH dizinine eklemek veya önceden beklemek oldukça kolaydır (dosyayı kalıcı hale getirmek için bir dosyayı düzenlemek zorunda olmanıza rağmen - sizin ~/.profileya da her neyse - ve bu dosyayı değiştirmek çok kolay değildir çünkü PATH komut dosyasında herhangi bir yere yerleştirilmiş olabilir. , ilk satırda değil).

    PATH dizinlerinin sırasını değiştirmek önemli ölçüde daha zordur .... ve sadece #!satırı düzenlemekten çok daha zordur .

    Ve hala kullanmanın #!/usr/bin/envsana verdiği diğer tüm problemlerin var .

    @jlliagre #!/usr/bin/env, "yalnızca PATH / PATH sırasını değiştirerek" komut dosyanızı birden fazla tercüman sürümüyle test etmek için yararlı bir yorumda bulundu.

    Bunu yapmanız gerekiyorsa #!, betiğinizin üstünde birkaç satır olması (ilk satırdan başka herhangi bir yerde yorum yaparlar ) ve şimdi kullanmak istediğiniz satırın kesip / kopyalayıp yapıştırın ilk satır

    Bu vi, imleci #!istediğiniz satıra taşımak , sonra dd1GPda veya yazmak kadar basit olacaktır Y1GP. En önemlisi bir editör olsa bile nano, fareyi kopyalayıp yapıştırmak için kullanmanız birkaç saniye alır.


Genel olarak, kullanmanın avantajları #!/usr/bin/enven az düzeydedir ve kesinlikle dezavantajlara ağır basmaya bile yaklaşmaz. “Rahatlık” avantajı bile büyük ölçüde aldatıcıdır.

IMO, işletim sistemlerinin çalışılması gereken bir şey olmadığını, etrafta çalışılması (ya da en iyi şekilde göz ardı edilmesi) bir sorun olduğunu düşünen belirli bir programcı tarafından teşvik edilen saçma bir fikirdir .

Not: İşte bir kerede birden fazla dosya yorumlayıcısı değiştirmek için basit bir komut dosyası.

change-shebang.sh:

#!/bin/bash

interpreter="$1"
shift

if [ -z "$(type -P $interpreter)" ] ; then
  echo "Error: '$interpreter' is not executable." >&2
  exit 1
fi

if [ ! -d "$interpreter" ] && [ -x "$interpreter" ] ; then
  shebang='#!'"$(realpath -e $interpreter)" || exit 1
else
  shebang='#!'"$(type -P $interpreter)"
fi

for f in "$@" ; do
  printf "%s\n" 1 i "$shebang" . w | ed "$f"
done

Örneğin, change-shebang.sh python2.7 *.pyveyachange-shebang.sh $HOME/bin/my-experimental-ruby *.rb


4
Buradaki hiç kimse ARGV [0] probleminden bahsetmedi. Ve hiç kimse / path / to / env sorununu, onu kullanmak için argümanlardan birini doğrudan ele alan bir formda bahsetmedi (yani, bash veya perl beklenmeyen bir konumda olabilir).
cas

2
Tercüman yolu sorunu, sembolik bağlantıları olan bir sysadmin veya komut dosyasını düzenleyen kullanıcı tarafından kolayca çözülür. İnsanları, burada ve diğer sorularda belirtilen shebang hattında env kullanmanın neden olduğu diğer tüm konulara ayak uydurmaya cesaretlendirmek kesinlikle önemli bir sorun değildir. Bu, kötü bir çözümü de aynı şekilde cshkodlamayı teşvik etmenin kötü bir çözümü teşvik etmesini teşvik ediyor : bu tür işler ancak daha iyi alternatifler var.
kas

3
sysadmins olmayanlar, sysadmin'den bunu yapmasını isteyebilir. veya sadece betiği düzenleyebilir ve #!satırı değiştirebilirler .
cas

2
Bu tam olarak env'nin kaçınmasına yardımcı olduğu şeydir: python ya da her neyse alakasız bir sysadmin veya işletim sistemine özgü teknik bilgiye bağlı olarak.
jlliagre

1
Keşke bunu bin kez daha fazla oylayabilseydim, çünkü her iki şekilde de gerçekten iyi bir cevap - biri kullanırsa /usr/bin/envve yerel olarak kurtulmam gerekiyorsa veya kullanmamışlarsa ve eklemem gerekiyorsa. Her iki durum da ortaya çıkabilir ve bu nedenle bu cevapta sağlanan komut dosyaları, araç kutusunda bulunması muhtemel bir araçtır.
jstine

8

Buraya başka bir örnek eklemek:

Kullanılması envBirden arasındaki komut dosyalarını paylaşmak istediğinizde de yararlıdır rvmörneğin ortamlar.

Bunu cmd satırında çalıştırmak, #!/usr/bin/env rubybir betiğin içinde kullanıldığında hangi yakut versiyonunun kullanılacağını gösterir :

env ruby --version

Bu nedenle, kullandığınızda env, komut dosyalarınızı değiştirmeden farklı ruby ​​versiyonlarını rvm ile kullanabilirsiniz.


1
//, Mükemmel fikir. Birden tercüman tüm amacı kodunu kırmak için, veya kod bağlıdır olması değil o belirli tercüman .
Nathan Basanese,

4

Yalnızca kendiniz veya işiniz için yazıyorsanız ve aradığınız tercümanın konumu her zaman aynı yerdedir, elbette doğrudan yolu kullanın. Diğer tüm durumlarda kullanın #!/usr/bin/env.

İşte nedeni: Sizin durumunuzda pythontercüman, hangi sözdizimini kullandığınızdan bağımsız olarak aynı yerde fakat birçok kişi için farklı bir yere kurmuş olabilir. Çoğu büyük programlama tercümanı /usr/bin/birçok yeni yazılımda bulunsa da , varsayılan ayardır /usr/local/bin/.

Her zaman kullanacağımı bile iddia ediyorum, #!/usr/bin/envçünkü aynı tercümanın birden fazla versiyonunun kurulu olması ve kabuğunuzun varsayılan olarak ne yapması gerektiğini bilmiyorsanız, muhtemelen bunu düzeltmelisiniz.


5
"Diğer tüm durumlarda #! / Usr / bin / env kullanın" çok güçlü. // @KeithThompson ve diğerlerinin işaret ettiği gibi, #! / Usr / bin / env, betiğin kime / nasıl çalıştığına bağlı olarak farklı davranabileceği anlamına gelir. Bazen bu farklı davranış bir güvenlik hatası olabilir. // Örneğin, bir setuid veya setgid betiği için ASLA "#! / Usr / bin / env python" kullanmayın, çünkü betiği çağıran kullanıcı kötü amaçlı yazılımı PATH içindeki "python" adlı bir dosyaya yerleştirebilir. Setuid / gid komut dosyalarının iyi bir fikir olup olmadığı farklı bir konudur - ancak kesinlikle, hiçbir setuid / gid çalıştırılabilir programının kullanıcı tarafından sağlanan ortama güvenmemesi gerekir.
Krazy Glew,

@KrazyGlew, Linux'ta bir script ayarlayamazsınız. Çalıştırılabilir bir şey yap. Ve bu yürütülebilir dosyayı yazarken, ortam değişkenlerini temizlemek iyi bir uygulamadır ve yaygın olarak yapılır. Bazı çevre değişkeni de bilerek yoksayılır .
MayeulC

3

Taşınabilirlik ve uyumluluk nedenlerden dolayı kullanmak daha iyidir

#!/usr/bin/env bash

onun yerine

#!/usr/bin/bash

Bir Linux / Unix sisteminde bir çiftliğin bulunabileceği çoklu olasılıklar vardır. Dosya sistemi hiyerarşisinin ayrıntılı açıklaması için hier (7) kılavuz sayfasına bakın.

Örneğin FreeBSD, temel sistemin bir parçası olmayan tüm yazılımı / usr / local / dizinine yükler . Bash, temel sistemin bir parçası olmadığından bash ikili / usr / local / bin / bash dizinine kurulur .

Çoğu Linux Dağıtımı ve FreeBSD altında çalışan bir bash / csh / perl / ne olursa olsun komut dosyası istediğinizde, #! / Usr / bin / env .

Ayrıca, Linux kurulumlarının çoğunun (hard) env binaryini / bin / env ya da softlinked / usr / bin içine / bin içine bağladığını not edin. Yani #! / Bin / env kullanmayın .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.