鍍金池/ 教程/ Linux/ shell 學(xué)習(xí)三十八天----執(zhí)行順序和 eval
shell 學(xué)習(xí)四十一天----列出文件 ls 和 od 命令
shell 學(xué)習(xí)小結(jié)
shell 學(xué)習(xí)第二十八天----case 語(yǔ)句
shell 學(xué)習(xí)四十四天----尋找文件
shell 學(xué)習(xí)三十五天----波浪號(hào)展開(kāi)與通配符
shell 學(xué)習(xí)三十八天----執(zhí)行順序和 eval
shell 學(xué)習(xí)四十八天----文件校驗(yàn)和匹配
shell 學(xué)習(xí)四十天----awk 的驚人表現(xiàn)
shell 學(xué)習(xí)第十一天----sed 正則的精確控制
shell 學(xué)習(xí)第十七天----awk 命令
shell 學(xué)習(xí)三十九天----內(nèi)建命令
shell 學(xué)習(xí)四十五天----xargs
shell 學(xué)習(xí)三十七天----引用
shell 學(xué)習(xí)第十六天----join 練習(xí)
shell 學(xué)習(xí)第二十四天----提取開(kāi)頭或結(jié)尾數(shù)行
shell 學(xué)習(xí)第九天----分組
shell 學(xué)習(xí)第五天----基本的 I/O 重定向
shell 學(xué)習(xí)第十五天----使用 cut 選定字段
shell 學(xué)習(xí)四十六天----文件系統(tǒng)的空間信息 df 和 du 命令
shell 學(xué)習(xí)第八天----擴(kuò)展正則表達(dá)式 (ERE)
shell 學(xué)習(xí)第十九天----文本塊排序
shell 學(xué)習(xí)第二十一天----重新格式化段落
shell 學(xué)習(xí)三十六天----命令替換
shell 學(xué)習(xí)第二十天----sort 的其他內(nèi)容以及 uniq 命令
shell 學(xué)習(xí)四十二天----使用 touch 更新文件時(shí)間
shell 學(xué)習(xí)五十一天----top 命令查看進(jìn)程列表
shell 學(xué)習(xí)第一天
shell 學(xué)習(xí)五十四天----進(jìn)程系統(tǒng)調(diào)用的追蹤 strace
shell 學(xué)習(xí)五十天----查看進(jìn)程 ps 命令
shell 學(xué)習(xí)第二十七天----退出狀態(tài)和 if 語(yǔ)句
shell 學(xué)習(xí)第二十三天----打印
shell 學(xué)習(xí)三十三天----關(guān)于重定向
shell 學(xué)習(xí)四十九天----進(jìn)程建立
shell 學(xué)習(xí)第三十天----break,continue,shift,getopts
shell 學(xué)習(xí)五十五天----進(jìn)程記賬
shell 學(xué)習(xí)總結(jié)一
shell 學(xué)習(xí)第二十六天----變量與算數(shù)
shell 學(xué)習(xí)第二十二天----計(jì)算行數(shù), 字?jǐn)?shù)以及字符數(shù)
shell 學(xué)習(xí)五十八天----/proc 文件系統(tǒng)
shell 學(xué)習(xí)第二十五天----神器的管道符
shell 學(xué)習(xí)第三十二天----read 讀取一行
sheel 學(xué)習(xí)第三天
shell 學(xué)習(xí)----小結(jié)
shell 學(xué)習(xí)第十八天----文本排序
shell 學(xué)習(xí)第三十一天----函數(shù)問(wèn)題
shell 學(xué)習(xí)第十天----sed 查找與替換
shell 學(xué)習(xí)四十三天----臨時(shí)性文件的建立與使用
shell 學(xué)習(xí)四十七天----文件比較 cmp,diff,patch
shell 學(xué)習(xí)三十四天----printf 詳解
shell 學(xué)習(xí)五十七天 ----linux 任務(wù)管理,針對(duì)上一講的總結(jié)和擴(kuò)展
shell 學(xué)習(xí)第六天----小結(jié)
shell 學(xué)習(xí)第十三天----sed 案例分析
shell 學(xué)習(xí)第七天----基礎(chǔ)正則表達(dá)式 (BRE)
shell 學(xué)習(xí)第十二天----行與字符串
shell 學(xué)習(xí)小結(jié)四
shell 學(xué)習(xí)第二十九天----循環(huán)
shell 學(xué)習(xí)五十二天----刪除進(jìn)程 kill 命令
shell 學(xué)習(xí)五十六天----延遲進(jìn)程調(diào)度
shell 學(xué)習(xí)第四天----華麗的 printf 輸出
shell 學(xué)習(xí)第十五天----join 連接字段
shell 學(xué)習(xí)完結(jié)篇----希望你能看到
shell 學(xué)習(xí)第二天
shell 學(xué)習(xí)五十三天----捕獲信號(hào) trap

