主頁(yè) > 知識(shí)庫(kù) > Linux折騰記(十):Bash腳本編程語(yǔ)言中的美學(xué)與哲學(xué)

Linux折騰記(十):Bash腳本編程語(yǔ)言中的美學(xué)與哲學(xué)

熱門標(biāo)簽:新鄭電銷外呼系統(tǒng)線路 樺甸電銷機(jī)器人 河北語(yǔ)音電銷機(jī)器人 商戶地圖標(biāo)注 電話機(jī)器人哪里有賣的 地球地圖標(biāo)注方法 怎樣給景區(qū)加百度地圖標(biāo)注 河南智能電話機(jī)器人公司 壽光百度地圖標(biāo)注中心網(wǎng)站

  我承認(rèn),我再一次地當(dāng)了標(biāo)題黨。但是不可否認(rèn),這一定是一篇精華隨筆。在這一篇中,我將探討B(tài)ash腳本語(yǔ)言中的美學(xué)與哲學(xué)。 這不是一篇Bash腳本編程的教程,但是卻能讓人更加深入地了解Bash腳本編程,更加快速地學(xué)習(xí)Bash腳本編程。 閱讀這篇隨筆,不需要你有Bash編程的經(jīng)驗(yàn),但一定要和我一樣熱衷于探索各種編程語(yǔ)言的本質(zhì),感悟它們的魅力。

  其實(shí)早就想寫關(guān)于Bash的東西了。 我們平時(shí)喜歡對(duì)編程語(yǔ)言進(jìn)行分類,比如面向過(guò)程的編程語(yǔ)言、面向?qū)ο蟮木幊陶Z(yǔ)言、函數(shù)式編程語(yǔ)言等等。在我心中,我認(rèn)為Bash就是一個(gè)面向字符串的編程語(yǔ)言。Bash腳本語(yǔ)言的本質(zhì):一切皆是字符串。 Bash腳本語(yǔ)言的一切哲學(xué)都圍繞著字符串:它們從哪里來(lái)?到哪里去?使命是什么? Bash腳本語(yǔ)言的一切美學(xué)都源自字符串: 由鍵盤上幾乎所有的符號(hào) “$ ~ ! # ( ) [ ] { } | > - . , ; * @ ' " ` \ ^” 排列組合而成的極富視覺(jué)沖擊力的、功能極其復(fù)雜的字符串。

一、一切皆是字符串

  Bash是一個(gè)Shell,Shell出現(xiàn)的初衷是為了將系統(tǒng)中的各種工具粘合在一起,所以它最根本的功能是調(diào)用各種命令。 但是Bash又提供了豐富的編程功能。 我們經(jīng)常對(duì)編程語(yǔ)言進(jìn)行分類,比如面向過(guò)程的語(yǔ)言、面向?qū)ο蟮恼Z(yǔ)言、面向函數(shù)的語(yǔ)言等等。 可以把Bash腳本語(yǔ)言看成是一個(gè)面向字符串的語(yǔ)言。 Bash語(yǔ)言的本質(zhì)就是:一切都是字符串。 看看下圖中的這些變量:

  上圖是我在交互式的Bash命令行中做的一些演示。在上圖中,我對(duì)變量分別賦值,不管等號(hào)右邊是一個(gè)沒(méi)有引號(hào)的字符串,還是帶有引號(hào)的字符串,甚至數(shù)字,或者數(shù)學(xué)表達(dá)式,最終的結(jié)果,變量里面存儲(chǔ)的都是字符串。我使用一個(gè)for循環(huán)顯示所有的變量,可以看到數(shù)學(xué)表達(dá)式也只是以字符串的形式儲(chǔ)存,沒(méi)有被求值。

