Dường như với tôi như các tập tin chạy giống nhau mà không có dòng đó.
Dường như với tôi như các tập tin chạy giống nhau mà không có dòng đó.
Câu trả lời:
Nếu bạn đã cài đặt một số phiên bản Python, /usr/bin/env
sẽ đảm bảo trình thông dịch được sử dụng là phiên bản đầu tiên trên môi trường của bạn $PATH
. Thay thế sẽ là mã hóa một cái gì đó như #!/usr/bin/python
; Điều đó ổn, nhưng kém linh hoạt.
Trong Unix, một tệp thực thi có nghĩa là được giải thích có thể chỉ ra trình thông dịch nào sẽ sử dụng bằng cách #!
bắt đầu dòng đầu tiên, theo sau là trình thông dịch (và bất kỳ cờ nào có thể cần).
Tất nhiên, nếu bạn đang nói về các nền tảng khác, quy tắc này không áp dụng (nhưng "dòng shebang" đó không có hại, và sẽ hữu ích nếu bạn từng sao chép tập lệnh đó sang nền tảng có nền tảng Unix, như Linux, Mac , Vân vân).
chmod +x myscript.py
) và sau đó chạy trực tiếp: ./myscript.py
thay vì chỉ python myscript.py
.
env
mang lại sự linh hoạt tối đa trong đó người dùng có thể chọn trình thông dịch để sử dụng bằng cách thay đổi PATH. Thường thì sự linh hoạt này là không bắt buộc và nhược điểm là linux chẳng hạn không thể sử dụng tên tập lệnh cho tên của quá trình trong ps
và hoàn nguyên thành "python". Khi đóng gói các ứng dụng python cho các bản phân phối chẳng hạn, tôi sẽ khuyên không nên sử dụng env
.
py
launcher có thể sử dụng dòng shebang trên Windows. Nó được bao gồm trong Python 3.3 hoặc nó có thể được cài đặt độc lập .
/usr/bin/env: Key has expired
sau nhiều giờ.
Đó được gọi là dòng shebang . Như mục Wikipedia giải thích :
Trong điện toán, một shebang (còn được gọi là hashbang, băm, pound bang hoặc crunchbang) đề cập đến các ký tự "#!" khi chúng là hai ký tự đầu tiên trong một lệnh phiên dịch là dòng đầu tiên của tệp văn bản. Trong một hệ điều hành giống Unix, trình tải chương trình lấy sự hiện diện của hai ký tự này làm dấu hiệu cho thấy tệp là tập lệnh và cố gắng thực thi tập lệnh đó bằng trình thông dịch được chỉ định bởi phần còn lại của dòng đầu tiên trong tệp.
Xem thêm mục Câu hỏi thường gặp về Unix .
Ngay cả trên Windows, nơi dòng shebang không xác định trình thông dịch sẽ được chạy, bạn có thể chuyển các tùy chọn cho trình thông dịch bằng cách chỉ định chúng trên dòng shebang. Tôi thấy hữu ích khi giữ một dòng shebang chung trong các tập lệnh một lần (chẳng hạn như tập lệnh tôi viết khi trả lời câu hỏi trên SO), vì vậy tôi có thể nhanh chóng kiểm tra chúng trên cả Windows và ArchLinux .
Các tiện ích env cho phép bạn gọi một lệnh trên con đường:
Đối số còn lại đầu tiên chỉ định tên chương trình cần gọi; nó được tìm kiếm theo
PATH
biến môi trường. Bất kỳ đối số còn lại được truyền dưới dạng đối số cho chương trình đó.
Mở rộng một chút về các câu trả lời khác, đây là một ví dụ nhỏ về cách các tập lệnh dòng lệnh của bạn có thể gặp rắc rối bằng cách sử dụng các /usr/bin/env
dòng shebang một cách vô thức:
$ /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
Mô-đun json không tồn tại trong Python 2.5.
Một cách để bảo vệ chống lại loại vấn đề đó là sử dụng các tên lệnh python được phiên bản thường được cài đặt với hầu hết các Pythons:
$ cat my_script.py
#!/usr/bin/env python2.6
import json
print "hello, json"
Nếu bạn chỉ cần phân biệt giữa Python 2.x và Python 3.x, các bản phát hành gần đây của Python 3 cũng cung cấp một python3
tên:
$ cat my_script.py
#!/usr/bin/env python3
import json
print("hello, json")
which python
trả về /usr/bin/python
, một đường dẫn thư mục cục bộ có thể được mã hóa cứng : #!/usr/bin/python
. Nhưng đó là ít linh hoạt hơn so với #!/usr/bin/env python
có một ứng dụng toàn cầu.
Để chạy tập lệnh python, chúng ta cần nói với shell ba điều:
Shebang #!
hoàn thành (1.). Các shebang bắt đầu bằng một #
vì #
nhân vật là một điểm đánh dấu nhận xét trong nhiều ngôn ngữ kịch bản. Do đó, nội dung của dòng shebang sẽ bị người phiên dịch tự động bỏ qua.
Các env
hoàn thành lệnh (2) và (3). Để trích dẫn "sự tham lam"
Một cách sử dụng phổ biến của
env
lệnh là để khởi chạy các trình thông dịch, bằng cách sử dụng thực tế là env sẽ tìm kiếm $ PATH cho lệnh mà nó được yêu cầu khởi chạy. Vì dòng shebang yêu cầu một đường dẫn tuyệt đối được chỉ định và vì vị trí của các trình thông dịch khác nhau (perl, bash, python) có thể thay đổi rất nhiều, nên thường được sử dụng:
#!/usr/bin/env perl
thay vì cố gắng đoán xem đó là / bin / perl, / usr / bin / perl, / usr / local / bin / perl, / usr / local / pkg / perl, / fileserver / usr / bin / perl hay / home / MrDaniel / usr / bin / perl trên hệ thống của người dùng ...Mặt khác, env hầu như luôn nằm trong / usr / bin / env. (Trừ những trường hợp không có; một số hệ thống có thể sử dụng / bin / env, nhưng đó là một sự cố khá hiếm gặp và chỉ xảy ra trên các hệ thống không phải là Linux.)
Có lẽ câu hỏi của bạn theo nghĩa này:
Nếu bạn muốn sử dụng: $python myscript.py
Bạn không cần dòng đó cả. Hệ thống sẽ gọi python và sau đó trình thông dịch python sẽ chạy tập lệnh của bạn.
Nhưng nếu bạn có ý định sử dụng: $./myscript.py
Gọi nó trực tiếp như một chương trình bình thường hoặc tập lệnh bash, bạn cần viết dòng đó để chỉ định cho hệ thống mà chương trình sử dụng để chạy nó, và cũng có thể thực hiện được với nó chmod 755
)
Cuộc exec
gọi hệ thống của nhân Linux hiểu rõ shebangs ( #!
)
Khi bạn làm trên bash:
./something
trên Linux, điều này gọi cuộc gọi exec
hệ thống với đường dẫn ./something
.
Dòng này của kernel được gọi trên tệp được chuyển đến exec
: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
Nó đọc các byte đầu tiên của tệp và so sánh chúng với #!
.
Nếu so sánh là đúng, thì phần còn lại của dòng được phân tích cú pháp bởi nhân Linux, điều này thực hiện một exec
cuộc gọi khác với đường dẫn /usr/bin/env python
và tệp hiện tại làm đối số đầu tiên:
/usr/bin/env python /path/to/script.py
và điều này hoạt động cho bất kỳ ngôn ngữ kịch bản sử dụng #
như một nhân vật bình luận.
Và vâng, bạn có thể tạo một vòng lặp vô hạn với:
printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a
Bash nhận ra lỗi:
-bash: /a: /a: bad interpreter: Too many levels of symbolic links
#!
tình cờ có thể đọc được, nhưng điều đó là không bắt buộc.
Nếu tệp bắt đầu với các byte khác nhau, thì lệnh exec
gọi hệ thống sẽ sử dụng một trình xử lý khác. Trình xử lý tích hợp quan trọng nhất khác dành cho các tệp thực thi ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 để kiểm tra byte 7f 45 4c 46
(cũng có thể là con người có thể đọc được .ELF
). Hãy xác nhận rằng bằng cách đọc 4 byte đầu tiên /bin/ls
, đây là tệp thực thi ELF:
head -c 4 "$(which ls)" | hd
đầu ra:
00000000 7f 45 4c 46 |.ELF|
00000004
Vì vậy, khi kernel nhìn thấy các byte đó, nó sẽ lấy tệp ELF, đặt nó vào bộ nhớ một cách chính xác và bắt đầu một quy trình mới với nó. Xem thêm: Làm thế nào để kernel có được một tệp nhị phân thực thi chạy trong linux?
Cuối cùng, bạn có thể thêm trình xử lý shebang của riêng bạn với binfmt_misc
cơ chế. Ví dụ: bạn có thể thêm một trình xử lý tùy chỉnh cho .jar
các tệp . Cơ chế này thậm chí hỗ trợ xử lý bằng cách mở rộng tập tin. Một ứng dụng khác là chạy trong suốt các tệp thực thi của một kiến trúc khác với QEMU .
Tôi không nghĩ rằng POSIX quy định cụ thể shebangs tuy nhiên: https://unix.stackexchange.com/a/346214/32558 , mặc dù nó không đề cập đến ở trên phần lý do, và theo hình thức "nếu kịch bản thực thi được hỗ trợ bởi một cái gì đó hệ thống có thể xảy ra ". macOS và FreeBSD dường như cũng thực hiện nó.
PATH
động lực tìm kiếm
Có khả năng, một động lực lớn cho sự tồn tại của shebang là thực tế là trong Linux, chúng ta thường muốn chạy các lệnh từ PATH
:
basename-of-command
thay vì:
/full/path/to/basename-of-command
Nhưng sau đó, không có cơ chế shebang, Linux sẽ biết cách khởi chạy từng loại tệp như thế nào?
Mã hóa phần mở rộng trong các lệnh:
basename-of-command.py
hoặc thực hiện tìm kiếm PATH trên mỗi trình thông dịch:
python basename-of-command
sẽ là một khả năng, nhưng điều này có một vấn đề lớn là mọi thứ sẽ bị phá vỡ nếu chúng ta quyết định cấu trúc lại lệnh thành ngôn ngữ khác.
Shebang giải quyết vấn đề này đẹp.
Về mặt kỹ thuật, trong Python, đây chỉ là một dòng bình luận.
Dòng này chỉ được sử dụng nếu bạn chạy tập lệnh py từ shell (từ dòng lệnh). Điều này được gọi là " Shebang !" và nó được sử dụng trong các tình huống khác nhau, không chỉ với các tập lệnh Python.
Ở đây, nó chỉ thị shell để bắt đầu một phiên bản Python cụ thể (chăm sóc phần còn lại của tệp.
py.exe
. Đây là một phần của bản cài đặt Python tiêu chuẩn.
Lý do chính để làm điều này là để làm cho tập lệnh di động trên các môi trường hệ điều hành.
Ví dụ, dưới mingw, tập lệnh python sử dụng:
#!/c/python3k/python
và theo phân phối GNU / Linux, nó là:
#!/usr/local/bin/python
hoặc là
#!/usr/bin/python
và theo hệ thống sw / hw Unix thương mại tốt nhất trong tất cả (OS / X), đó là:
#!/Applications/MacPython 2.5/python
hoặc trên FreeBSD:
#!/usr/local/bin/python
Tuy nhiên, tất cả những khác biệt này có thể làm cho tập lệnh di động trên tất cả bằng cách sử dụng:
#!/usr/bin/env python
/usr/bin/python
. Trong Linux, Python được cài đặt bởi hệ thống cũng gần như chắc chắn /usr/bin/python
(tôi chưa bao giờ thấy bất cứ điều gì khác và nó sẽ không có ý nghĩa). Lưu ý rằng có thể có các hệ thống không có /usr/bin/env
.
python
không phải là di động, đó là trình thông dịch Python mặc định phân phối. Arch Linux mặc định cho Python 3 trong một thời gian dài và có thể các bản phân phối cũng nghĩ về nó vì Python 2 chỉ được hỗ trợ cho đến năm 2020.
Có lẽ có ý nghĩa để nhấn mạnh một điều mà hầu hết đã bỏ lỡ, có thể ngăn chặn sự hiểu biết ngay lập tức. Khi bạn nhập python
vào thiết bị đầu cuối, bạn thường không cung cấp một đường dẫn đầy đủ. Thay vào đó, tệp thực thi được xem xét trong PATH
biến môi trường. Đổi lại, khi bạn muốn trực tiếp thực hiện chương trình Python /path/to/app.py
, người ta phải nói với trình bao sử dụng trình thông dịch nào (thông qua hàm băm , những gì các cộng tác viên khác đang giải thích ở trên).
Hashbang mong đợi đường dẫn đầy đủ đến một thông dịch viên. Do đó, để chạy chương trình Python của bạn trực tiếp, bạn phải cung cấp đường dẫn đầy đủ đến tệp nhị phân Python khác nhau đáng kể, đặc biệt là xem xét việc sử dụng virtualenv . Để giải quyết tính di động, thủ thuật với /usr/bin/env
được sử dụng. Cái sau ban đầu được dự định để thay đổi môi trường tại chỗ và chạy một lệnh trong đó. Khi không có sự thay đổi nào được cung cấp, nó sẽ chạy lệnh trong môi trường hiện tại, điều này dẫn đến hiệu quả trong quá trình PATH
tra cứu tương tự .
Đây là một quy ước shell cho shell biết chương trình nào có thể thực thi tập lệnh.
#! / usr / bin / env trăn
giải quyết một đường dẫn đến nhị phân Python.
Đó là cách được đề xuất, đề xuất trong tài liệu:
2.2.2. Tập lệnh Python có thể thực thi
Trên các hệ thống Unix của BSD, các tập lệnh Python có thể được thực hiện trực tiếp, như tập lệnh shell, bằng cách đặt dòng
#! /usr/bin/env python3.2
từ http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts
Bạn có thể thử vấn đề này bằng virtualenv
Đây là test.txt
#! /usr/bin/env python
import sys
print(sys.version)
Tạo môi trường ảo
virtualenv test2.6 -p /usr/bin/python2.6
virtualenv test2.7 -p /usr/bin/python2.7
kích hoạt từng môi trường sau đó kiểm tra sự khác biệt
echo $PATH
./test.py
Nó chỉ xác định những gì trình thông dịch bạn muốn sử dụng. Để hiểu điều này, hãy tạo một tệp thông qua thiết bị đầu cuối bằng cách thực hiện touch test.py
, sau đó nhập vào tệp đó như sau:
#!/usr/bin/env python3
print "test"
và làm chmod +x test.py
để làm cho kịch bản của bạn thực thi. Sau này khi bạn làm, ./test.py
bạn sẽ nhận được một thông báo lỗi:
File "./test.py", line 2
print "test"
^
SyntaxError: Missing parentheses in call to 'print'
bởi vì python3 không hỗ trợ toán tử in.
Bây giờ hãy tiếp tục và thay đổi dòng đầu tiên của mã của bạn thành:
#!/usr/bin/env python2
và nó sẽ hoạt động, in test
ra thiết bị xuất chuẩn, vì python2 hỗ trợ toán tử in. Vì vậy, bây giờ bạn đã học cách chuyển đổi giữa các trình thông dịch tập lệnh.
Dường như với tôi như các tập tin chạy giống nhau mà không có dòng đó.
Nếu vậy, có lẽ bạn đang chạy chương trình Python trên Windows? Thay vào đó, Windows không sử dụng dòng đó, nó sử dụng phần mở rộng tên tệp để chạy chương trình được liên kết với phần mở rộng tệp.
Tuy nhiên vào năm 2011, một "trình khởi chạy Python" đã được phát triển (ở một mức độ nào đó) bắt chước hành vi Linux này cho Windows. Điều này chỉ giới hạn trong việc chọn trình thông dịch Python nào được chạy - ví dụ: để chọn giữa Python 2 và Python 3 trên hệ thống có cài đặt cả hai. Trình khởi chạy được cài đặt tùy chọn như cài đặt py.exe
Python và có thể được liên kết với .py
các tệp để trình khởi chạy sẽ kiểm tra dòng đó và lần lượt khởi chạy phiên bản trình thông dịch Python được chỉ định.
$ python myscript.py
.
Điều này có nghĩa là nhiều thông tin lịch sử hơn là một câu trả lời "thực sự".
Hãy nhớ rằng trở lại trong ngày bạn đã LOTS của unix như hệ điều hành mà các nhà thiết kế đều có quan niệm riêng của họ về nơi để đặt các công cụ, và đôi khi không bao gồm Python, Perl, Bash, hoặc nhiều GNU khác / Open Source thứ ở tất cả .
Điều này thậm chí đúng với các bản phân phối Linux khác nhau. Trên Linux - trước FHS [1] -bạn có thể có python trong / usr / bin / hoặc / usr / local / bin /. Hoặc nó có thể chưa được cài đặt, vì vậy bạn đã tự xây dựng và đặt nó vào ~ / bin
Solaris là điều tồi tệ nhất tôi từng làm, một phần là sự chuyển đổi từ Berkeley Unix sang System V. Bạn có thể kết thúc với những thứ trong / usr /, / usr / local /, / usr / ucb, / opt / v.v. cho một số con đường thực sự dài. Tôi có ký ức về những thứ từ Sunfreeware.com cài đặt từng gói trong thư mục riêng của nó, nhưng tôi không thể nhớ lại nếu nó liên kết các nhị phân vào / usr / bin hay không.
Ồ, và đôi khi / usr / bin đã ở trên máy chủ NFS [2].
Vì vậy, các env
tiện ích đã được phát triển để làm việc xung quanh này.
Sau đó, bạn có thể viết #!/bin/env interpreter
và miễn là con đường là những thứ thích hợp có cơ hội chạy hợp lý . Tất nhiên, có nghĩa là hợp lý (đối với Python và Perl) mà bạn cũng đã đặt các biến môi trường phù hợp. Đối với bash / ksh / zsh, nó chỉ hoạt động.
Điều này rất quan trọng vì mọi người đã chuyển qua các tập lệnh shell (như perl và python) và nếu bạn mã hóa cứng / usr / bin / python trên máy trạm Red Hat Linux của bạn thì nó sẽ bị hỏng trên SGI ... tốt, không , Tôi nghĩ IRIX đặt trăn vào đúng chỗ. Nhưng trên một trạm Sparc, nó có thể không chạy.
Tôi nhớ trạm sparc của tôi. Nhưng không nhiều. Ok, bây giờ bạn đã khiến tôi trolling trên E-Bay. Khốn kiếp.
[1] Tiêu chuẩn phân cấp hệ thống tệp. https://en.wikipedia.org/wiki/Filesystem_HVELy_St Chuẩn
[2] Có, và đôi khi mọi người vẫn làm những thứ như vậy. Và không, tôi đã không đeo củ cải HOẶC hành tây trên thắt lưng của tôi.
Nếu bạn đang chạy tập lệnh của mình trong môi trường ảo, giả sử venv
, khi thực thi which python
trong khi làm việc venv
sẽ hiển thị đường dẫn đến trình thông dịch Python:
~/Envs/venv/bin/python
Lưu ý rằng tên của môi trường ảo được nhúng trong đường dẫn đến trình thông dịch Python. Do đó, mã hóa đường dẫn này trong tập lệnh của bạn sẽ gây ra hai vấn đề:
Do đó, để thêm vào câu trả lời của Jonathan , shebang lý tưởng là #!/usr/bin/env python
, không chỉ về tính di động trên các hệ điều hành mà còn cho tính di động trên các môi trường ảo!
Xem xét các vấn đề về tính di động giữa python2
và python3
, bạn phải luôn chỉ định một trong hai phiên bản trừ khi chương trình của bạn tương thích với cả hai.
Một số các bản phân phối được vận chuyển python
symlinked để python3
trong một thời gian bây giờ - không dựa vào python
việc python2
.
Điều này được nhấn mạnh bởi PEP 394 :
Để chấp nhận sự khác biệt giữa các nền tảng, tất cả các mã mới cần gọi trình thông dịch Python không nên chỉ định python, mà nên chỉ định python2 hoặc python3 (hoặc các phiên bản python2.x và python3.x cụ thể hơn; xem Ghi chú di chuyển ) . Sự khác biệt này phải được thực hiện trong shebang, khi gọi từ tập lệnh shell, khi gọi qua lệnh gọi hệ thống () hoặc khi gọi trong bất kỳ bối cảnh nào khác.
Nó cho phép bạn chọn tệp thực thi mà bạn muốn sử dụng; Điều này rất tiện lợi nếu có lẽ bạn có nhiều cài đặt python và các mô-đun khác nhau trong mỗi mô-đun và muốn chọn. ví dụ
#!/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())
Điều này cho biết kịch bản nơi thư mục python!
#! /usr/bin/env python
#!/usr/bin/env python
ở trên cùng.