![](/img/trans.png)
[英]Should I put #! (shebang) in Python scripts, and what form should it take?
[英]What shebang should I use to consistently point to python3?
我有一個使用 shebang #!/usr/bin/env python
的腳本。 它在 Python 3 是唯一可用版本的機器上運行良好,但在同時具有 Python 2 和 Python 3 的機器上,它使用 Python 2 運行腳本。
如果我將 shebang 修改為#!/usr/bin/env python3
,它將在具有 Python 2 和 Python 3 的機器上工作,但在只有 Python 3 的機器上,它會失敗並顯示“沒有這樣的文件或目錄“ 錯誤。
一種解決方案是創建別名alias python=python3
。
是否有其他解決方案可以在每台機器上統一工作相同的shebang?
不幸的是,沒有通用的工作方式可以在任何和所有未知的 Linux 主機上工作,而且您在很大程度上受發行版維護者和本地主機配置的支配。
alias
無濟於事,因為#!
由內核和/usr/bin/env
處理,在這種情況下它會執行不知道你的 shell 的別名。
使用env
,您可以確保首先找到env
后面的名稱並表示您希望它的含義:
mkdir /tmp/bin ln -s /usr/bin/python /tmp/bin/python3 PATH="/tmp/bin:${PATH}" ./myscript.py
但這一切都不是真正偉大的,最終是你所要求的。
你的解釋器(雖然這聽起來比聽起來更難,解釋器解析代碼非常簡單;把它放在哪里,如何調用它以便內核找到和使用它)也可以是一個簡單的 shell 腳本,它試圖弄清楚你打包了你的 python 代碼,但我擔心你看到的任何選項都不是很好。
有一個PEP-394用於在類似 U*X 的系統上建議/預期的內容:
python
python3
為 python3但它承認這從未完全一致地應用......而且在 2020 年也沒有那么有用:
但是,這些建議隱含地假設 Python 2 始終可用。 隨着 Python 2 在 2020 年接近其生命周期(PEP 373、PEP 404),發行版正在使 Python 2 成為可選或完全刪除它。 這意味着要么刪除 python 命令,要么將其切換為調用 Python 3。一些發行商還認為,忽略 PEP 的原始建議可以更好地為他們的用戶提供服務,並為系統管理員提供了根據其特定需求配置系統的自由環境。
TL; DR 不幸的是,沒有辦法普遍工作並補償各種發行版甚至個別主機維護者的決定。 :(
我很可能會選擇堅持使用#!/usr/bin/env python3
(到目前為止是推薦的命名)並添加一個README
來解釋先決條件以及如何設置主機以確保安全。
為了完整起見,我應該補充一下,PEP 確實在這方面提出了建議:設置和使用虛擬環境或使用(第三方)環境管理器。 但是,我閱讀問題的方式:“便攜式解釋器規范不對目標主機配置做出任何假設,也不對目標主機配置提出任何(額外)要求”,這將不符合要求,並且並不意味着比說:做的實質性改進確保搜索路徑中有python3
可執行文件,如果沒有,則創建一個符號鏈接。
#!/usr/bin/env python3
是最正確的通用解決方案。 在安裝了 Python 3 的系統上,這應該可以工作,無論是否安裝了 Python 2。
也就是說,仍然不能保證安裝了正確版本的 Python 3,並且硬編碼,例如, python3.7
是不可行的,因為大多數用戶不會安裝多個版本。
幸運的是,有一個更好的解決方案:如果您使用Setuptools將應用程序作為 Python 包分發並為您的二進制文件/二進制文件指定entry_points
,安裝過程將選擇正確的解釋器並將其路徑硬編碼到您的應用程序腳本中。 這將適用於任何環境,只要安裝程序(例如pip
)可以找到 Python。
作為一個非常簡單的示例,假設您的項目example
具有以下文件夾結構:
example
├── example
│ ╰── __init__.py
├── pyproject.toml
╰── setup.cfg
假設你的__init__.py
有一個main
函數,我們想用它作為可執行腳本的入口點。
然后pyproject.toml
看起來像這樣: 1
[build-system]
requires = ["setuptools", "wheel"]
setup.cfg
如下所示: 1,2
[metadata]
name = example
version = 0.0.1
[options]
packages = find:
[options.entry_points]
console_scripts =
example-bin = example:main
現在您可以使用例如pip3 bdist path-to-project
構建您pip3 bdist path-to-project
,這將生成 Wheel 安裝包。 當您安裝它時(在將其上傳到PyPI或本地(通過pip3 install wheel-filename.whl
)之后,pip3 將安裝二進制example-bin
。
example-bin
將啟動你的包的入口點函數,無論你的系統上調用什么 Python 3 二進制文件,它都會工作,因為pip3 install …
在 shebang 行中創建了一個具有絕對路徑的文件。 例如,在我的系統上,該文件的第一行是
#!/usr/local/opt/python/bin/python3.7
1與使用pyproject.toml
和setup.cfg
,使用舊版setup.py
文件同樣有效——但使用上述文件更簡單。
2請注意,在撰寫本文時,Setuptools 快速入門中有一個錯字:而不是[entry_point]
,它應該是[entry_point s ]
。
這是我現在確定的內容,盡管我仍在嘗試編寫我的代碼,以便在必要的情況下它可以與 Python 2 一起使用。 例如,如果您有幾個已知路徑,則可以根據需要修改它(不需要全部都在一行上):
#!/bin/bash
_='''' # Prefer python3 if it exists
command -v python3 &> /dev/null && exec python3 $0 "$@" || exec python $0 "$@"
'''
# Python code here
print ("Hello")
這是基於此頁面上有關混合腳本的示例: https : //gist.github.com/andyneff/fafba17b748bba6d7cd5
該腳本最初在 bash 中加載,它看到第一行以_=''''
開頭,bash 將其解釋為將變量_
設置為連續的兩個空字符串 ( ''
)。
第二行使用command -v python3
嘗試確定python3
是否在路徑中或定義為別名,並將 stdout 和 stderr 重定向到空設備。 如果返回碼成功,則運行exec python3 $0 "$@"
,其中$0
是腳本的名稱, "$@"
擴展為參數列表。 如果失敗,則運行等效的python
命令。
由於命令是exec
'd,python3/python 命令替換了當前的 bash 進程,bash 永遠不會看到以下幾行。
當 Python 讀取文件時,它看到_=''''
並將其解釋為'''...'''
多行三引號字符串的開頭,即使它以四個撇號而不是三個開頭,所以腳本以一個無害的額外_
變量集結束。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.