二、引用和元字符

  如果一切都是沒(méi)有特殊功能的平凡的字符串,那就無(wú)法構(gòu)成一門編程語(yǔ)言。在Bash中,有很多符號(hào)具有特殊含義,比如“$”符號(hào)被用于字符串展開,“”符號(hào)用于讓命令在后臺(tái)執(zhí)行, “|”用作管道, “>” “”用于輸入輸出重定向等等。所以在Bash中,雖然同樣是字符串,但是被引號(hào)包圍的字符串和不被引號(hào)包圍的字符串使用起來(lái)是不一樣的,被單引號(hào)包圍的字符串和被雙引號(hào)包圍起來(lái)的字符串也是不一樣的。

  究竟帶引號(hào)的字符串和不帶引號(hào)的字符串使用起來(lái)有什么不一樣呢?下圖是我構(gòu)建的一些比較典型的例子:

  在上圖中,我展示了Bash中生成字符串的7種方法:大括號(hào)展開、波浪符展開、參數(shù)展開、命令替換、算術(shù)展開、單詞分割和文件路徑展開。還有兩種生成字符串的方式?jīng)]有講(Process substitution和歷史命令展開)。在使用Bash腳本編程的時(shí)候,了解以上7種字符串生成的方式就夠了。在交互式使用Bash命令行的時(shí)候,還需要了解歷史命令展開,熟練使用歷史命令展開可以讓人事半功倍。

  在上面的圖片中可以看到,有一些展開方式在被雙引號(hào)包圍的字符串中是不起作用的,比如大括號(hào)展開、波浪符展開、單詞分割、文件路徑展開,而只有參數(shù)展開、命令替換和算術(shù)展開是起作用的。從圖片中還可以看出,字符串中的參數(shù)展開、命令替換和算術(shù)展開都是由“$”符號(hào)引導(dǎo),命令替換還可以由“`”引導(dǎo)。所以,可以進(jìn)一步總結(jié)為,在雙引號(hào)包圍的字符串中,只有“$、`、\&;這三個(gè)字符具有特殊含義。

  如果想讓任何一個(gè)字符都不具有特殊含義,可以使用單引號(hào)將字符串包圍。比如使用正則表達(dá)式的時(shí)候,還比如使用sed、awk等工具的時(shí)候,由于sed和awk自己執(zhí)行的命令中往往包含有很多特殊字符,所以它們的命令最好用單引號(hào)包圍。 比如使用awk命令顯示/etc/passwd文件中的每個(gè)用戶的用戶名和全名,可以使用這個(gè)命令 awk -e '{print $1,$5}' ,其中,傳遞給awk的命令用單引號(hào)包圍,說(shuō)明bash不執(zhí)行其中的任何替換或展開。

  另外一個(gè)特殊的字符是“\&;,它也是引用的一種。它可以解除緊跟在它后面的一個(gè)特殊字符的特殊含義(引用)。之所以需要“\&;的存在,是因?yàn)樵贐ash中,有些字符稱為元字符,這些字符一旦出現(xiàn),就會(huì)將一個(gè)字符串分割為多個(gè)子串。如果需要在一個(gè)字符串中包含這些元字符本身,就必須對(duì)它們進(jìn)行引用。如下圖:

  最常見(jiàn)的元字符就是空格。 從上面幾張圖片可以看出,如果要將一個(gè)含有空格的字符串賦值給一個(gè)變量,要么把這個(gè)字符串用雙引號(hào)包圍,要么使用“\&;對(duì)空格進(jìn)行引用。 從上圖中可以看出,Bash中只有9個(gè)元字符,它們分別是“| ( ) ; > space tab”,而在其它編程語(yǔ)言中經(jīng)常出現(xiàn)的元字符“. { } [ ]”以及作為數(shù)學(xué)運(yùn)算的加減乘除,在Bash中都不是元字符。

三、字符串從哪里來(lái)、到哪里去

  介紹完字符串、介紹完引用和元字符,下一個(gè)目標(biāo)就是來(lái)探討這一個(gè)哲學(xué)問(wèn)題:字符串從哪里來(lái)、到哪里去?通過(guò)該哲學(xué)問(wèn)題的探討,可以推導(dǎo)出Bash腳本語(yǔ)言的整個(gè)語(yǔ)法。字符串從哪里來(lái)?很顯然,其中一個(gè)很直接的來(lái)源就是我們從鍵盤上敲上去的。除此之外,就是我前面提到的七八九種字符串展開的方法了。

  字符串展開的流程如下:

    1.先用元字符將一個(gè)字符串分割為多個(gè)子串;

    2.如果字符串是用來(lái)給變量賦值,則不管它是否被雙引號(hào)包圍,都認(rèn)為它被雙引號(hào)包圍;

    3.如果字符串不被單引號(hào)和雙引號(hào)包圍,則進(jìn)行大括號(hào)展開,即將{a,b}c展開為ab ac;