shell 學(xué)習(xí)三十八天----執(zhí)行順序和 eval

執(zhí)行順序和 eval

shell 從標(biāo)準(zhǔn)輸入或腳本中讀取的每一行稱為管道,它包含了一個(gè)或多個(gè)命令,這些命令被一個(gè)或多個(gè)管道字符 (|) 隔開(kāi)。

事實(shí)上還有很多特殊符號(hào)可用來(lái)分割單個(gè)的命令:分號(hào) (;),管道 (|)&,邏輯 AND(&&),邏輯 OR(||)。對(duì)于每一個(gè)地區(qū)的管道,shell 都會(huì)將命令分割,為管道設(shè)置 I/O,并且對(duì)每一個(gè)命令依次執(zhí)行下面的操作。

http://wiki.jikexueyuan.com/project/learn-shell/images/1.png" alt="" />

看起來(lái)很復(fù)雜,但是每一個(gè)步驟都是在 shell 的內(nèi)存里發(fā)生的,shell 不會(huì)真的把每個(gè)步驟的發(fā)生演示給我們看。所以這是我們分析 shell 內(nèi)存的情況,從而知道每個(gè)階段的命令行是如何被轉(zhuǎn)換的。

案例:

mkdir /tmp/x #建立臨時(shí)性目錄
cd /tmp/x #切換到該目錄
touch f1 f2 #建立文件
f=f y=”a b” #賦值兩個(gè)變量
echo ~+/${f}[12] $y $(echo cmd subst) $((3+2))>out      #忙碌的命令

上述命令的執(zhí)行步驟:

1 命令一開(kāi)始會(huì)根據(jù) shell 語(yǔ)法而分割為 token。最重要的一點(diǎn)是:I/O 重定向 >out 在這里是被識(shí)別的,并存儲(chǔ)供稍后使用。流程繼續(xù)處理下面這行,其中每個(gè) token 的范圍顯示于命令下方的行上:

echo ~+/${f}[12] $y $(echo cmd subst) $((3 + 2)) 
| 1 | |----- 2 ----| |3 | |-------- 4----------| |----5-----|

2 堅(jiān)持第一個(gè)單詞 (echo) 是否為關(guān)鍵字,例如 if 或 for。在這里不是,所以命令行不變繼續(xù)執(zhí)行。

3 堅(jiān)持第一個(gè)單詞 (依然是 echo) 是否為別名。這里不是,所以命令行不變繼續(xù)處理。

4 掃描所有單詞是否需要波浪號(hào)展開(kāi)。在這里 ~+ 等同于 $PWD,也就是當(dāng)前目錄。token2 將被修改,處理繼續(xù)如下:

echo /tmp/x/${f}[12] $y $(echo cmd subst) $((3 + 2)) 
| 1 | |------- 2 -------| |3 | |-------- 4----------| |----5-----|

5 變量展開(kāi):token2 與 3 都被修改。產(chǎn)生:

echo /tmp/x/${f}[12] a b $(echo cmd subst) $((3 + 2)) 
| 1 | |------- 2 -------| | 3 | |-------- 4----------| |----5-----|

6 處理命令替換。注意,這里可遞歸引用列表里的所有步驟! 在此例中,因?yàn)槲覀円噲D讓所有的東西容易理解,因此命令修改了 token4,結(jié)果:

echo /tmp/x/${f}[12] a b cmd subst $((3 + 2)) 
| 1 | |------- 2 -------| | 3 | |--- 4 ----| |----5-----|

7 執(zhí)行算術(shù)替換 。修改 token5,結(jié)果:

echo /tmp/x/${f}[12] a b cmd subst 5 
| 1 | |------- 2 -------| | 3 | |--- 4 ----| |5|

8 前面所有的展開(kāi)產(chǎn)生的結(jié)果,都將再一次被掃描,看看是否有 $IFS 字符。如果有,則他們是作為分隔符,產(chǎn)生額外的單詞。例如,兩個(gè)字符 $y 原來(lái)是組成一個(gè)單詞,但展開(kāi)式 “a 空格 b”,在此階段被切分為兩個(gè)單詞:a 與 b。相同方式也應(yīng)用于命令替換 $(echo cmd subst) 的結(jié)果上。先前的 token3 變成了 token3 與 4。先前的 token4 則成了 token5 與 6。結(jié)果:

echo /tmp/x/${f}[12] a b cmd subst 5 
| 1 | |------- 2 -------| 3 4 |-5-| |- 6 -| 7

9 通配符展開(kāi)。token2 變成了 token2 與 token3:

echo /tmp/x/$f1 /tmp/x/$f2 a b cmd subst 5 
| 1 | |---- 2 ----| |---- 3 ----| 4 5 |-6-| |- 7 -| 8

10 這時(shí),shell 已經(jīng)準(zhǔn)備好了要執(zhí)行最后的命令了,他會(huì)去尋找 echo。正好 ksh93 與 bash 的 echo 都內(nèi)建到 shell 中了

