關於 Bash 指令碼中 Shebang 的趣事

鹹魚Linux運維發表於2023-05-07

哈嘍大家好,我是鹹魚

不知道小夥伴們在寫 Bash 指令碼或者說看別人的 Bash 指令碼的時候有沒有注意過指令碼的第一行

#!/bin/bash

Bash 指令碼的第一行往往以 #! 開頭,這一行稱作 shebang 行

在 類 UNIX 系統中,shebang 行用來指定指令碼的直譯器路徑,通常出現在第一行,格式如下

#! interpreter_path

shebang 行中開頭 #! 字元的作用是告訴作業系統這不是一個普通二進位制檔案,而是需要透過直譯器執行的東西

而這個直譯器則透過 #! 字元後面來指定。例如 /bin/bash 表示使用 bash 直譯器來執行該指令碼檔案

下面則是一些 Bash 指令碼的 shebang 行,指定了不同的直譯器

#! /usr/bin/perl
#! /usr/bin/awk
#! /usr/bin/python

那麼這時候小夥伴們可能就會有疑問:我忘了加 shebang 行,指令碼為什麼還能執行?

如果一個指令碼沒有新增 shebang 行來指定直譯器路徑,則預設情況下系統會使用預設的 shell 來執行指令碼,系統預設的 shell 可以透過下面的命令來檢視

# 一般情況下預設的 shell 為bash
echo $SHELL

現在我們知道了 shebang 行的作用,那麼我們現在來編寫一個指令碼並修改 shebang 行試試

test.sh 內容如下:

#!/bin/bash
echo Hello

先給 test.sh 指令碼新增一下執行許可權

chmod +x test.sh

接下來我們用幾種方式來執行這個指令碼

可以看到指令碼都成功執行了

下面我們來改一下 shebang 行,將其改成其他命令

#!/usr/bin/ls -l
echo Hello

然後我們分別用幾種方式來執行這個指令碼

上面指令碼執行的結果是不是看的一臉懵逼,說實話我一開始看到的時候也是很懵

我們先來看下這四種指令碼執行方式的區別

  • bash tesh.sh

這種方式執行指令碼的原理是將 test.sh 作為引數傳給 bash 直譯器(命令)來執行,而不是 test,sh 自己來執行

這種方式執行指令碼不需要給指令碼檔案新增執行許可權、不需要寫 shebang 行指定直譯器路徑,因為指令碼是作為引數被傳給 bash 來執行

  • sh test.sh

這種執行指令碼的方式跟上面的方式原理一樣,都是將指令碼作為引數傳進去,只不過是這個方式用的是 sh 直譯器(命令),而不是 bash

  • /root/test.sh

這種是透過絕對路徑去執行指令碼,透過絕對路徑來執行指令碼就需要指令碼擁有執行許可權

當使用絕對路徑來執行指令碼時,作業系統需要知道該指令碼檔案所使用的直譯器型別,這就需要依靠指令碼檔案中的 shebang 行

實際上你用絕對路徑執行指令碼的時候,如果裡面定義了 shebang 行(例如 #! /bin/bash

那麼實際上跟下面的命令是一樣的

/bin/bash /root/test.sh

在執行指令碼的時候,作業系統會讀取指令碼的 shebang 行

如果你的 shebang 行是其他 Linux 命令而不是直譯器,那麼就會導致作業系統將你的 shebang 行當作命令,而你的指令碼則是命令的引數

就好比上面的例子,我將 shebang 行改成了 #! /usr/bin/ls -l ,當我執行指令碼的時候其實就是下面這樣的

/usr/bin/ls -l /root/test.sh

這樣會導致指令碼無法執行

  • ./test.sh

這種是透過相對路徑去執行指令碼,跟上面用絕對路徑執行指令碼方式是一樣的,只不過區別是一個是相對路徑一個是絕對路徑

總結:

  • shebang 行通常出現在 UNIX 系統的指令碼當中,用來指定指令碼的直譯器路徑,出現在第一行,以 #! 開頭
  • 如果指令碼里面沒有定義 shebang 行,系統會去找預設的直譯器,預設直譯器用 echo $SHELL 檢視
  • 用 bash 或者 sh 命令執行指令碼的時候,其實是把指令碼作為引數傳給 bash 或 sh 命令了,這時候指令碼可以不新增執行許可權、可以不需要 shebang 行
  • 如果用絕對路徑或者相對路徑的方式來執行指令碼,需要指令碼擁有執行許可權,如果 shebang 行定義的不是直譯器而是其他命令,就會導致指令碼無法執行

附上參考連結:Shebang Shenanigans :: Linus Karlsson

相關文章