以上三個(gè)流程可以通過(guò)下圖證明:

    4.如果字符串不被單引號(hào)或雙引號(hào)包圍,則進(jìn)行波浪符展開,即將~/展開為用戶的主目錄,將~+/展開為當(dāng)前工作目錄(PWD),將~-/展開為上一個(gè)工作目錄(OLDPWD);

    5.如果字符串不被單引號(hào)包圍,則進(jìn)行參數(shù)和變量展開;這一類的展開全都以“$”開頭,這是整個(gè)Bash字符串展開中最復(fù)雜的,其中包括用戶定義的變量,包括所有的環(huán)境變量,以上兩種展開方式都是“$”后跟變量名,還包括位置變量“$1、 $2、 ...、 $9、 ... ”,其它特殊變量:“$@、 $*、 $#、 $-、 $!、 $0、 $?、 $_ ”,甚至還有數(shù)組:“${var[i]}”, 還可以在展開的過(guò)程中對(duì)字符串進(jìn)行各種復(fù)雜的操作,如:“ ${parameter:-word}、 ${parameter:=word}、 ${parameter:+word}、 ;${parameter:?word}、 ${parameter:offset}、 ${parameter:offset:length}、 ${!prefix*}、 ${!prefix@}、 ${name[@]}、 ${!name[*]}、 ${#parameter}、 ${parameter#word}、 ${parameter##word}、 ${parameter%word}、 ${parameter%%word}、 ${parameter/pattern/string}、 ${parameter^pattern}、 ${parameter^^pattern}、 ${parameter,pattern}、 ${parameter,,pattern}”;

    6.如果字符串不被單引號(hào)包圍,則進(jìn)行命令替換;命令替換有兩種格式,一種是$(...),一種是`...`;也就是將命令的輸出作為字符串的內(nèi)容;

    7.如果字符串不被單引號(hào)包圍,則進(jìn)行算術(shù)展開;算術(shù)展開的格式為$((...));

    8.如果字符串不被單引號(hào)或雙引號(hào)包圍,則進(jìn)行單詞分割;

    9.如果字符串不被單引號(hào)或雙引號(hào)包圍,則進(jìn)行文件路徑展開;

    10.以上流程全部完成后,最后去掉字符串外面的引號(hào)(如果有的話)。以上流程只按以上順序進(jìn)行一遍。比如不會(huì)在變量展開后再進(jìn)行大括號(hào)展開,更不會(huì)在第10步去除引用后執(zhí)行前面的任何一步。如果需要將流程再走一遍,請(qǐng)使用eval。

  探討完了字符串從哪里來(lái),下面來(lái)看看字符串到哪里去。也就是怎么使用這些字符串。使用字符串有以下幾種方式:

    1.把它當(dāng)命令執(zhí)行;這是Bash中的最根本的用法,畢竟Shell的存在就是為了粘合各種命令。如果一個(gè)字符串出現(xiàn)在本該命令出現(xiàn)的地方(比如一行的開頭,或者關(guān)鍵字then、do等的后面),它將會(huì)被當(dāng)成命令執(zhí)行,如果它不是個(gè)合法的命令,就會(huì)報(bào)錯(cuò);

    2.把它當(dāng)成表達(dá)式;Bash中本沒(méi)有表達(dá)式,但是有了((...))和[[...]],就有了表達(dá)式;((...))可以把它里面的字符串當(dāng)成算術(shù)表達(dá)式,而[[...]]會(huì)把它里面的字符串當(dāng)邏輯表達(dá)式,僅此兩個(gè)特例;

    3.給變量賦值;這也是一個(gè)特例,有點(diǎn)破壞Bash編程語(yǔ)言語(yǔ)法哲學(xué)的完整性。為什么這么說(shuō)呢?因?yàn)?ldquo;=”即不是一個(gè)元字符,也不允許兩邊有空格,而且只有第1個(gè)等號(hào)會(huì)被當(dāng)成賦值運(yùn)算符。

  下面圖片為以上觀點(diǎn)給出證據(jù):

