Linux 腳本首行的秘密:究竟 #!/usr/bin/env bash#!/bin/bash 代表什麼?

Linux 中編寫腳本時,我們習慣性地會在最上方寫上這行程式碼。

#!/usr/bin/env bash

或者

#!/bin/bash

表面上看起來像註解,但這一行的真實身份究竟是什麼?它們之間又存在什麼差異呢?本文將深入解析這行程式碼的奧秘,並探討在不同情境下應選擇哪種寫法。


1. 這不是註解:什麼是 shebang?{#sec-9f8041c620ba}

#! 開頭的這一行,我們稱之為 shebang

#!/bin/bash

對於 Shell 來說,因為它以 # 開頭,所以看起來確實像是「註解」。 然而,對於作業系統(核心)而言,這並非註解,而是:

「指示作業系統,告知執行此腳本的直譯器程式位於何處的指令。」

換句話說,

  • #!/bin/bash → 「請使用 /bin/bash 來執行此檔案。」
  • #!/usr/bin/env bash → 「請透過 env 尋找 bash 直譯器,並使用它來執行此檔案。」

這就是它們各自的含義。


2. 核心如何執行腳本?{#sec-33d96b8d8f01}

將執行過程極度簡化,大致如下:

  1. 使用者執行具有執行權限的腳本
chmod +x script.sh
./script.sh
  1. 核心讀取 script.sh
  2. 檢查檔案的前兩個字元是否為 #!
  3. 若是,則將該行剩餘部分
  • 識別為「直譯器的路徑 + 參數」,並
  • 執行該程式,同時將腳本檔案路徑作為參數傳遞

例如,如果 script.sh 的首行是這樣:

#!/bin/bash

核心實際執行的動作,大致可以這樣理解:

/bin/bash script.sh

換句話說,核心替我們執行了類似於我們手動輸入 bash script.sh 的操作。

注意: #!/ 前面不能有任何空格。 檔案的第一個字元必須是 #,第二個是 !


3. #!/bin/bash 的含義與特性

這是最常見的形式。

#!/bin/bash

含義

  • 「這個腳本是 bash 腳本,且 bash 位於 /bin/bash。」
  • 核心在執行此腳本時,總是會執行 /bin/bash

優點

  • 明確性:由於總是使用 /bin/bash,因此很容易預測將使用哪個 bash 版本。
  • 效能/簡潔性:直接執行,不需透過 env,因此沒有路徑搜尋的過程。
  • 在許多 Linux 發行版中,/bin/bash 實際上被視為「標準位置」。

缺點

  • 可能不具可攜性

  • 在某些系統上,bash 可能位於不同的路徑,例如 /usr/bin/bash/usr/local/bin/bash 等。

  • 有些系統可能根本沒有安裝 bash,只有 /bin/sh
  • 特別是在 macOS, BSD 系列, NixOS, 某些容器環境等系統中,路徑可能有所不同。

4. #!/usr/bin/env bash 的含義與特性

這是目前腳本中常見的形式。

#!/usr/bin/env bash

這裡的關鍵是 /usr/bin/env

  • env 是一個協助「設定/檢查環境變數 + 在 PATH 中搜尋程式」的工具程式。

  • 核心實際執行的動作,可以這樣理解:

/usr/bin/env bash script.sh
  • env 會查看系統的 PATH 環境變數,並在其中尋找並執行 bash 可執行檔。

優點

  1. 可攜性高(在多種環境中運作良好)
  • 無論 bash 位於 /bin/bash/usr/bin/bash 還是 /usr/local/bin/bash
  • 只要正確地註冊在 PATH 中,env 就能找到它。
  1. 使用符合使用者環境的 bash
  • 如果使用者自定義了 PATH,並設定優先使用特定版本的 bash,那麼腳本就會使用該版本。
  1. 在 Python 等語言中也採用相同模式
#!/usr/bin/env python3

缺點

  1. 前提是 /usr/bin/env 必須存在
  • 儘管它幾乎存在於所有現代 Unix/Linux 系統中,但在極特殊環境下可能不存在。
  1. 可能因 PATH 設定而選用不同的直譯器
  • 如果 PATH 設定混亂,或者在搜尋路徑中意外地找到了非預期的 bash 版本,可能會執行到錯誤的版本。
  1. 從安全角度來看,有時需要特別注意
  • 在對安全性極為敏感的環境中,人們可能會偏好使用絕對路徑,而不喜歡基於 PATH 來尋找直譯器的方式。

5. 兩種方式比較總結

以下透過簡要比較表進行整理。

類別 #!/bin/bash #!/usr/bin/env bash
直譯器位置指定方式 固定絕對路徑 透過 PATH 搜尋
可攜性(系統相容性) 較低(路徑不同則失效) 較高(只要 bash 在 PATH 中即可)
使用哪個 bash 總是 /bin/bash 在 PATH 中找到的第一個 bash
預期版本保證 相對容易 可能因 PATH 狀態而異
安全性/控制力 更強(路徑固定) 略為寬鬆(依賴 PATH)
一般最新趨勢 相對舊式風格 近來較推薦此方式

6. 何時該選擇哪種方式?{#sec-a9bac1ab9ba4}

對於「究竟我該用哪種?」這個問題,我們將根據不同情境進行整理。

1) 個人/團隊開發用腳本(一般開發環境)

  • 通常建議使用以下方式:
#!/usr/bin/env bash
  • 原因:

  • 在開發者直接管理的伺服器、本地環境、CI 環境等中,bash 的位置可能不同。

  • 基於 PATH 搜尋並執行更具彈性,且現代的工具/腳本也偏好此方式。

2) 適用於特定伺服器環境的營運腳本

  • 例如,若公司內所有伺服器都統一將 bash 安裝在 /bin/bash

  • 且伺服器環境相當固定,則:

#!/bin/bash
  • 原因:

  • 確保始終使用相同的直譯器。

  • 可以減少因 PATH 修改而導致的意外行為。

3) 如果真的想讓腳本具備極高的可攜性?

  • 如果環境中「可能根本沒有 bash」,那麼 首先應該考慮該腳本是否真的適合依賴 bash。

  • 如果可能,請使用 sh 編寫:

