沒有#!的bash指令碼的執行

來源:互聯網
上載者:User

有些bash指令碼寫的不規範,沒有在檔案開頭寫#!,但是卻能直接執行,可是如果看核心代碼,shell指令碼的載入函數中的開頭就會判斷,如果沒有#!的話就會返回錯誤:
static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
{
...
    if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!') || (bprm->sh_bang))
        return -ENOEXEC;
...
}
為何在linux上寫bash指令碼時檔案開頭不寫#!也能成功調用?bash中執行一條鍵盤命令時調用的是shell_execve函數,該函數封裝了很多邏輯:
int shell_execve (command, args, env)
{
    execve (command, args, env);
    if (errno != ENOEXEC) {
        //非檔案格式的錯誤,返回出錯
          } else {
        int larray = array_len (args) + 1;
        int i, should_exec = 0;
          int fd = open (command, O_RDONLY);
          if (fd != -1) {
              unsigned char sample[80]; //讀取檔案頭的80個位元組,然後進行格式判斷
              int sample_len = read (fd, &sample[0], 80);
              ...//如果是空檔案,則正確返回,bash容忍這種情況
        if (sample_len > 0 && sample[0] == '#' && sample[1] == '!')
            return (execute_shell_script(...));//執行shell指令碼,其實現過程幾乎和核心的script_format結構對象中的

load_binary回呼函數一模一樣,主要用於不識別#!的作業系統
              else if (check_binary_file (sample, sample_len))
            //如果是二進位檔案,則bash是不會逐行協助執行的,出錯返回
    }
...//bash會幫你執行command:
/*
args[0] = shell_name; //就是本shell的全路徑
args[1] = command; //要執行的指令碼
execve (shell_name, args, env); //重新執行
*/
}
以下是上述shell_execve函數依賴的協助函數和一些宏定義:
由於bash是通過字元的可見度來判斷是否是二進位檔案的,由於空格/定位字元等也是不可見的字元,然而它們確實是屬於文本ascii碼的,因此需要單獨進行過濾:
#define isspace(c) ((c) == ' ' || (c) == '/t' || (c) == '/n' || (c) == '/f')
文本字元包括字母字元,數字字元以及除此之外的可以列印字元:
#define isprint(c) (isletter(c) || digit(c) || ispunct(c))
判斷讀取的80個位元組是否全部是文本字元,只要有一個不是就將該檔案判定為二進位檔案,雖然前80位元組的字元是文本不能保證後面的就一定是文本,可是bash畢竟需要做出一個權衡和一個假設:
int check_binary_file (sample, sample_len)
{
    for (i = 0; i < sample_len; i++) {
        if (sample[i] == '/n')
            break;
        if (!isspace (sample[i]) && !isprint (sample[i]))
            return (1);
        }
      return (0);
}
因此如果執行一個空檔案,將會看到什麼也不輸出,如果執行一個非linux可執行檔二進位檔案比如cer格式的二進位認證,那麼核心將返回ENOEXEC,接下來bash會嘗試執行之,然後最終返回一個EX_BINARY_FILE錯誤,列印“cannot execute binary file”,如果執行一個檔案頭不是#!的文字檔,同樣核心會返回一個ENOEXEC錯誤,但是bash卻可以在shell_execve的後續執行中成功直接執行,可是如果在一個二進位檔案的頭部增加80位元組或者80位元組以上的文本,那麼根據shell_execve的邏輯,bash也會將之作為指令碼來分行執行,然後就會大錯特錯,因此雖然有的時候指令碼中不寫#!也能直接執行,但是要知道那可是bash本身幫你執行的,而不是核心直接執行的,起碼執行緒會先從核心錯誤返回,然後繼續shell_execve的後面的部分,確實稍微影響了效率,因此最好還是寫上標準的#!,況且有的shell並不會像bash這麼智能地重新解釋執行命令。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.