四、再加上一點(diǎn)點(diǎn)的定義,就可以推導(dǎo)出整個(gè)Bash腳本語(yǔ)言的語(yǔ)法了

  前面我已經(jīng)展示了我對(duì)字符串從哪里來(lái)、到哪里去這個(gè)問(wèn)題的理解。關(guān)于字符串的去向,除了兩個(gè)表達(dá)式和一個(gè)為變量賦值這三個(gè)特例,剩下的就只有當(dāng)命令來(lái)執(zhí)行了。在前面,我提到了元字符和引用的概念,這里,還得再增加一點(diǎn)點(diǎn)定義:

    定義1:控制操作符(Control Operator) 前面提到元字符是為了把一個(gè)字符串分割為多個(gè)子串,而控制操作符就是為了把一系列的字符串分割成多個(gè)命令。舉例說(shuō)明,在Bash中,一個(gè)字符串 cat /etc/passwd 就是一個(gè)命令,第一個(gè)單詞cat是命令,第2個(gè)單詞/etc/passwd是命令的參數(shù),而字符串 cat /etc/passwd | grep youxia 就是兩個(gè)命令,這兩個(gè)命令分別是cat和grep,它們之間通過(guò)“|”分割,所以這里的“|”是控制操作符。熟悉Shell的朋友肯定知道“|”代表的是管道,所以它的作用是1.把一個(gè)字符串分割為兩個(gè)命令,2.將第一個(gè)命令的輸出作為第二個(gè)命令的輸入。在Bash中,總共只有10個(gè)控制操作符,它們分別是“|| | ; ;; ( ) | newline>”。只要看到這些控制操作符,就可以認(rèn)為它前面的字符串是一個(gè)完整的命令。

    定義2:關(guān)鍵字(Reserved Words) 我沒(méi)有將其翻譯成保留字,很顯然,作為編程語(yǔ)言來(lái)說(shuō),它們應(yīng)該叫做關(guān)鍵字。一門編程語(yǔ)言肯定必須得提供選擇、循環(huán)等流程控制語(yǔ)句,還得提供定義函數(shù)的功能。這些功能只能通過(guò)關(guān)鍵字實(shí)現(xiàn)。在Bash中,只有22個(gè)關(guān)鍵字,它們是“! case coproc do done elif else esac fi for function if in select then until while { } time [[ ]]”。這其中有不少的特別之處,比如“! { } [[ ]]”等符號(hào)都是關(guān)鍵字,也就是說(shuō)它們當(dāng)關(guān)鍵字使用時(shí)相當(dāng)于一個(gè)單詞,也就是說(shuō)它們和別的單詞必須以元字符分開(否則無(wú)法成為獨(dú)立的單詞)。這也是為什么在Bash中使用“! { } [[ ]]”時(shí)經(jīng)常要在它們周圍留空格的原因。(再一次證明“=”是一個(gè)很變態(tài)的特例,因?yàn)樗炔皇窃址?,也不是控制操作符,更加不是關(guān)鍵字,它到底是什么?)

  下面開始推導(dǎo)Bash腳本語(yǔ)言的語(yǔ)法:

    推導(dǎo)1:簡(jiǎn)單命令(Simple command) 就是一條簡(jiǎn)單的命令,它可以是一個(gè)以上述控制操作符結(jié)尾的字符串。比如單獨(dú)放在一行的 uname -r 命令(單獨(dú)放在一行的命令其實(shí)是以newline>結(jié)尾,newline>是控制操作符),或者雖然不單獨(dú)放在一行,但是以“;”或“”結(jié)尾,比如 uname -r; who; pwd; gvim 其中每一個(gè)命令都是一個(gè)簡(jiǎn)單命令(當(dāng)然,這四個(gè)命令放在一起的這行代碼不叫簡(jiǎn)單命令),“;”就是簡(jiǎn)單地分割命令,而“”還有讓命令在后臺(tái)執(zhí)行的功能。這里比較特殊的是雙分號(hào)“;;”,它只用在case語(yǔ)句中。

    推導(dǎo)2:管道(Pipe Line) 管道是Shell中的精髓,就是讓前一個(gè)命令的輸出成為后一個(gè)命令的輸入。管道的完整語(yǔ)法是這樣 [time [-p]] [ ! ] command1 | command2 或這樣 [time [-p]] [ ! ] command1 | command2 的。其中time關(guān)鍵字和!關(guān)鍵字都是可選的(使用[...]指出哪些部分是可選的),time關(guān)鍵字可以計(jì)算命令運(yùn)行的時(shí)間,而!關(guān)鍵字是將命令的返回狀態(tài)取反??辞宄?關(guān)鍵字周圍的空格哦。如果使用“|”,就是把第一個(gè)命令的標(biāo)準(zhǔn)輸出作為第二個(gè)命令的標(biāo)準(zhǔn)輸入,如果使用“|”,則將第一個(gè)命令的標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出都當(dāng)成第二個(gè)命令的輸入。

    推導(dǎo)3:命令序列(List) 如果多個(gè)簡(jiǎn)單命令或多個(gè)管道放在一起,它們之間以“; newline> || ”等控制操作符分開,就稱之為一個(gè)命令序列。關(guān)于“; newline>”前面已經(jīng)講過(guò)了,無(wú)需重復(fù)。關(guān)于“||”和“”,熟悉C、C++、Java等編程語(yǔ)言的朋友們肯定也不會(huì)陌生,它們遵循同樣的短路求值的思想。比如 command1 || command2 只有當(dāng)command1執(zhí)行不成功的時(shí)候才執(zhí)行command2,而 command1 command2 只有當(dāng)command1執(zhí)行成功的時(shí)候才執(zhí)行command2。

    推導(dǎo)4:復(fù)合命令(Compound Commands) 如果將前面的簡(jiǎn)單命令、管道或者命令序列以更復(fù)雜的方式組合在一起,就可以構(gòu)成復(fù)合命令。在Bash中,有4種形式的復(fù)合命令,它們分別是 (list) 、  { list; } 、  ((expression)) 、  [[ expression ]] 。請(qǐng)注意第2種形式和第4種形式大括號(hào)和中括號(hào)周圍的空格,也請(qǐng)注意第2種形式中l(wèi)ist后面的“;”,不過(guò)如果“}”另起一行,則不需要“;”,因?yàn)閚ewline>和“;”是起同樣作用的。在以上4種復(fù)合命令中, (list) 是在一個(gè)新的Shell中執(zhí)行命令序列,這些命令的執(zhí)行不會(huì)影響當(dāng)前Shell的環(huán)境變量,而 { list; } 只是簡(jiǎn)單地將命令序列分組。后面兩種表達(dá)式求值前面已經(jīng)講過(guò),這里就不講了。后面可能會(huì)詳細(xì)列出邏輯表達(dá)式求值的選項(xiàng)。

  上面的4步推導(dǎo)是一步更進(jìn)一步的,是由簡(jiǎn)單逐漸到復(fù)雜的,最簡(jiǎn)單的命令可以組合成稍復(fù)雜的管道,再組合成更復(fù)雜的命令序列,最后組成最復(fù)雜的復(fù)合命令。

  下面是Bash腳本語(yǔ)言的流程控制語(yǔ)句,如下:

    1. for name [ [ in [ word ... ] ] ; ] do list ; done ;

    2. for (( expr1 ; expr2 ; expr3 )) ; do list ; done ;

    3. select name [ in word ] ; do list ; done ;

    4. case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac ;

    5. if list; then list; [ elif list; then list; ] ... [ else list; ] fi ;

    6. while list-1; do list-2; done ;

    7. until list-1; do list-2; done 。

  上面的公式大家看得懂吧,我相信大家肯定看得懂。其中的[...]代表的是可以有也可以真沒(méi)有的部分。在以上公式中,請(qǐng)注意第2個(gè)公式for循環(huán)中的雙括號(hào),它執(zhí)行的是其中的表達(dá)式的算術(shù)運(yùn)算,這是和其它高級(jí)語(yǔ)言的for循環(huán)最像的,但是很遺憾,Bash中的算術(shù)表達(dá)式目前只能計(jì)算整數(shù)。再請(qǐng)注意第3個(gè)公式,select語(yǔ)法,和for...in...循環(huán)的語(yǔ)法比較類似,但是它可以在屏幕上顯示一個(gè)菜單。如果我沒(méi)有記錯(cuò)的話,Basic語(yǔ)言中應(yīng)該有這個(gè)功能。其它的控制結(jié)構(gòu)在別的高級(jí)語(yǔ)言中都很常見(jiàn),就不需要我在這里啰嗦了。

  最后,再來(lái)展示一下如何定義函數(shù):

     name () compound-command [redirection] 

    或者

     function name [()] compound-command [redirection] 

  可以看出,如果有function關(guān)鍵字,則“()”是可選的,如果沒(méi)有function關(guān)鍵字,則“()”是必須的。這里需要特別指出的是:函數(shù)體只要求是compound-command,我前面總結(jié)過(guò)compound-command有四種形式,所以有時(shí)候定義一個(gè)函數(shù)并不會(huì)出現(xiàn)“{ }”哦。如下圖,這樣的函數(shù)也是合法的:

  That's all。這就是Bash腳本語(yǔ)言的全部語(yǔ)法。就這么簡(jiǎn)單。

  好像忘了點(diǎn)什么?對(duì)了,還有輸入輸出重定向沒(méi)有講。輸入輸出重定向是Shell中又一個(gè)偉大的發(fā)明,它的存在有著它獨(dú)特的哲學(xué)意義。這個(gè)請(qǐng)看下一節(jié)。