#!/bin/sh
  • 這樣可以在更廣泛的環境中執行。 但請注意,不能使用 bash 特有的語法(如 [[ ]]、陣列、擴展字串處理等)。

7. 使用 #!/usr/bin/env bash 的實用技巧

1) 腳本執行方法

要正確利用 shebang,不應該只這樣做:

bash script.sh   # ← 這樣執行 shebang 幾乎沒有意義

建議以下列方式使用:

chmod +x script.sh   # 賦予執行權限
./script.sh          # 直接執行

這樣核心才會讀取 #!,並使用指定的直譯器。

2) 確認參數傳遞

例如,假設我們建立了一個 test.sh 腳本如下:

#!/usr/bin/env bash

echo "直譯器: $0"
echo "參數: $@"

執行:

chmod +x test.sh
./test.sh hello world

輸出:

直譯器: ./test.sh
參數: hello world

這裡的 $0 是「腳本檔案本身的的路徑」, 只需記住核心實際上是像 /usr/bin/env bash test.sh hello world 這樣執行即可。


8. 常見問題

我曾看過沒有 shebang 的腳本,這是怎麼回事?

  • 沒錯,shebang 並非總是必需的。

  • 如果像下面這樣直接指定直譯器來執行,則不需要 shebang。即使寫了 shebang 也會被忽略。

bash myscript.sh
python3 myscript.py
  • 但是,如果想以下列方式執行,則 shebang 是必需的:
./myscript.sh
./myscript.py
  • 特別是對於提供給他人使用的「工具型」腳本,shebang 幾乎可以說是必不可少

「除了 #!/usr/bin/env,我還看過 #!/bin/env 也能用?」

  • 在某些系統上,確實可能存在 /bin/env
  • 然而,通常 /usr/bin/env 是更為通用的標準位置。
  • 如果沒有特殊理由,使用 #!/usr/bin/env … 會更安全。

9. 總結

  • #!/usr/bin/env bash#!/bin/bash 並非註解,而是告知核心「應使用哪個直譯器來執行此腳本」的指令
  • #!/bin/bash

  • 總是使用 /bin/bash在固定環境中可預測,但可能不具可攜性

  • #!/usr/bin/env bash

  • PATH 中尋找 bash 並使用 → 更具彈性與可攜性,但會受 PATH 狀態影響

  • 在一般的開發/部署環境中,如果以「現代風格」編寫腳本, 則建議將 #!/usr/bin/env bash 作為預設選項
Linux 腳本中的 shebang 示意圖
shebang 的運作原理圖