Bana öyle geliyor ki dosyalar bu satır olmadan aynı şekilde çalışıyor.
Bana öyle geliyor ki dosyalar bu satır olmadan aynı şekilde çalışıyor.
Yanıtlar:
Python'un birkaç sürümü yüklüyse, /usr/bin/env
kullanılan yorumlayıcının ortamınızdaki ilk yorumlayıcı olmasını sağlar $PATH
. Alternatif olarak #!/usr/bin/python
; tamam ama daha az esnek.
Unix'te, yorumlanması gereken yürütülebilir bir dosya #!
, ilk satırın başında bir ve ardından yorumlayıcı (ve ihtiyaç duyabileceği herhangi bir bayrak) ile hangi yorumlayıcının kullanılacağını gösterebilir.
Eğer diğer platformlarda bahsediyorsan, tabii ki, bu kural geçerli değildir (ama bu "mesele çizgi" Zarar yapar ve hiç bir platforma o senaryoyu kopyalarsanız yardımcı olacaktır ile Linux, Mac gibi Unix tabanında, , vb).
chmod +x myscript.py
) yaparak ve daha sonra doğrudan çalıştırarak çalıştırdığınızda geçerlidir: ./myscript.py
sadece python myscript.py
.
env
kullanıcı PATH değiştirerek kullanımına tercüman seçebilmesi ile maksimum esneklik sağlar. Genellikle bu esneklik gerekli değildir ve dezavantajı, linux'un işlemin adı için komut dosyası adını kullanamaması ve ps
"python" a geri dönmesidir. Dağıtımlar için python uygulamalarını paketlerken kullanmamanızı tavsiye ederim env
.
py
başlatıcısı Windows'taki shebang hattını kullanabilir. Python 3.3'e dahildir veya bağımsız olarak kurulabilir .
/usr/bin/env: Key has expired
saatler geçtikten sonra mesajla ölen süreçler yaşadım .
Buna shebang hattı denir . As Vikipedi girişi açıklıyor :
Bilgi işlemde, bir karmaşa (hashbang, hashpling, pound bang veya crunchbang olarak da bilinir) karakterleri "#!" bir metin dosyasının ilk satırı olarak bir tercüman direktifindeki ilk iki karakter olduklarında. Unix benzeri bir işletim sisteminde, program yükleyici bu iki karakterin varlığını dosyanın bir komut dosyası olduğunu gösterir ve dosyadaki ilk satırın geri kalanında belirtilen yorumlayıcıyı kullanarak bu komut dosyasını yürütmeye çalışır.
Ayrıca bkz. Unix SSS girişi .
Shebang hattının çalıştırılacak yorumlayıcıyı belirlemediği Windows'da bile, seçenekleri shebang satırında belirterek tercümana iletebilirsiniz. Bir kerelik komut dosyalarında (SO ile ilgili soruları cevaplarken yazdıklarım gibi) genel bir shebang satırını tutmayı yararlı buluyorum, böylece bunları hem Windows hem de ArchLinux'da hızlı bir şekilde test edebilirim .
Env yarar Eğer yolda bir komut çağırmak için izin verir:
Kalan ilk bağımsız değişken çağrılacak program adını belirtir;
PATH
ortam değişkenine göre aranır . Kalan bağımsız değişkenler bu programa bağımsız değişken olarak iletilir.
Diğer cevaplara biraz genişleyerek, komut satırı komut dosyalarınızın, /usr/bin/env
shebang satırlarının dikkatsizce kullanılmasıyla nasıl sorun yaşayabileceğine dair küçük bir örnek :
$ /usr/local/bin/python -V
Python 2.6.4
$ /usr/bin/python -V
Python 2.5.1
$ cat my_script.py
#!/usr/bin/env python
import json
print "hello, json"
$ PATH=/usr/local/bin:/usr/bin
$ ./my_script.py
hello, json
$ PATH=/usr/bin:/usr/local/bin
$ ./my_script.py
Traceback (most recent call last):
File "./my_script.py", line 2, in <module>
import json
ImportError: No module named json
Json modülü Python 2.5'te mevcut değildir.
Bu tür bir soruna karşı korunmanın bir yolu, genellikle çoğu Python ile yüklenmiş olan sürümlendirilmiş python komut adlarını kullanmaktır:
$ cat my_script.py
#!/usr/bin/env python2.6
import json
print "hello, json"
Python 2.x ve Python 3.x arasında ayrım yapmanız gerekiyorsa, Python 3'ün son sürümleri de bir python3
ad sağlar:
$ cat my_script.py
#!/usr/bin/env python3
import json
print("hello, json")
which python
getiri /usr/bin/python
, yerel bir dizin yolu sabit kodlanmış olabilir: #!/usr/bin/python
. Ancak bu, #!/usr/bin/env python
küresel bir uygulamaya sahip olandan daha az esnektir .
Python betiğini çalıştırmak için kabuğa üç şey söylememiz gerekir:
Mesele #!
başarır (1.). Mesele ile başlar, #
çünkü #
karakter birçok kodlama dilinde bir yorum işaretleyicisidir. Mesele hattının içeriği bu nedenle tercüman tarafından otomatik olarak göz ardı edilir.
env
Komut gerçekleştirir (2) ve (3). "Yerçekimi"
env
Komutun yaygın bir kullanımı, env'nin başlatması söylenen komut için $ PATH'ı arayacağı gerçeğinden faydalanarak yorumlayıcıları başlatmaktır. Mesele hattının belirtilmesi için mutlak bir yol gerektiğinden ve çeşitli tercümanların (perl, bash, python) yeri çok değişebileceğinden, kullanımı yaygındır:
#!/usr/bin/env perl
/ bin / perl, / usr / bin / perl, / usr / local / bin / perl, / usr / local / pkg / perl, / fileserver / usr / bin / perl veya / home olup olmadığını tahmin etmeye çalışmak yerine / MrDaniel / usr / bin / perl kullanıcı sisteminde ...Öte yandan, env neredeyse her zaman / usr / bin / env'de bulunur. (Olmadığı durumlar dışında; bazı sistemler / bin / env kullanabilir, ancak bu oldukça nadir bir durumdur ve yalnızca Linux dışı sistemlerde gerçekleşir.)
Belki de sorunuz bu anlamda:
Kullanmak istiyorsanız: $python myscript.py
O çizgiye hiç ihtiyacınız yok. Sistem python'u arayacak ve sonra python yorumlayıcısı betiğinizi çalıştıracaktır.
Ancak kullanmayı düşünüyorsanız: $./myscript.py
Doğrudan normal bir program veya bash betiği gibi çağırmak için, bu satırı çalıştırmak için hangi programın kullanılacağını sisteme belirtmek için bu satırı yazmanız gerekir (ve ayrıca çalıştırılabilir hale getirin chmod 755
)
exec
Linux çekirdeğinin sistem çağrısı (shebangs anlar #!
yerel)
Bash yaparken:
./something
Linux'ta bu exec
sistem çağrısını yolla çağırır ./something
.
Çekirdeğin bu satırı şu dosyaya aktarılır exec
: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
Dosyanın ilk baytını okur ve bunları karşılaştırır #!
.
Karşılaştırma doğruysa, satırın geri kalanı Linux argümanı tarafından ayrıştırılır, bu da ilk argüman olarak exec
yol /usr/bin/env python
ve geçerli dosya ile başka bir çağrı yapar :
/usr/bin/env python /path/to/script.py
ve bu #
, yorum karakteri olarak kullanılan herhangi bir komut dosyası dili için işe yarar .
Ve evet, aşağıdakilerle sonsuz bir döngü yapabilirsiniz:
printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a
Bash hatayı tanır:
-bash: /a: /a: bad interpreter: Too many levels of symbolic links
#!
sadece insan tarafından okunabilir oluyor, ama bu gerekli değil
Dosya farklı baytlarla başlamışsa, exec
sistem çağrısı farklı bir işleyici kullanır. Diğer en önemli yerleşik işleyici, ELF yürütülebilir dosyaları içindir: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 baytları kontrol eder 7f 45 4c 46
(ki bu da insan olur) için okunabilir .ELF
). /bin/ls
Bir ELF yürütülebilir dosyası olan ilk 4 baytı okuyarak onaylayalım :
head -c 4 "$(which ls)" | hd
çıktı:
00000000 7f 45 4c 46 |.ELF|
00000004
Çekirdek bu baytları gördüğünde, ELF dosyasını alır, doğru belleğe koyar ve onunla yeni bir işlem başlatır. Ayrıca bakınız: Çekirdek Linux altında çalışan yürütülebilir bir ikili dosyayı nasıl alır?
Son olarak, binfmt_misc
mekanizma ile kendi shebang işleyicileri ekleyebilirsiniz . Örneğin, dosyalar için özel bir işleyici.jar
ekleyebilirsiniz . Bu mekanizma dosya uzantısına göre işleyicileri bile destekler. Başka bir uygulama, QEMU ile farklı bir mimarinin yürütülebilir dosyalarını şeffaf bir şekilde çalıştırmaktır .
Sanmıyorum POSIX ancak belirtir shebangs: https://unix.stackexchange.com/a/346214/32558 o mantık bölümlerde de söz yapar ve formda "her ne kadar yürütülebilir komut sistemi şey tarafından destekleniyorsa, may olmak". macOS ve FreeBSD de bunu uyguluyor gibi görünüyor.
PATH
arama motivasyonu
Muhtemelen, shebang'ların varlığı için büyük bir motivasyon, Linux'ta sıklıkla aşağıdaki PATH
gibi komutları çalıştırmak istiyoruz :
basename-of-command
onun yerine:
/full/path/to/basename-of-command
Ama sonra, shebang mekanizması olmadan, Linux her bir dosyayı nasıl başlatacağını nasıl bilebilir?
Komutlarda uzantıyı kodlama:
basename-of-command.py
veya her tercümana PATH araması uygulamak:
python basename-of-command
bir olasılık olurdu, ancak bu, komutu başka bir dile yeniden düzenlemeye karar verirsek, her şeyin kırılması gibi büyük bir soruna sahiptir.
Shebangs bu sorunu güzelce çözüyor.
Teknik olarak, Python'da bu sadece bir yorum satırıdır.
Bu satır yalnızca py komut dosyasını kabuktan (komut satırından) çalıştırırsanız kullanılır. Bu " Shebang !" Olarak bilinir . ve sadece Python betikleriyle değil, çeşitli durumlarda kullanılır.
Burada, kabuğa Python'un belirli bir sürümünü başlatmasını bildirir (dosyanın geri kalanıyla ilgilenmek için.
py.exe
. Bu, standart bir Python kurulumunun bir parçasıdır.
Bunu yapmanın ana nedeni komut dosyasını işletim sistemi ortamlarında taşınabilir yapmaktır.
Örneğin, mingw altında python komut dosyaları şunları kullanır:
#!/c/python3k/python
ve GNU / Linux dağıtımı altında:
#!/usr/local/bin/python
veya
#!/usr/bin/python
ve en iyi ticari Unix sw / hw sistemi (OS / X) altında:
#!/Applications/MacPython 2.5/python
veya FreeBSD'de:
#!/usr/local/bin/python
Ancak, tüm bu farklılıklar komut dosyasını kullanarak aşağıdakileri kullanarak taşınabilir hale getirebilir:
#!/usr/bin/env python
/usr/bin/python
. Linux altında, sistem tarafından kurulan Python da neredeyse kesinlikle /usr/bin/python
(başka hiçbir şey görmedim ve hiçbir anlam ifade etmeyecekti). Sahip olunmayan sistemler olabileceğini unutmayın /usr/bin/env
.
python
o kadar taşınabilir değil, dağıtım varsayılan Python yorumlayıcısı. Arch Linux varsayılan olarak Python 3'ü uzun süre kullanıyor ve dağıtımlar da düşünüyor olabilir, çünkü Python 2 sadece 2020'ye kadar destekleniyor.
Muhtemelen en çok kaçırmış olan bir şeyi vurgulamak mantıklıdır, bu da anında anlayışı engelleyebilir. python
Terminalde yazdığınızda normalde tam bir yol sağlamazsınız. Bunun yerine, çalıştırılabilir PATH
ortam değişkenine bakılır . Buna karşılık, doğrudan bir Python programı yürütmek istediğinizde /path/to/app.py
, kabuğa hangi yorumlayıcıyı kullanacağını söylemelidir ( hashbang aracılığıyla) , diğer katkıda bulunanların yukarıda açıkladıklarını).
Hashbang bir tercümana giden tam yolu bekler . Python programınızı doğrudan çalıştırmak için, özellikle virtualenv kullanımı göz önüne alındığında, önemli ölçüde değişen Python ikili dosyasına tam yol sağlamanız gerekir . Taşınabilirliği ele almak için hile /usr/bin/env
kullanılır. İkincisi başlangıçta ortamı yerinde değiştirmek ve içinde bir komut çalıştırmak için tasarlanmıştır. Herhangi bir değişiklik yapılmadığında, komutu geçerli ortamda çalıştırır, bu da etkin bir şekilde aynı sonuç verir.PATH
da hile yapan aramaya .
Bu, hangi programın komut dosyasını çalıştırabileceğini söyleyen bir kabuk kuralıdır.
#! / usr / bin / env python
Python ikili yoluna gider.
Belgelerde önerilen önerilen yol:
2.2.2. Yürütülebilir Python Komut Dosyaları
BSD'nin Unix sistemlerinde, Python betikleri kabuk betikleri gibi doğrudan çalıştırılabilir hale getirilebilir.
#! /usr/bin/env python3.2
dan http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts
Virtualenv kullanarak bu sorunu deneyebilirsiniz
İşte test.py
#! /usr/bin/env python
import sys
print(sys.version)
Sanal ortamlar oluşturun
virtualenv test2.6 -p /usr/bin/python2.6
virtualenv test2.7 -p /usr/bin/python2.7
her ortamı etkinleştirin ve farklılıkları kontrol edin
echo $PATH
./test.py
Sadece hangi tercümanı kullanmak istediğinizi belirtir. Bunu anlamak için, terminal aracılığıyla bir dosya oluşturun touch test.py
ve ardından bu dosyaya aşağıdakileri yazın:
#!/usr/bin/env python3
print "test"
ve chmod +x test.py
komut dosyanızı yürütülebilir yapmak için yapın. Bundan sonra yaptığınız zaman ./test.py
bir hata mesajı almalısınız:
File "./test.py", line 2
print "test"
^
SyntaxError: Missing parentheses in call to 'print'
çünkü python3 yazdırma işlecini desteklemez.
Şimdi devam edin ve kodunuzun ilk satırını şu şekilde değiştirin:
#!/usr/bin/env python2
ve test
python2 yazdırma işlecini desteklediğinden, stdout'a yazdırır. Şimdi, senaryo çevirmenleri arasında nasıl geçiş yapılacağını öğrendiniz.
Bana öyle geliyor ki dosyalar bu satır olmadan aynı şekilde çalışıyor.
Öyleyse, belki de Windows'ta Python programını çalıştırıyorsunuz? Windows bu satırı kullanmaz; bunun yerine, dosya uzantısıyla ilişkili programı çalıştırmak için dosya adı uzantısını kullanır.
Ancak 2011 yılında, bir dereceye kadar Windows için bu Linux davranışını taklit eden bir "Python başlatıcısı" geliştirilmiştir. Bu sadece hangi Python yorumlayıcısının çalıştırılacağını seçmekle sınırlıdır - örneğin her ikisinin de kurulu olduğu bir sistemde Python 2 ve Python 3 arasında seçim yapmak. Başlatıcı isteğe bağlı olarak py.exe
Python yüklemesinde olduğu gibi yüklenir .py
ve başlatıcı bu satırı kontrol edecek ve ardından belirtilen Python yorumlayıcı sürümünü başlatacak şekilde dosyalarla ilişkilendirilebilir .
$ python myscript.py
.
Bu, "gerçek" bir cevaptan çok tarihsel bilgi anlamına gelir.
Kimin tasarımcılar tüm eşyalarını koymak için nereye kendi düşünce vardı ve bazen Python, Perl, Bash veya diğer GNU / Açık Kaynak sürü içermiyordu işletim sistemleri gibi unix bir sürü vardı günde onu geri hatırla hiç .
Bu farklı Linux dağıtımları için bile geçerliydi. Linux'ta - FHS öncesi [1] - / usr / bin / veya / usr / local / bin / dizininde pythonunuz olabilir. Veya kurulmamış olabilir, bu yüzden kendinizinkini oluşturup ~ / bin
Solaris, kısmen Berkeley Unix'ten Sistem V'e geçiş olarak üzerinde çalıştığım en kötüydü / usr /, / usr / local /, / usr / ucb, / opt / vb. bazıları için gerçekten uzun yollar. Sunfreeware.com her paket kendi dizininde yükleme şeyler anıları var, ama ben / usr / bin içine ikili dosyaları symlinked olup olmadığını hatırlayamıyorum.
Oh ve bazen / usr / bin bir NFS sunucusundaydı [2].
Böylece, env
yardımcı program bu sorunu çözmek için geliştirilmiştir.
Sonra yazabilirsiniz #!/bin/env interpreter
ve yol uygun olduğu sürece işlerin makul bir şansı vardı . Tabii ki, makul (Python ve Perl için) de uygun çevresel değişkenleri ayarladığınız anlamına geliyordu. Bash / ksh / zsh için işe yaradı.
Bu önemliydi, çünkü insanlar kabuk komut dosyalarından (perl ve python gibi) geçiyorlardı ve Red Hat Linux iş istasyonunuzda sabit kodlanmış / usr / bin / python kullanırsanız, bir SGI'da kötüye gidecekti ... evet, hayır , IRIX'in python'u doğru yere koyduğunu düşünüyorum. Ancak bir Sparc istasyonunda hiç çalışmayabilir.
Sparc istasyonumu özledim. Ama çok değil. Tamam, şimdi beni E-Körfezi'nde gezdiriyorsun. Bastages.
[1] Dosya sistemi Hiyerarşi Standardı. https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard
[2] Evet ve bazen insanlar hala böyle şeyler yapıyorlar. Ve hayır, kemerime şalgam VEYA soğan takmadım.
Betiğinizi sanal bir ortamda venv
çalıştırıyorsanız , diyelim ki which python
üzerinde çalışırken yürütmek venv
Python yorumlayıcısının yolunu görüntüler:
~/Envs/venv/bin/python
Not sanal ortamda adı gömülü olduğu Python yorumlayıcısı yolunda. Bu nedenle, komut dosyanızdaki bu yolu zor kodlamak iki soruna neden olur:
Bu nedenle, Jonathan'ın cevabına eklemek için , ideal #!/usr/bin/env python
işletim sistemi , yalnızca işletim sistemleri arasında taşınabilirlik için değil, sanal ortamlar arasında taşınabilirlik için de!
Arasındaki taşınabilirlik sorunları göz önüne alındığında python2
ve python3
program hem uyumlu olmadıkça, her zaman her iki versiyonunu belirtmelidir.
Bazı dağıtımlar bir süredir python
işaretli olarak python3
gönderiliyor - python
varlığa güvenmeyin python2
.
Bu PEP 394 tarafından vurgulanmaktadır :
Platformlardaki farklılıkları tolere etmek için, Python yorumlayıcısını çağırması gereken tüm yeni kodlar python belirtmemeli, python2 veya python3'ü (veya daha spesifik python2.x ve python3.x sürümlerini belirtmelidir; Geçiş Notları ) . Bu ayrım, bir kabuk betiğinden çağrılırken, system () çağrısı ile çağrılırken veya başka bir bağlamda çağrılırken yapılmalıdır.
Kullanmak istediğiniz yürütülebilir dosyayı seçmenizi sağlar; belki de birden fazla python yüklemeniz ve her birinde farklı modüller varsa ve seçmek istiyorsanız çok kullanışlıdır. Örneğin
#!/bin/sh
#
# Choose the python we need. Explanation:
# a) '''\' translates to \ in shell, and starts a python multi-line string
# b) "" strings are treated as string concat by python, shell ignores them
# c) "true" command ignores its arguments
# c) exit before the ending ''' so the shell reads no further
# d) reset set docstrings to ignore the multiline comment code
#
"true" '''\'
PREFERRED_PYTHON=/Library/Frameworks/Python.framework/Versions/2.7/bin/python
ALTERNATIVE_PYTHON=/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
FALLBACK_PYTHON=python3
if [ -x $PREFERRED_PYTHON ]; then
echo Using preferred python $ALTERNATIVE_PYTHON
exec $PREFERRED_PYTHON "$0" "$@"
elif [ -x $ALTERNATIVE_PYTHON ]; then
echo Using alternative python $ALTERNATIVE_PYTHON
exec $ALTERNATIVE_PYTHON "$0" "$@"
else
echo Using fallback python $FALLBACK_PYTHON
exec python3 "$0" "$@"
fi
exit 127
'''
__doc__ = """What this file does"""
print(__doc__)
import platform
print(platform.python_version())
Bu komut dosyasına python dizininin nerede olduğunu söyler!
#! /usr/bin/env python
#!/usr/bin/env python
üstte çalıştıracak ve görüntüleyecek CGI betiği (.py) .