五、輸入輸出重定向

  Unix世界有一個(gè)偉大的哲學(xué):一切皆是文件。(這個(gè)扯得有點(diǎn)遠(yuǎn)。)Unix世界還有一個(gè)偉大的哲學(xué):創(chuàng)建進(jìn)程比較方便。(這個(gè)扯得也有點(diǎn)遠(yuǎn)。)而且,每一個(gè)進(jìn)程一創(chuàng)建,就會(huì)自動(dòng)打開三個(gè)文件,它們分別是標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯(cuò)誤輸出,普通情況下,它們連接到用戶的控制臺(tái)。在Shell中,使用數(shù)字來(lái)標(biāo)識(shí)一個(gè)打開的文件,稱為文件描述符,而且數(shù)字0、 1、 2分別代表標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出。在Shell中,可以通過(guò)“>”、“”將命令的輸入、輸出進(jìn)行重定向。結(jié)合exec命令,可以非常方便地打開和關(guān)閉文件。需要注意的是,當(dāng)文件描述符出現(xiàn)在“>”、“”右邊的時(shí)候,前面要使用“”符號(hào),這可能是為了和數(shù)學(xué)表達(dá)式中的大于和小于進(jìn)行區(qū)別吧。使用“-”可以關(guān)閉文件描述符。

  “> 數(shù)字 exec -”,這就是輸入輸出重定向的全部。下面的公式中,我使用n代表數(shù)字,如果是兩個(gè)不同的數(shù)字,則使用n1、n2,使用[...]代表可選參數(shù)。輸入輸出重定向的語(yǔ)法如下:


