本文翻譯自:What is the difference between “#!/usr/bin/env bash” and “#!/usr/bin/bash”?
In the header of a bash
script, what's the difference between those two statements ? 在bash
腳本的標題中,這兩個語句之間的區別是什麼?
#!/usr/bin/env bash
#!/usr/bin/bash
When I tried to see the env
man page, I get this definition: 當我試圖看到env
手冊頁時,我得到了這個定義:
env - run a program in a modified environment
What does it mean? 這是什麼意思?
#1樓
參考:https://stackoom.com/question/16fJi/usr-bin-env-bash-和-usr-bin-bash-有什麼區別
#2樓
Instead of explicitly defining the path to the interpreter as in /usr/bin/bash/
, by using the env command, the interpreter is searched for and launched from wherever it is first found. 不是通過使用env命令在/usr/bin/bash/
中明確定義解釋器的路徑,而是從首次找到解釋器的任何地方搜索並啓動解釋器。 This has both upsides and downsides 這有好處和缺點
#3樓
Using #!/usr/bin/env NAME
makes the shell search for the first match of NAME in the $PATH environment variable. 使用#!/usr/bin/env NAME
使shell在$ PATH環境變量中搜索NAME的第一個匹配項。 It can be useful if you aren't aware of the absolute path or don't want to search for it. 如果您不知道絕對路徑或不想搜索它,它會很有用。
#4樓
Running a command through /usr/bin/env
has the benefit of looking for whatever the default version of the program is in your current env ironment. 通過運行一個命令/usr/bin/env
有尋找任何程序的默認版本是在當前的ENV ironment利益。
This way, you don't have to look for it in a specific place on the system, as those paths may be in different locations on different systems. 這樣,您不必在系統上的特定位置查找它,因爲這些路徑可能位於不同系統上的不同位置。 As long as it's in your path, it will find it. 只要它在你的道路上,它就會找到它。
One downside is that you will be unable to pass more than one argument (eg you will be unable to write /usr/bin/env awk -f
) if you wish to support Linux, as POSIX is vague on how the line is to be interpreted, and Linux interprets everything after the first space to denote a single argument. 一個缺點是,如果你想支持Linux,你將無法傳遞多個參數(例如,你將無法編寫/usr/bin/env awk -f
),因爲POSIX對於該線路是如何模糊的解釋,並且Linux在第一個空格之後解釋所有內容以表示單個參數。 You can use /usr/bin/env -S
on some versions of env
to get around this, but then the script will become even less portable and break on fairly recent systems (eg even Ubuntu 16.04 if not later). 您可以在某些版本的env
上使用/usr/bin/env -S
來解決這個問題,但隨後腳本將變得更不便攜並且在相當近期的系統上中斷(例如,如果不是更晚的話,甚至是Ubuntu 16.04)。
Another downside is that since you aren't calling an explicit executable, it's got the potential for mistakes, and on multiuser systems security problems (if someone managed to get their executable called bash
in your path, for example). 另一個缺點是,由於您沒有調用顯式可執行文件,因此可能存在錯誤,並且存在多用戶系統安全問題(例如,如果有人設法在您的路徑中獲取名爲bash
的可執行文件)。
#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash #gives you explicit control on a given system of what executable is called
In some situations, the first may be preferred (like running python scripts with multiple versions of python, without having to rework the executable line). 在某些情況下,第一個可能是首選(如運行python腳本與多個版本的python,而不必重做可執行行)。 But in situations where security is the focus, the latter would be preferred, as it limits code injection possibilities. 但是在以安全性爲重點的情況下,後者將是首選,因爲它限制了代碼注入的可能性。
#5樓
I find it useful, because when I didn't know about env, before I started to write script I was doing this: 我發現它很有用,因爲當我不知道env時,在我開始編寫腳本之前我就是這樣做的:
type nodejs > scriptname.js #or any other environment
and then I was modifying that line in the file into shebang. 然後我將文件中的那一行修改爲shebang。
I was doing this, because I didn't always remember where is nodejs on my computer -- /usr/bin/ or /bin/, so for me env
is very useful. 我這樣做,因爲我並不總是記得我的計算機上的nodejs在哪裏 - / usr / bin /或/ bin /,所以對我來說env
非常有用。 Maybe there are details with this, but this is my reason 也許這裏有細節,但這是我的理由
#6樓
If the shell scripts start with #!/bin/bash
, they will always run with bash
from /bin
. 如果shell腳本以#!/bin/bash
開頭,它們將始終使用來自/bin
bash
運行。 If they however start with #!/usr/bin/env bash
, they will search for bash
in $PATH
and then start with the first one they can find. 然而,如果他們以#!/usr/bin/env bash
開頭,他們將在$PATH
搜索bash
,然後從他們可以找到的第一個開始。
Why would this be useful? 爲什麼這會有用? Assume you want to run bash
scripts, that require bash 4.x or newer, yet your system only has bash
3.x installed and currently your distribution doesn't offer a newer version or you are no administrator and cannot change what is installed on that system. 假設您要運行需要bash 4.x或更新版本的bash
腳本,但您的系統只安裝了bash
3.x,目前您的發行版沒有提供更新的版本,或者您不是管理員,也無法更改安裝的內容那個系統。
Of course, you can download bash source code and build your own bash from scratch, placing it to ~/bin
for example. 當然,您可以從頭開始下載bash源代碼並構建自己的bash,例如將它放在~/bin
中。 And you can also modify your $PATH
variable in your .bash_profile
file to include ~/bin
as the first entry ( PATH=$HOME/bin:$PATH
as ~
will not expand in $PATH
). 您還可以在.bash_profile
文件中修改$PATH
變量,以包含~/bin
作爲第一個條目( PATH=$HOME/bin:$PATH
as ~
將不會在$PATH
展開)。 If you now call bash
, the shell will first look for it in $PATH
in order, so it starts with ~/bin
, where it will find your bash
. 如果你現在調用bash
,shell將首先在$PATH
中按順序查找它,所以它以~/bin
開頭,它會找到你的bash
。 Same thing happens if scripts search for bash
using #!/usr/bin/env bash
, so these scripts would now be working on your system using your custom bash
build. 如果腳本使用#!/usr/bin/env bash
搜索bash
則會發生同樣的事情,因此這些腳本現在將使用您的自定義bash
構建在您的系統上運行。
One downside is, that this can lead to unexpected behavior, eg same script on the same machine may run with different interpreters for different environments or users with different search paths, causing all kind of headaches. 一個缺點是,這可能導致意外行爲,例如,同一臺機器上的相同腳本可能針對不同的環境或具有不同搜索路徑的用戶使用不同的解釋器運行,從而導致各種令人頭疼的問題。
The biggest downside with env
is that some systems will only allow one argument, so you cannot do this #!/usr/bin/env <interpreter> <arg>
, as the systems will see <interpreter> <arg>
as one argument (they will treat it as if the expression was quoted) and thus env
will search for an interpreter named <interpreter> <arg>
. env
最大的缺點是有些系統只允許一個參數,所以你不能這樣做#!/usr/bin/env <interpreter> <arg>
,因爲系統會將<interpreter> <arg>
視爲一個參數(他們會把它看作是引用的表達式,因此env
將搜索名爲<interpreter> <arg>
。 Note that this is not a problem of the env
command itself, which always allowed multiple parameters to be passed through but with the shebang parser of the system that parses this line before even calling env
. 請注意,這不是env
命令本身的問題,它始終允許傳遞多個參數,但使用系統的shebang解析器,甚至在調用env
之前解析此行。 Meanwhile this has been fixed on most systems but if your script wants to be ultra portable, you cannot rely that this has been fixed on the system you will be running. 與此同時,這已在大多數系統上得到修復,但如果您的腳本想要超便攜,則不能依賴於已在您運行的系統上修復此腳本。
It can even have security implications, eg if sudo
was not configured to clean environment or $PATH
was excluded from clean up. 它甚至可能具有安全隱患,例如,如果sudo
未配置爲清理環境或$PATH
被排除在清理之外。 Let me demonstrate this: 讓我演示一下:
Usually /bin
is a well protected place, only root
is able to change anything there. 通常/bin
是一個受到良好保護的地方,只有root
能夠在那裏改變任何東西。 Your home directory is not, though, any program you run is able to make changes to it. 但是,您的主目錄不是,您運行的任何程序都可以對其進行更改。 That means malicious code could place a fake bash
into some hidden directory, modify your .bash_profile
to include that directory in your $PATH
, so all scripts using #!/usr/bin/env bash
will end up running with that fake bash
. 這意味着惡意代碼可能會將僞造的bash
放入某個隱藏目錄,修改.bash_profile
以將該目錄包含在$PATH
,因此使用#!/usr/bin/env bash
所有腳本最終都將使用該僞造的bash
運行。 If sudo
keeps $PATH
, you are in big trouble. 如果sudo
保持$PATH
,你就會遇到大麻煩。
Eg consider a tool creates a file ~/.evil/bash
with the following content: 例如,考慮一個工具創建一個文件~/.evil/bash
其中包含以下內容:
#!/bin/bash
if [ $EUID -eq 0 ]; then
echo "All your base are belong to us..."
# We are root - do whatever you want to do
fi
/bin/bash "$@"
Let's make a simple script sample.sh
: 讓我們製作一個簡單的腳本sample.sh
:
#!/usr/bin/env bash
echo "Hello World"
Proof of concept (on a system where sudo
keeps $PATH
): 概念證明(在sudo
保留$PATH
的系統上):
$ ./sample.sh
Hello World
$ sudo ./sample.sh
Hello World
$ export PATH="$HOME/.evil:$PATH"
$ ./sample.sh
Hello World
$ sudo ./sample.sh
All your base are belong to us...
Hello World
Usually the classic shells should all be located in /bin
and if you don't want to place them there for whatever reason, it's really not an issue to place a symlink in /bin
that points to their real locations (or maybe /bin
itself is a symlink), so I would always go with #!/bin/sh
and #!/bin/bash
. 通常,經典shell應該都位於/bin
,如果你不想因爲某種原因將它們放在那裏,那麼在/bin
中放置指向它們真實位置的符號鏈接(或者/bin
本身)真的不是問題。是一個符號鏈接),所以我總是選擇#!/bin/sh
和#!/bin/bash
。 There's just too much that would break if these wouldn't work anymore. 如果這些不再起作用,那就太多了會破壞。 It's not that POSIX would require these position (POSIX does not standardize path names and thus it doesn't even standardize the shebang feature at all) but they are so common, that even if a system would not offer a /bin/sh
, it would probably still understand #!/bin/sh
and know what to do with it and may it only be for compatibility with existing code. 並不是POSIX會要求這些位置(POSIX沒有標準化路徑名,因此它甚至根本沒有標準化shebang功能)但它們是如此常見,即使系統不提供/bin/sh
,它也是如此可能仍然會理解#!/bin/sh
並知道該如何處理它可能只是爲了與現有代碼兼容。
But for more modern, non standard, optional interpreters like Perl, PHP, Python, or Ruby, it's not really specified anywhere where they should be located. 但是對於更現代的,非標準的,可選的解釋器,如Perl,PHP,Python或Ruby,它並沒有真正指定它們應該位於何處。 They may be in /usr/bin
but they may as well be in /usr/local/bin
or in a completely different hierarchy branch ( /opt/...
, /Applications/...
, etc.). 它們可能位於/usr/bin
但它們也可能位於/usr/local/bin
或完全不同的層次結構分支( /opt/...
, /Applications/...
等)中。 That's why these often use the #!/usr/bin/env xxx
shebang syntax. 這就是爲什麼這些經常使用#!/usr/bin/env xxx
shebang語法。