11 shell 實(shí)際執(zhí)行命令。首先執(zhí)行 >out 的 I/O 重定向,在調(diào)用內(nèi)部的 echo 版本,顯示最后的參數(shù)。

最后的結(jié)果:

$cat out 
/tmp/x/f1 /tmp/x/f2 a b cmd subst 5

eval 語(yǔ)句

shell 中的 eval 這個(gè)命令很神奇,他能把字符串當(dāng)做命令來(lái)執(zhí)行。PS:這個(gè)字符串必須是可執(zhí)行的 bash 命令才可以。

案例:

eval "ls" #輸出當(dāng)前目錄的所有文件

語(yǔ)法:eval [參數(shù)]

補(bǔ)充說(shuō)明:eval 可讀取一連串的參數(shù),然后再依慘呼本身的特性來(lái)執(zhí)行。

參數(shù):不限數(shù)目,彼此之間用分號(hào)隔開(kāi)。

案例:我有一個(gè)文件 test.txt
命令:cat test.txt
輸出:hello world
命令:myfile="cat test.txt"
命令:echo $myfile
輸出:cat test.txt
命令:eval $myfile
輸出:hello world

eval $myfile 這條命令可以看出,eval 進(jìn)行了變量替換,將字符串中屬于 bash 的命令執(zhí)行了。把拼接起來(lái)的字符串當(dāng)作命令執(zhí)行,這就是 eval 的神奇之處。

subShell 與代碼塊

subShell 是一群被括在圓括號(hào)里的命令,這些命令會(huì)在另外的進(jìn)程中執(zhí)行。當(dāng)你需要讓一小組的命令在不同的目錄下執(zhí)行時(shí),這種方式可以讓你不必修改主腳本的目錄,直接處理這種情況。

例如:tar -cf -.| (cd /tmp;tar -xpf -)

左邊的 tar 命令會(huì)產(chǎn)生當(dāng)前目錄的 tar 打包文件,將他傳送給標(biāo)準(zhǔn)輸出。這份打包文件會(huì)通過(guò)管道傳遞給走遍的 subShell 里的命令。開(kāi)頭的 cd 命令會(huì)先切換到新目錄,也就是讓大寶文件在此目錄下解開(kāi)。然后,走遍的 tar 將從打包文件中解開(kāi)文件。注意,執(zhí)行此管道的 shell(或腳本) 并未更改他的目錄。

代碼塊概念上與 subShell 雷同,只不過(guò)他不會(huì)建立新的進(jìn)程。代碼塊里的命令以花括號(hào) ({}) 括起來(lái),且對(duì)主腳本的狀態(tài)會(huì)造成影響 (例如他的當(dāng)前目錄)。一般來(lái)說(shuō),花括號(hào)被視為 shell 關(guān)鍵字,意即他們只有出現(xiàn)在命令的第一個(gè)符號(hào)時(shí)會(huì)被識(shí)別。實(shí)際上:這表示你必須將結(jié)束花括號(hào)放置在換行字符或分號(hào)之后。例如:

cd /home/directory||{
echo could not change to /home/directory!>&2
echo you lose !>&2
exit1
}

IO 重定向也可以套用 subShell 與代碼塊里。在該情況下,所有的命令會(huì)從重定向來(lái)源讀取它們的輸入或傳送他們的輸出。

subShell 與代碼塊

結(jié)構(gòu)
定界符
認(rèn)可的位置
另外的進(jìn)程
SubShell
()
行上的任何位置
是
代碼塊
{}
在換行字符,分號(hào)或關(guān)鍵字之后
否

注意:代碼塊里的 exit 會(huì)終止整個(gè)腳本。

我們通常在 shell 中運(yùn)行一個(gè)腳本只需要簡(jiǎn)單的調(diào)用 ./[script_name] 即可,這種方式下,shell 會(huì)啟動(dòng)一個(gè)子進(jìn)程來(lái)運(yùn)行該腳本,稱為 subShell,當(dāng) subShell 運(yùn)行完成,子進(jìn)程結(jié)束。父進(jìn)程的環(huán)境不會(huì)有任何改變。

案例:bash 代碼

\#!/bin/bash
cd /var/cache
testname="fine"

分別在 shell 中運(yùn)行

  1. ./test.sh;echo $testname 會(huì)發(fā)現(xiàn)還是位于原來(lái)的目錄中,$testname 的值書粗話為 null。
  2. source ./test.sh;echo $testname 這里就不一樣了,現(xiàn)在你位于 /var/cache 中,$testname 的值也變成了 fine

source 命令來(lái)運(yùn)行腳本,不會(huì)產(chǎn)生子進(jìn)程,腳本在 shell 的進(jìn)程空間中執(zhí)行,所以運(yùn)行重定義的變量,執(zhí)行的操作,都會(huì)在 shell 的運(yùn)行環(huán)境中保留下來(lái)。