復(fù)制代碼
代碼如下:

[n]> file #重定向標(biāo)準(zhǔn)輸出(或 n)到file。
[n]>> file #重定向標(biāo)準(zhǔn)輸出(或 n)到file,追加到file末尾。
[n] file #將file重定向到標(biāo)準(zhǔn)輸入(或 n)。
[n1]>n2 #重定向標(biāo)準(zhǔn)輸出(或 n1)到n2。
2> file >2 #重定向標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出到file。
| command #將標(biāo)準(zhǔn)輸出通過(guò)管道傳遞給command。
2>1 | command #將標(biāo)準(zhǔn)輸出和錯(cuò)誤輸出一起通過(guò)管道傳遞給command,等同于|。

  請(qǐng)注意,數(shù)字和“>”、“”符號(hào)之間是沒(méi)有空格的。結(jié)合exec,可以非常方便地使用一個(gè)文件描述符來(lái)打開、關(guān)閉文件,如下:


復(fù)制代碼
代碼如下:

echo Hello >file1
exec 3file1 4>file2 #打開文件
cat 3 >4 #重定向標(biāo)準(zhǔn)輸入到 3,標(biāo)準(zhǔn)輸出到 4,相當(dāng)于讀取file1的內(nèi)容然后寫入file2
exec 3- 4>- #關(guān)閉文件
cat file2
#顯示結(jié)果為 Hello/p> p>#還可以暫存和恢復(fù)文件描述符,如下:
exec 5>2 #把原來(lái)的標(biāo)準(zhǔn)錯(cuò)誤輸出保存到文件描述符5上
exec 2> /tmp/$0.log #重定向標(biāo)準(zhǔn)錯(cuò)誤輸出
...
exec 2>5 #恢復(fù)標(biāo)準(zhǔn)錯(cuò)誤輸出
exec 5>- #關(guān)閉文件描述符5,因?yàn)椴恍枰?br />

  還可以將“>”一起使用,表示打開一個(gè)文件進(jìn)行讀寫。

  除了exec,輸入輸出重定向和read命令配合也很好用,read命令每次讀取文件的一行。但是要注意的是,輸入輸出重定向放到for、while等循環(huán)的循環(huán)體和循環(huán)外,效果是不一樣的。如下圖:

  另外,輸入輸出重定向符號(hào)“>”、“”還可以和“()”一起使用,表示進(jìn)程替換(Process substitution),如“>(list)”、“(list)”。結(jié)合前面提到的“”、“>”、“(list)”的含義,進(jìn)程替換的作用是很容易猜到的哦。

