## Bash的生命周期
讓我們來了解一下Bash從開始到、執行過程到結束它是什么一個過程。
### Bash的開始
目前我們已經比較了解Bash腳本編程了,一個腳本程序的開始方式都比較統一,它們幾乎都開始于一個\#!符號。這個符號的作用大家似乎也都知道,叫做聲明解釋器。腳本語言跟編譯型語言的不一樣之處主要是腳本語言需要解釋器。因為腳本語言主要是文本,而系統中能夠執行的文件實際上都是可執行的二進制文件,就是編譯好的文件。文本的好處是人看方便,但是操作系統并不能直接執行,所以就需要將文本內容傳遞給一個可執行的二進制文件進行解析,再由這個可執行的二進制文件根據腳本的內容所確定的行為進行執行。可以做這種解析執行的二進制可執行程序都可以叫做解釋器。
腳本開頭的\#!就是用來聲明本文件的文本內容是交給那個解釋器進行解釋的。比如我們寫bash腳本,一般聲明的方法是\#!/bin/bash或\#!/bin/sh。如果寫的是一個python腳本,就用\#!/usr/bin/python。當然,在不同環境的系統中,這個解釋器放的路徑可能不一樣,所以固定寫一個路徑的方式就可能造成腳本在不同環境的系統中不通用的情況,于是就出現了這樣的寫法:
```
#!/usr/bin/env 腳本解釋器名稱
```
這就利用了env命令可以得到可執行程序執行路徑的功能,讓腳本自行找到在當前系統上到底解釋器在什么路徑。讓腳本更具通用性。但是大家有沒有想過一個問題,大多數腳本語言都是將\#后面出現的字符當作是注釋,在腳本中并不起作用。這個\#!和這個注釋的規則不沖突么?
這就要從\#!符號起作用的原因說起,其實也很簡單,這個功能是由操作系統的程序載入器做的。在Linux操作系統上,出了1號進程以外,我們可以認為其它所有進程都是由父進程fork出來的。所以對bash來說,所謂的載入一個腳本執行,無非就是父進程調用fork\(\)、exec\(\)來產生一個子進程。這\#!就是在內核處理exec的時候進行解析的。
_內核中整個調用過程如下(linux 4.4),內核處理exec族函數的主要實現在fs/exec.c文件的do\_execveat\_common\(\)方法中,其中調用exec\_binprm\(\)方法處理執行邏輯,這函數中使用search\_binary\_handler\(\)對要加載的文件進行各種格式的判斷,腳本(script)只是其中的一種。確定是script格式后,就會調用script格式對應的load\_binary方法:load\_script\(\)進行處理,\#!就是在這個函數中解析的。解析到了\#!以后,內核會取其后面的可執行程序路徑,再傳遞給search\_binary\_handler()重新解析。這樣最終找到真正的可執行二進制文件進行相關執行操作。_
因此,對腳本第一行的\#!解析,其實是內核給我們變的魔術。\#!后面的路徑內容在起作用的時候還沒有交給腳本解釋器。很多人認為\#!這一行是腳本解釋器去解析的,然而并不是。了解了原理之后,也順便明白了為什么\#!一定要寫在第一行的前兩個字符,因為這是在內核里寫死的,它就只檢查前兩個字符。當內核幫你選好了腳本解釋器之后,后續的工作就都交給解釋器做了。腳本的所有內容也都會原封不動的交給解釋器再次解釋,是的,包括\#!。但是由于對于解釋器來說,\#開頭的字符串都是注釋,并不生效,所以解釋器自然對\#!后面所有的內容無感,繼續解釋對于它來說有意義的字符串去了。
### Bash執行過程
實際上bash在做這些功能的時候已經安排好了它們在名字沖突的情況下究竟該先以什么方式解釋。優先順序是:
* 別名:alias
* 關鍵字:keyword
* 函數:function
* 內建命令:built in
* 哈西索引:hash
* 外部命令:command
1.alias命令用來設置指令的別名。我們可以使用該命令可以將一些較長的命令進行簡化。
```
[root@blog.puppeter.com_centos ~]# alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
```
alias基本使用:
* 設置alias,新的命令='原命令 -選項/參數'
* 查看alias,alias -p
* 刪除alias別名,unalias 命令名
2.keyword 關鍵字\(key word\),譬如if,while,function等等。我們通過type來查看是否為關鍵字。
```
[root@blog.puppeter.com_centos ~]# type function
function is a shell keyword
```
3.在判斷為是否關鍵字后,Bash會再判斷是否為函數\(function\)。
4.僅接著會判斷是否為內建命令\(built in\)。
```
[root@blog.puppeter.com_centos ~]# type alias
alias is a shell builtin
```
5.接著判斷是否在hash索引。bash提供了一種功能,就是建立一個bash表,在第一次找到一個命令的路徑之后,對其命令名和對應的路徑建立一個hash索引。這樣下次再執行這個命令的時候,就不用去遍歷所有的目錄了,只要查詢索引就可以更快的找到命令路徑,以加快執行程序的速度。
```
[root@blog.puppeter.com_centos ~]# hash
hits command
1 /bin/ls
```
最后是外部命令,外部命令主要搜索$PATH變量。
案例來源:[zooro的文章](https://mp.weixin.qq.com/s?__biz=MzIxNDMyODgyMA==&mid=2247483666&idx=1&sn=b3df5f3f8d8803fb88719463388db4ed&scene=0#wechat_redirect。)
- Introduction
- 1.Shell編程基礎
- 什么是Shell
- 編寫第一個Bash程序
- 2.變量
- 什么是變量
- 變量四種賦值方式
- 指定變量類型
- 變量的類型
- 特殊變量
- 3.Bash符號相關
- Bash符號相關
- 特殊符號
- 數學運算符號
- 4.內建命令與外部命令
- 內建命令與外部命令
- 5.read命令
- read命令
- 6.條件語句
- if..then..fi
- if..then..else..fi
- if..then..elif..fi
- 空命令
- 文件測試符
- 字符串判斷、與、或和非
- []與[[]]符號
- 7.Bash循環 && 分支語句
- while循環語句
- for循環
- for..in循環
- until循環
- select循環
- case分支語句
- 8.正則表達式
- 正則表達式
- 9.子Shell和受限Shell
- 子Shell
- 受限Shell
- 10.函數與函數的加載
- 函數語法與案例
- 遞歸函數
- 函數的加載
- 11.Bash腳本風格
- Bash腳本風格
- 12.Bash腳本調試
- Bash腳本調試
- 13.sed && awk
- sed
- 14.awk
- awk
- 15 Bash雜項 && 案例
- while循環的陷阱
- Bash的生命周期
- IO重定向
- /bin/bash和/bin/sh區別
- 命令雜項
- Shell加密
- 16.10.2.案例
- Bash版回收站
- 17.Bash中文速查表
- 中文速查表