六、Bash腳本語(yǔ)言的美學(xué):大道至簡(jiǎn)

  如果你問(wèn)我Bash腳本語(yǔ)言哪里美?我會(huì)回答:簡(jiǎn)潔就是美。請(qǐng)看下面逐條論述:

  1.使用了簡(jiǎn)潔的抽象的符號(hào)。Bash腳本語(yǔ)言幾乎使用到了鍵盤上能夠找到的所有符號(hào),比如“$”用作字符串展開,“|”用作管道,“”、“>”用作輸入輸出重定向,一點(diǎn)都不浪費(fèi);

  2.只使用了9個(gè)元字符、10個(gè)控制操作符和22個(gè)關(guān)鍵字,就構(gòu)建了一個(gè)完整的、面向字符串編程的語(yǔ)言;

  3.概念上具有很好的一致性;比如 (list) 復(fù)合命令的功能是執(zhí)行括號(hào)內(nèi)的命令序列,而“$”用于引導(dǎo)字符串展開,所以 $(list) 用于命令替換(所以我前面說(shuō)“$()”形式的命令替換比“``”形式的命令替換更加具有一致性)。比如 ((expresion)) 用于數(shù)學(xué)表達(dá)式求值,所以 $((expression)) 代表算術(shù)展開。再比如“{}”和“,”配合使用,且中間沒(méi)有空格時(shí),代表大括號(hào)展開,但是當(dāng)需要使用“{ }”來(lái)定義復(fù)合命令時(shí),必須把“{ }”當(dāng)關(guān)鍵字,它們和它里面的內(nèi)容必須以空格隔開,而且“}”和它前面的一條命令之間必須有一個(gè)“;”或者“newline>”。這些概念上的一致性設(shè)計(jì)得非常精妙,使用起來(lái)自然而然可以讓人體會(huì)到一種美感;

  4.完美解決了一個(gè)命令執(zhí)行時(shí)的輸出和運(yùn)行狀態(tài)的分離。有其它編程語(yǔ)言經(jīng)歷的人也經(jīng)常會(huì)遇到這樣的問(wèn)題:當(dāng)我們調(diào)用一個(gè)函數(shù)的時(shí)候,函數(shù)可能會(huì)產(chǎn)生兩個(gè)結(jié)果,一個(gè)是函數(shù)的返回值,一個(gè)是函數(shù)調(diào)用是否成功。在C#和Java等高級(jí)語(yǔ)言中,往往使用try...catch等捕獲異常的方式來(lái)判斷函數(shù)調(diào)用是否成功,但仍然有程序員讓函數(shù)返回null代表失敗,而C語(yǔ)言這種沒(méi)有異常機(jī)制的語(yǔ)言,實(shí)在是難以判斷一個(gè)函數(shù)的返回值究竟如何表示該函數(shù)調(diào)用是否成功(比如就有很多API讓函數(shù)返回-1代表失敗,而有的函數(shù)運(yùn)行失敗是會(huì)設(shè)置errno全局變量)。在Bash中,命令運(yùn)行的狀態(tài)和命令的標(biāo)準(zhǔn)輸出區(qū)分很明確,如果你需要命令的標(biāo)準(zhǔn)輸出,使用命令替換來(lái)生成字符串,如果你只需要命令的運(yùn)行狀態(tài),直接將命令寫在if語(yǔ)句之中即可,或者使用$?特殊變量來(lái)檢查上一條命令的運(yùn)行狀態(tài)。如果不想在檢查命令運(yùn)行狀態(tài)的時(shí)候讓命令的標(biāo)準(zhǔn)輸出影響用戶,可以把它重定向到/dev/null,比如


復(fù)制代碼
代碼如下:

if cat /etc/passwd | grep youxia > /dev/null; then echo 'youxia is exist'; fi

  5.使用管道和輸入輸出重定向讓文件的讀寫變得簡(jiǎn)單。想一想在C語(yǔ)言中怎么讀文件吧,除了麻煩的open、close不說(shuō),每讀一個(gè)字符串還得先準(zhǔn)備一個(gè)buffer,準(zhǔn)備長(zhǎng)了怕浪費(fèi)空間,準(zhǔn)備短了怕緩沖區(qū)溢出,虐心啦。使用Bash,那真的是太方便了。

  6.它還是一門不折不扣的動(dòng)態(tài)語(yǔ)言哦,eval命令實(shí)在是太強(qiáng)大了,請(qǐng)看下圖,模擬指針進(jìn)行查表:

  當(dāng)然,自從Bash 3之后,Bash本身就提供了間接引用的功能(使用“${!var}”)。

  例外:

    Bash語(yǔ)言也并不是在所有的方面都是完美的,還存在幾個(gè)特別的例外。比如前面說(shuō)的“=”。除了“=”,“()”也有一個(gè)使用不一致的地方,那就是對(duì)數(shù)組的初始化,比如 array=(a b c d e f) ,這和前面講的“()”用于在子Shell中執(zhí)行命令序列還真的是不一致。

總結(jié):

  以上內(nèi)容是我的胡言亂語(yǔ),因?yàn)橐陨蟽?nèi)容即無(wú)法教會(huì)大家完整的Bash語(yǔ)法,也無(wú)法教會(huì)大家用Bash做任何一點(diǎn)有意義的工作。

  如果想用Bash干點(diǎn)實(shí)事,我送大家一本O'Reilly出的《Shell腳本學(xué)習(xí)指南》

  另外,我的主要參考資料來(lái)自于Bash用戶手冊(cè)。大家可以在自己的系統(tǒng)中運(yùn)行 man bash 。

標(biāo)簽:荊州 楚雄 來(lái)賓 忻州 阜陽(yáng) 淄博 迪慶 遼陽(yáng)

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Linux折騰記(十):Bash腳本編程語(yǔ)言中的美學(xué)與哲學(xué)》,本文關(guān)鍵詞  Linux,折騰,記,十,Bash,腳本,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Linux折騰記(十):Bash腳本編程語(yǔ)言中的美學(xué)與哲學(xué)》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于Linux折騰記(十):Bash腳本編程語(yǔ)言中的美學(xué)與哲學(xué)的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章