<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      標(biāo)簽列表

      everest33

      自制力

      導(dǎo)航

      Linux命令行與shell腳本編程大全第三版 shell腳本專題 學(xué)習(xí)筆記(二)

      ※,平時(shí)記錄 

      獲取當(dāng)前腳本所在目錄的絕對(duì)路徑:·basepath=$(cd $(dirname $0); pwd)·

      第二部分:shell腳本編程基礎(chǔ) 

      shell官方文檔(最權(quán)威):https://www.gnu.org/software/bash/manual/bash.html 注意使用代理可能打不開(kāi)!

      手冊(cè)性教程:

      http://c.biancheng.net/view/706.html

      shell在線中文手冊(cè)abs,shell中文教程,shell中文教程 

      ※,多行注釋:

      :'
      這里是多行注釋,經(jīng)測(cè)試這種方式還是會(huì)被解析
      '
      <<! 這個(gè)!可以是任意字符(也可以是單詞如LongComment),下面對(duì)應(yīng)起來(lái)即可
      這里是另一種多行注釋
      !

      ※,shell,exec,source執(zhí)行腳本的區(qū)別:參考此文。可以在腳本中打印當(dāng)前的進(jìn)程號(hào)`$$`來(lái)驗(yàn)證。

      • 使用$ sh script.sh執(zhí)行腳本時(shí),當(dāng)前shell是父進(jìn)程,生成一個(gè)子shell進(jìn)程,在子shell中執(zhí)行腳本。腳本執(zhí)行完畢,退出子shell,回到當(dāng)前shell。$ ./script.sh$ sh script.sh等效。
      • 使用$ source script.sh方式,在當(dāng)前上下文中執(zhí)行腳本,不會(huì)生成新的進(jìn)程。腳本執(zhí)行完畢,回到當(dāng)前shell。source方式也叫點(diǎn)命令,$ . script.sh$ source script.sh等效。
      • 使用exec command方式,會(huì)用command進(jìn)程替換當(dāng)前shell進(jìn)程,并且保持PID不變。執(zhí)行完畢,直接退出,不回到之前的shell環(huán)境。所以exec命令一般放在shell腳本里執(zhí)行。
        • 一個(gè)例外,當(dāng)exec命令來(lái)對(duì)文件描述符操作的時(shí)候,就不會(huì)替換shell,而且操作完成后,還會(huì)繼續(xù)執(zhí)行接下來(lái)的命令。 ·exec 3<&0·這個(gè)命令就是將操作符3也指向標(biāo)準(zhǔn)輸入

      ※,shell腳本的追蹤與debug:

      • ·bash -n myfile.sh·// -n參數(shù):不執(zhí)行腳本,僅檢查語(yǔ)法問(wèn)題。
      • ·bash -v myfile.sh·// -v參數(shù):在執(zhí)行腳本前,先將腳本內(nèi)容輸出到屏幕上(并不是一次性輸出全部?jī)?nèi)容,而是執(zhí)行一個(gè)命令輸出一次)。這個(gè)參數(shù)和-x參數(shù)差不多,但是沒(méi)有-x參數(shù)清晰(有個(gè)+號(hào)區(qū)分源碼和執(zhí)行結(jié)果)
      • ·bash -x myfile.sh· // -x參數(shù):將使用到的 script 內(nèi)容顯示到屏幕上(也是執(zhí)行一個(gè)命令輸出一次),這是很有用的參數(shù)!
        • 輸出中的+號(hào)后面的內(nèi)容是腳本源碼內(nèi)容。不同級(jí)別的源碼內(nèi)容前面的+個(gè)數(shù)不同,第一級(jí)一個(gè)+號(hào),第二級(jí)兩個(gè)+號(hào),以此類推。
        • 這個(gè)bash -x  就是通過(guò) set -x (即在shell腳本中shebang之后添加一行代碼:set -x)實(shí)現(xiàn)的...!!

      ※,`set`命令

      https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html官方文檔

      set [-abefhkmnptuvxBCEHPT] [-o option-name] [--] [-] [argument …]
      set [+abefhkmnptuvxBCEHPT] [+o option-name] [--] [-] [argument …]
      • ·+·號(hào)表示關(guān)閉功能,·-·號(hào)表示開(kāi)啟功能。
      • `set -o <command>` // 啟用command命令,如`set -o history`表示啟用命令的history記錄功能,history這個(gè)命令功能在交互式shell中默認(rèn)是開(kāi)啟的。
      • ·set +o <command>· // 關(guān)閉command命令,如·set +o history·表示關(guān)閉history日志記錄功能,這在外部黑客入侵機(jī)器時(shí)經(jīng)常會(huì)使用這個(gè)命令。
      • `set -e` // 等同于·set -o errexit·,腳本出錯(cuò)立即退出。使用方法:在shell腳本的shebang之后添加一行代碼:`set -e`。對(duì)于管道set -e失效,需要使用:·set -o pipefail·
      • 官方文檔中講到,set -e對(duì) command1 && command2這種形式不生效(除非出錯(cuò)的是最后一個(gè)命令...)。如果command1出錯(cuò),程序還會(huì)繼續(xù)運(yùn)行;但最后一個(gè)命令出錯(cuò)程序會(huì)退出。可以把這個(gè)命令拆成兩行來(lái)寫(xiě)就解決問(wèn)題了。
      • 如果函數(shù)返回非0(false),那么set -e將使程序在調(diào)用函數(shù)處退出,如果函數(shù)返回了0(true),那么程序?qū)⒗^續(xù)運(yùn)行。
      • set -e有一個(gè)例外情況,就是不適用于管道命令。解決方法:·set -o pipefail·
        所謂管道命令,就是多個(gè)子命令通過(guò)管道運(yùn)算符(|)組合成為一個(gè)大的命令。Bash 會(huì)把最后一個(gè)子命令的返回值,作為整個(gè)命令的返回值。也就是說(shuō),只要最后一個(gè)子命令不失敗,管道命令總是會(huì)執(zhí)行成功,因此它后面命令依然會(huì)執(zhí)行,set -e就失效了。
      • 函數(shù)調(diào)用并不會(huì)產(chǎn)生子shell,可以自己驗(yàn)證(通過(guò)ps --forest,通過(guò) $$, $PPID等等可以驗(yàn)證)

      ※,使用腳本添加crontab任務(wù)的若干方法

      crontab 是運(yùn)維過(guò)程中常用的定時(shí)任務(wù)執(zhí)行工具
       一般情況下在有新的定時(shí)任務(wù)要執(zhí)行時(shí),使用crontab -e ,將打開(kāi)一個(gè)vi編輯界面,配置好后保存退出,但是在自動(dòng)化運(yùn)維的過(guò)程中往往需要使用shell腳本
       或命令自動(dòng)添加定時(shí)任務(wù)。接下來(lái)結(jié)束三種(Centos)自動(dòng)添加的crontab 任務(wù)的方法:
      
      方法一:
      編輯 /var/spool/cron/用戶名 文件,如:
      echo "* * * * * hostname >> /tmp/tmp.txt" >> /var/spool/cron/root
      優(yōu)點(diǎn):簡(jiǎn)單
      缺點(diǎn):需要root權(quán)限
      
      方法二:
      編輯 /etc/crontab 文件,
      echo "* * * * * root hostname >> /tmp/tmp.txt" >> /etc/crontab
      需要注意的是,與常用的crontab 有點(diǎn)不同,/etc/crontab 需指定用名。而且該文件定義為系統(tǒng)級(jí)定時(shí)任務(wù) 不建議添加非系統(tǒng)類定時(shí)任務(wù),編輯該文件也需要root權(quán)限
       
      方法三:
       利用crontab -l 加 crontab file 兩個(gè)命令實(shí)現(xiàn)自動(dòng)添加
      crontab -l > conf && echo "* * * * * hostname >> /tmp/tmp.txt" >> conf && crontab conf && rm -f conf
      由于crontab file會(huì)覆蓋原有定時(shí)任務(wù),所以使用 crontab -l 先導(dǎo)出原有任務(wù)到臨時(shí)文件 “conf” 再追加新定時(shí)任務(wù)
      優(yōu)點(diǎn):不限用戶,任何有crontab權(quán)限的用戶都能執(zhí)行
      缺點(diǎn):稍微復(fù)雜 




      11章.構(gòu)建基本腳本

      ※,shell腳本的第一行 #!/bin/bash 

      在shell腳本的第一行中,必須寫(xiě)#!/bin/bash。如果是用其他shell,在修改相應(yīng)的shell路徑#!/bin/sh 表示本腳本由/bin/路徑的sh程序來(lái)解釋.... 跟命令行下~ #/bin/sh Scriptname效果相同如果不寫(xiě)也成,那就用你登陸的那個(gè)shell來(lái)解釋執(zhí)行. 可以不寫(xiě),但應(yīng)該有良好的編程習(xí)慣.“在很多情況中,如果沒(méi)有設(shè)置好這一行,那么該程序很可能會(huì)無(wú)法執(zhí)行,因?yàn)橄到y(tǒng)可能無(wú)法判斷該程序需要使用什么shell來(lái)執(zhí)行” -------鳥(niǎo)哥。所以,shell腳本第一行不寫(xiě)這一條語(yǔ)句,能不能執(zhí)行就看人品。

      但是對(duì)于非shell腳本,這第一行就大有學(xué)問(wèn)了!參考此文,總結(jié)如下:

      • #! 是有名字的,叫做·shebang·或`sha-bang`
      • #! 后面可以有一個(gè)或多個(gè)空白字符,后接解釋器的絕對(duì)路徑,用于指明執(zhí)行這個(gè)腳本文件的解釋器。
      • `#!/usr/bin/python` //python腳本首行加了這個(gè)之后就可以./xx.py執(zhí)行了,不用每次都執(zhí)行 python xx.py了。另外,python(Linux,python2.7)的CGIHTTPServer模塊調(diào)用py腳本作為cgi腳本時(shí),首行不寫(xiě)這個(gè)會(huì)報(bào)錯(cuò)!
      • ·#!/usr/bin/env python· // env python 是在 `env | grep PATH`下的所有目錄中尋找名為python的可執(zhí)行文件,執(zhí)行找到的第一個(gè)。而python則是指定的目錄下(·which python·可查看)的python可執(zhí)行文件。

      ※,bash shell中的 $(), ${}, $[], $(()), [], [[]], (()) 代表的含義:https://blog.csdn.net/taiyang1987912/article/details/39551385

      • $()和反引號(hào)作用一樣,用于命令替換,就是在shell中fork 一個(gè)子進(jìn)程去做括號(hào)里的命令然后再返回父進(jìn)程。Shell命令替換一般用于將命令的輸出結(jié)果賦值給某個(gè)變量,也可以配合其他命令使用。如:cd $(docker inspect ubuntu --format "{{.GraphDriver.Data.UpperDir}}"),先執(zhí)行$()中的命令,將其輸出作為cd的參數(shù)。注意cd命令是內(nèi)置命令,不能配合涉及寫(xiě)管道的xargs命令使用,所以cd命令一般配合$()使用。
        • 反引號(hào)基本上可用在全部的 unix shell 中使用,若寫(xiě)成 shell script ,其移植性比較高,但反單引號(hào)容易打錯(cuò)或看錯(cuò)。
        • $()并不是所有shell都支持。
      • ${ }用于變量替換。一般情況下,$var 與${var} 并沒(méi)有什么不一樣,但是用 ${ } 會(huì)比較精確的界定變量名稱的范圍。
        • ${}還有個(gè)模式匹配功能,亦即字符串截取功能
        • 字符串替換
          • ${var/pattern/replacement} //將字符串變量var中第一個(gè)pattern替換成replacement。
          • ${var//pattern/replacement} //將字符串變量var中所有的pattern替換成replacement。
          • ·${var/$'\r'/''}· // 將變量var(字符串)中的\r替換為空, 使用\r無(wú)法完成替換。·$'\n'·$''這種格式很獨(dú)特,$'\n'指的是換行符。$'\r'同理。
      • $[]和$(())是一樣的,在$(())中可以操作的在$[]中也可以,都是進(jìn)行數(shù)學(xué)運(yùn)算的,支持+ - * / %(“加、減、乘、除、取模”),注意在其中只能作整數(shù)運(yùn)算,對(duì)于浮點(diǎn)數(shù)是當(dāng)作字符串處理的,會(huì)報(bào)錯(cuò)。bash中使用bc(bash calculator)命令計(jì)算浮點(diǎn)數(shù)。
        • $(()) 和 $[] 返回的是一個(gè)具體的運(yùn)算后的值,而(()) 和 [] 和 [[]] 返回的是true或false,是用于if判斷語(yǔ)句后面的
        • 在 $[]和$(())中的變量名稱,可于其前面加 $ 符號(hào)來(lái)替換,也可以不用.如 $((a +b* c))和 $(($a + $b*$c))效果相同。
        • 此外,$[]和$(()) 還可作不同進(jìn)位(如二進(jìn)制、八進(jìn)位、十六進(jìn)制)運(yùn)算,只是,輸出結(jié)果皆為十進(jìn)制而已。如 $((2#111)),輸出7。 8#,16#分別表示8進(jìn)制和16進(jìn)制。
      • ============
      • [] 為test命令的另一種形式,[ expression ]。是用于if判斷語(yǔ)句后面的
        • 必須在左括號(hào)的右側(cè)和右括號(hào)的左側(cè)各加一個(gè)空格,否則會(huì)報(bào)錯(cuò)
        • test命令使用標(biāo)準(zhǔn)的數(shù)學(xué)比較符號(hào)來(lái)表示字符串的比較,而用文本符號(hào)(-gt 等等)來(lái)表示數(shù)值的比較。
        • 大于符號(hào)或小于符號(hào)必須要轉(zhuǎn)義,否則會(huì)被理解成重定向。
      •  
      • (()) :(()) 專門(mén)針對(duì)數(shù)學(xué)運(yùn)算,對(duì)運(yùn)算結(jié)果進(jìn)行判斷從而返回true或false。格式為 (( expression )),空格非必須,expression可以是 數(shù)學(xué)運(yùn)算表達(dá)式 或 (數(shù)學(xué))比較表達(dá)式。且不需要再將表達(dá)式里面的大小于符號(hào)轉(zhuǎn)義。是用于if判斷語(yǔ)句后面的
        • expression 是數(shù)學(xué)表達(dá)式時(shí),如果計(jì)算結(jié)果為0,那么返回的退出狀態(tài)碼為1,即為false;而一個(gè)非零值的表達(dá)式所返回的退出狀態(tài)碼將為0,或者是"true"
        • expression 是比較表達(dá)式時(shí),表達(dá)式結(jié)果為true,則返回退出狀態(tài)碼0,即true。表達(dá)式結(jié)果為false,則返回退出狀態(tài)碼1,即false。
        • (())中可以使用的數(shù)學(xué)運(yùn)算符號(hào)有:+;-;*;/;%;val++ 后增;val-- 后減;++val 先增;--val 先減;! 邏輯求反;~ 位求反;** 冪運(yùn)算;<< 左位移;>> 右位移;& 位布爾和;| 位布爾或;&& 邏輯和;|| 邏輯或
        • ·((3%3))·// 計(jì)算結(jié)果為0,返回退出狀態(tài)碼為1,false.
        • `((3>2))`// 表達(dá)式為true,返回退出狀態(tài)碼為0,true。
      • [[]]:[[]]可以視為 test命令的加強(qiáng)版,在[]中可以使用的在[[]]中也可以使用。[[ expression ]]。是用于if判斷語(yǔ)句后面的,當(dāng)然while之類的也能用
        • 和[]一樣,必須在左括號(hào)的右側(cè)和右括號(hào)的左側(cè)各加一個(gè)空格,否則會(huì)報(bào)錯(cuò)
        • 和[]一樣,字符串比較時(shí),==,>,等符號(hào)兩邊需要空格。
        • 和[]一樣,若使用了 ==,<,>等符號(hào),則符號(hào)兩邊即使是數(shù)字也被視為字符串。數(shù)字比較只能用 -eq, -gt等。
        • 和[]一樣,[[]]中的 >,<不用轉(zhuǎn)義。
        • 雙方括號(hào)在bash shell中工作良好。不過(guò)要小心,不是所有的shell都支持雙方括號(hào)
        • 使用[[ ... ]]條件判斷結(jié)構(gòu),而不是[ ... ],能夠防止腳本中的許多邏輯錯(cuò)誤。比如可以直接使用if [[ $a != 1 && $a != 2 ]], 如果不使用雙括號(hào), 則為if [ $a -ne 1] && [ $a != 2 ]或者if [ $a -ne 1 -a $a != 2 ]。
        • [[ ]]中針對(duì)字符串增加模式匹配(pattern matching)特性。在模式匹配中可以定義個(gè)正則表達(dá)式來(lái)匹配字符串值。
        • ====普通模式匹配:普通模式匹配不是正則,在此模式中,*代表任意多個(gè)字符,?代表單個(gè)字符======
        • ·user=tong;[[ $user == t* ]]·// *代表任意多個(gè)字符。是否以字母 t 開(kāi)頭,返回退出狀態(tài)碼0,true。
        • ·[[ tong == "t*" ]]· //如果 t* 用雙引號(hào)或單引號(hào)括起來(lái),那么就不是模式匹配了,而是字面匹配(literal match),返回1,false。
        • ·[[ tong == ton. ]]· //這里的.就是普通的字符.,沒(méi)有任何含義。返回退出狀態(tài)碼1,false。
        • ·[[ tong == ton? ]]·// ?代表單個(gè)字符,返回0,true。
        • ====正則匹配模式:使用 =~ 開(kāi)啟正則匹配模式!====================
        • ·[[ tong =~ ton. ]]· //正則,返回0,true。
        •  

      ※,內(nèi)聯(lián)輸入重定向( << )

      官方文檔參考此處

      內(nèi)聯(lián)輸入重定向允許你直接在命令行中重定向數(shù)據(jù)。格式如下:

      內(nèi)聯(lián)輸入重定向 (英文為 Here Document ) 允許你直接在命令行中重定向數(shù)據(jù)。格式如下:

      command << delimiter
      document
      delimiter
      它的作用是將兩個(gè) delimiter 之間的內(nèi)容(document) 作為輸入傳遞給 command

      注意:結(jié)尾的delimiter 一定要頂格寫(xiě),前面和后面都不能有任何字符(包括空格和 tab 縮進(jìn))。開(kāi)始的delimiter前后的空格會(huì)被忽略掉。

      cat << EOF
      line1
      line2
      line3 word1
      EOF
      # EOF 標(biāo)記了內(nèi)聯(lián)重定向的開(kāi)始和結(jié)束,可是使用任意的字符串來(lái)標(biāo)記,只要相同就可以。
      
      更常用的方式:
      cat >> logFile << EOF
      ...
      EOF
      # 注意>>logFile這部分和Here Doc沒(méi)有關(guān)系,是配合Here Doc命令使用的(追加文檔,若是>logFile則是覆蓋文檔)。cat命令也可以是其他命令。

      如果腳本中有shell命令($()包裹的命令),則命令會(huì)被解析。如果不想命令被解析,可以將始的delimiter用雙引號(hào)引起來(lái),如

      # 如果不用雙引號(hào)將EOF引起來(lái),那么寫(xiě)入shell.sh中的將是USER_IP=192.168.35.36這種解析后的結(jié)果,
      # 而使用了雙引號(hào)則會(huì)將命令原樣寫(xiě)入shell.sh文件中。
      cat >> shell.sh << "EOF"
      USER_IP=$(who -u am i 2>/dev/null| awk '{print $NF}'|sed -e 's/[()]//g')
      EOF

      ※,"<<<" (和"<<"使用方法一樣,區(qū)別就在于一個(gè)是一行,一個(gè)是多行): 在BASH文檔中,稱之為 "Here Strings"(官方文檔點(diǎn)此)。Here String是Here Documents 的一個(gè)變種。它由操作符"<<<"和作為標(biāo)準(zhǔn)輸入的字符串構(gòu)成,作用是將一個(gè)普通字符串重定向到command命令。使用場(chǎng)景如:sed命令處理一個(gè)字符串。 command <<<  "WORD" // WORD也可以是執(zhí)行某個(gè)命令的輸出,如 command <<< $(pwd)。

      • 示例0:
        grep -q redis-server <<< $(ps -ef) //可以在if條件是判斷redis-server進(jìn)程是否存在。如果存在返回0(true),如果不存在返回1(false)
        示例一:
        [root@localhost scripts]$a=HelloWorld
        [root@localhost scripts]$sed -n p <<< $a
        HelloWorld
        [root@localhost scripts]$
        示例二:
        cat >> /tmp/a <<< $(ps -ef) //將ps -ef命令的輸出追加到/tmp/a文件中。
        示例三:
        以下命令相同
        # mysql -u root   -e      "select user,host from mysql.user;"
        # mysql -u root   <<<    "select user,host from mysql.user;"

      輸入重定向總結(jié):標(biāo)準(zhǔn)輸入正常情況下是來(lái)自鍵盤(pán)的輸入,以下三個(gè)符號(hào)都表示 重定向了標(biāo)準(zhǔn)輸入

      • < filename 表示標(biāo)準(zhǔn)輸入被重定向至文件。
      • << 表示標(biāo)準(zhǔn)輸入來(lái)自輸入的多行文件中
      • <<< 表示標(biāo)準(zhǔn)輸入來(lái)自輸入的單行字符串

      ※,浮點(diǎn)數(shù)計(jì)算:bc計(jì)算器。

      • bash計(jì)算器實(shí)際上是一種編程語(yǔ)言,它允許在命令行中輸入浮點(diǎn)表達(dá)式,然后解釋并計(jì)算該表達(dá)式,最后返回結(jié)果。
      • 浮點(diǎn)運(yùn)算是由內(nèi)建變量 scale 控制的。必須將這個(gè)值設(shè)置為你希望在計(jì)算結(jié)果中保留的小數(shù)位數(shù),否則無(wú)法得到期望的結(jié)果。

      腳本中使用bc計(jì)算器: 可以用命令替換運(yùn)行 bc 命令,并將輸出賦給一個(gè)變量。基本格式如下:`variable=$(echo "options; expression" | bc)`。第一部分 options 允許你設(shè)置變量。如果你需要不止一個(gè)變量,可以用分號(hào)將其分開(kāi)。expression 參數(shù)定義了通過(guò) bc 執(zhí)行的數(shù)學(xué)表達(dá)式。

      例一:

      $ cat test9
      #!/bin/bash
      var1=$(echo "scale=4; 3.44 / 5" | bc)
      echo The answer is $var1
      $

      例二:

      #!/bin/bash
      var1=10.46
      var2=43.67
      var3=33.2
      var4=71
      var5=$(bc << EOF
      scale = 4
      a1 = ( $var1 * $var2)
      b1 = ($var3 * $var4)
      a1 + b1
      EOF
      )
      echo The final answer for this mess is $var5
      # 必須用命令替換符號(hào)標(biāo)識(shí)出用來(lái)給變量賦值的命令。

      ※,管道:command1 | command2

      • 管道是將一個(gè)命令的輸出重定向到另一個(gè)命令中。不要以為由管道串起的兩個(gè)命令會(huì)依次執(zhí)行。Linux系統(tǒng)實(shí)際上會(huì)同時(shí)運(yùn)行這兩個(gè)命令,在系統(tǒng)內(nèi)部將它們連接起來(lái)。在第一個(gè)命令產(chǎn)生輸出的同時(shí),輸出會(huì)被立即送給第二個(gè)命令。數(shù)據(jù)傳輸不會(huì)用到任何中間文件或緩沖區(qū)。

      ※,退出腳本:shell中運(yùn)行的每個(gè)命令都使用退出狀態(tài)碼(exit status)告訴shell它已經(jīng)運(yùn)行完畢。退出狀態(tài)碼是一個(gè)0~255的整數(shù)值,在命令結(jié)束運(yùn)行時(shí)由命令傳給shell。可以捕獲這個(gè)值并在腳本中使用。

      • 1,查看退出狀態(tài)碼:Linux提供了一個(gè)專門(mén)的變量 $? 來(lái)保存上個(gè)已執(zhí)行命令的退出狀態(tài)碼。對(duì)于需要進(jìn)行檢查的命令,必須在其運(yùn)行完畢后立刻查看或使用 $? 變量。它的值會(huì)變成由shell所執(zhí)行的最后一條命令的退出狀態(tài)碼。按照慣例,一個(gè)成功結(jié)束的命令的退出狀態(tài)碼是 0 。如果一個(gè)命令結(jié)束時(shí)有錯(cuò)誤,退出狀態(tài)碼就是一個(gè)正數(shù)值。
      • 1.1, exit命令:默認(rèn)情況下,shell腳本會(huì)以腳本中的最后一個(gè)命令的退出狀態(tài)碼退出。你可以改變這種默認(rèn)行為,返回自己的退出狀態(tài)碼。 exit 命令允許你在腳本結(jié)束時(shí)指定一個(gè)退出狀態(tài)碼。如exit 5;在運(yùn)行完腳本后立即查看$?可以看到其值為作為參數(shù)傳給 exit 命令的值,即5,exit的參數(shù)可以是變量,但其大小只能是0-255,超出的部分將會(huì)除以256然后取模,如256會(huì)被當(dāng)做0,257為1。
      • 總結(jié):exit命令有兩個(gè)作用,一是中斷腳本執(zhí)行,二是自定義腳本退出狀態(tài)碼(不給exit傳參數(shù)時(shí)默認(rèn)為0)。

      ※,



      12章. 使用結(jié)構(gòu)化指令

      ※,使用if - then 語(yǔ)句

      bash shell的if語(yǔ)句有兩種使用方式:1是后面跟命令;2是后面跟test命令(test命令即[ ]形式),具體如下敘述

      if使用方式一:bash shell的 if 語(yǔ)句會(huì)運(yùn)行 if 后面的那個(gè)命令。如果該命令的退出狀態(tài)碼是 0(該命令成功運(yùn)行),位于 then部分的命令就會(huì)被執(zhí)行。如果該命令的退出狀態(tài)碼是其他值,then部分的命令就不會(huì)被執(zhí)行,bash shell會(huì)繼續(xù)執(zhí)行腳本中的下一個(gè)命令。 fi 語(yǔ)句用來(lái)表示 if-then語(yǔ)句到此結(jié)束。

      if使用方式二:if test condition 形式: test 命令是一個(gè)特殊的命令,可以用來(lái)判斷某些條件是否成立。如果 test 命令中列出的條件成立,test 命令就會(huì)退出并返回退出狀態(tài)碼 0 ,這樣if語(yǔ)句就會(huì)執(zhí)行;如果條件不成立, test 命令就會(huì)退出并返回非零的退出狀態(tài)碼,這樣if語(yǔ)句就不會(huì)執(zhí)行

      if語(yǔ)句的格式如下:

      ### if 語(yǔ)句都有兩種格式,要么關(guān)鍵詞單獨(dú)放一行,要么通過(guò)分號(hào)隔開(kāi) ###
      # if - then 格式1:
      if command
      then
      commands
      fi
      
      # if -then 格式2:
      if command; then
      commands
      fi
      
      # if-then-else 格式1:
      if command
      then
      commands
      else
      commands
      fi
      
      # if-then-else 格式2:
      if command; then
      commands; else
      commands
      fi
      
      # if - then -elif - then - elif - then - else
      if  command1
      then
      command set 1
      elif  command2
      then
      command set 2
      elif  command3
      then
      command set 3
      elif  command4
      then
      command set 4
      else
      command set 5
      fi
      ##在 elif 語(yǔ)句中,緊跟其后的 else 語(yǔ)句屬于 elif 代碼塊。它們并不屬于之前的if-then 代碼塊。
      • `if sh -c "exit 0"; then echo Shell exited as true; else echo Shell exited as false; fi` // exit 零值 會(huì)被shell的if判斷為true
      • `if sh -c "exit 1"; then echo Shell exited as true\!; else echo Shell exited as false; fi` //  exit 非零值 會(huì)被shell的if判斷為false
      • // 判斷是否安裝了某個(gè)程序。! 與 type之間有個(gè)空格是必須的
        if ! type node >/dev/null 2>&1; then
            echo 'node 未安裝';
        else
            echo 'node 已安裝';
        fi
      •  

      ※,case

      case 命令會(huì)采用列表格式來(lái)檢查單個(gè)變量的多個(gè)值。兩個(gè)分號(hào)不是錯(cuò)誤,而是語(yǔ)法就是這樣的。

      case variable in
          pattern1 | pattern2 ) commands1 ;;
          pattern3 ) commands2 ;;
          *) default commands ;;
      esac

      ,test指令:test命令可以測(cè)試某個(gè)條件是否成立。

      ★,test命令在bash shell中有一個(gè)等同語(yǔ)法:方括號(hào),注意前方括號(hào)后和后方括號(hào)前必須都要有個(gè)空格。$?  表示上一個(gè)命令的返回值,0表示成功(if中判斷為true),其他表示各種異常(if中判斷為false)。 另外:·$!· 表示Shell最后運(yùn)行的后臺(tái)Process的PID

      • 如果不寫(xiě) test 命令的 condition 部分,它會(huì)以非零的退出狀態(tài)碼退出。false
      • `a="hello"; test a` //返回0。true
      • ·a=""; test a·//返回非0。false

      ★,test指令可以用來(lái)作:1,數(shù)值比較;2,字符串比較;3,文件比較

      test命令的數(shù)值比較功能
      比較(-eq等兩邊需要空格,并且eq前面有個(gè)短橫線!) 描述
      n1 -eq n2 檢查 n1 是否與 n2 相等
      n1 -ge n2 檢查 n1 是否大于或等于 n2
      n1 -gt n2 檢查 n1 是否大于 n2
      n1 -le n2 檢查 n1 是否小于或等于 n2
      n1 -lt n2 檢查 n1 是否小于 n2
      n1 -ne n2 檢查 n1 是否不等于 n2

      例子

      • =====數(shù)值比較:數(shù)值比較可以使用數(shù)字和變量========
      • ·[ 3 -gt 2 ]· //返回0,true.
      • `a=4; [ $a -gt 1 ]` //返回0,true.
      • `[ 5.5 -gt 4 ]`// 報(bào)錯(cuò)。bash shell只能處理整數(shù),不能在test 命令中使用浮點(diǎn)值。

       

      test命令的字符串比較功能
      比較(==,!=, >等操作符兩邊需要空格!) 描述
      str1 = str2(或 str1 == str2) 檢查 str1 是否和 str2 相同
      str1 != str2 檢查 str1 是否和 str2 不同
      str1 < str2 檢查 str1 是否比 str2 小(在ASCII字母順序下)
      str1 > str2 檢查 str1 是否比 str2 大(在ASCII字母順序下)

      大于號(hào)和小于號(hào)必須轉(zhuǎn)義,否則shell會(huì)把它們當(dāng)作重定向符號(hào),把字符串值當(dāng)作文件名。

      大于和小于順序和 sort 命令所采用的不同

      `[ "a" > "b" ]`//這個(gè)腳本中只用了大于號(hào),沒(méi)有出現(xiàn)錯(cuò)誤,但結(jié)果是錯(cuò)的。

      腳本把大于號(hào)解釋成了輸出重定向,因此它創(chuàng)建了一個(gè)名為b的文件。由于重定向順利完成

      test命令返回了退出狀態(tài)碼0。p.s. "a","b"可以不用加引號(hào)

      -n str1 檢查 str1 的長(zhǎng)度是否非0
      -z str1 檢查 str1 的長(zhǎng)度是否為0
      例子
      • `[ b \> a ]`//這里的b和a無(wú)需加引號(hào),會(huì)被自動(dòng)識(shí)別為字符串而不是變量。返回0,true.
      • `[ A \> a ]`//返回1,false.test命令中使用的是標(biāo)準(zhǔn)的ASCII順序,根據(jù)每個(gè)字符的ASCII數(shù)值來(lái)決定排序結(jié)果小寫(xiě)字母大于大寫(xiě)字母。但是sort命令剛好相反
      • -n 和 -z 可以檢查一個(gè)變量是否含有數(shù)據(jù)
      • `[ -n d ]`//d在這里是字符串d非變量d。返回0,true.
      • ·[ -n "" ]·// 返回非0,false.
      • `[ 3 == 3 ]`//返回0,true。這里使用==符號(hào),則3是字符串而非數(shù)字
      • ·[ 3 \> 13 ]·//返回非0,false。這里使用了>符號(hào),則3,13都是字符串。

       

       

      test命令的文件比較功能
      -e file 檢查file是否存在(文件或目錄皆可)
      -d file 檢查file是否存在且是一個(gè)目錄
      -f file 檢查file是否存在且是一個(gè)文件
      -s file 檢查file是否存在且非空
      -r file 檢查file是否存在且可讀
      -w file 檢查file是否存在且可寫(xiě)
      -x file 檢查file是否存在且可執(zhí)行
      -O file 檢查file是否存在且屬當(dāng)前用戶所有
      -G file

      檢查file是否存在且文件的默認(rèn)組與當(dāng)前用戶的默認(rèn)組相同

      (這里注意-G只會(huì)比較用戶的默認(rèn)組,而不是用戶所屬的所有組,這讓人有點(diǎn)困惑)

      • /etc/passwd的第四個(gè)字段表示用戶所屬的默認(rèn)組。/etc/group的第四個(gè)字段列出了屬于某個(gè)組的所有用戶(但是除了以這個(gè)組作為默認(rèn)組的用戶,這些用戶被默認(rèn)省略了)
      • 假如文件myFile默認(rèn)組為groupA,若當(dāng)前用戶默認(rèn)組為groupA,則test -G myFile返回0,表示成功,假如當(dāng)前用戶的默認(rèn)組為groupB,即使當(dāng)前用戶也屬于groupA,也返回1,表示失敗。
      file1 -nt file2 檢查file1 是否比 file2 新,注意這個(gè)命令不會(huì)判斷file1和file2是否存在,使用時(shí)需要首先判斷文件是否存在,否則會(huì)得到錯(cuò)誤的結(jié)果。
      file1 -ot file2 檢查file1 是否比 file2 舊,同上注意事項(xiàng)。
       例子  
      windows@Tonus:~/shellScript$ ll
      總用量 20
      drwxrwxr-x  2 windows windows 4096  3月 18 10:06 ./
      drwxr-xr-x 24 windows windows 4096  3月 18 09:50 ../
      -rwxrwxr-x  1 windows windows  133  3月 18 09:50 forShell*
      -rwxrwxr-x  1 windows windows  502  3月 17 10:39 ifShell.sh*
      -rw-rw-r--  1 windows windows   54  3月 18 09:50 sortFile
      windows@Tonus:~/shellScript$ [ -f sortFile ];echo $?
      0
      windows@Tonus:~/shellScript$ test -f sortFile;echo $?
      0
      windows@Tonus:~/shellScript$ [ -d sortFile ];echo $?
      1
      windows@Tonus:~/shellScript$ test -d sortFile;echo $?
      1
      windows@Tonus:~/shellScript$ test -f a b;echo $? //這里參數(shù)過(guò)多,只能有一個(gè)參數(shù)
      -bash: test: a:需要二元表達(dá)式
      2
      windows@Tonus:~/shellScript$ test -f a;echo $? //a不存在
      1
      windows@Tonus:~/shellScript$ 

      ※,符合條件測(cè)試

      • ·if [ condition1 ] && [ condition2 ]·//布爾AND
      • ·if [ condition1 ] || [ condition2 ]·//布爾OR

      ※,Linux多個(gè)命令執(zhí)行順序

      • 順序執(zhí)行多條命令:command1;command2;command3
        • 用;號(hào)隔開(kāi)每個(gè)命令, 每個(gè)命令按照從左到右的順序,順序執(zhí)行, 彼此之間不關(guān)心是否失敗, 所有命令都會(huì)執(zhí)行。
      • 有條件執(zhí)行多條命令: command1 && command2 || command3; 可以使用括號(hào)改變優(yōu)先級(jí)。
        • && 表示 在前一個(gè)命令執(zhí)行成功后才會(huì)執(zhí)行第二個(gè)命令;
        • || 表示 在前一個(gè)命令執(zhí)行失敗后才會(huì)執(zhí)行第二個(gè)命令(第一個(gè)為真第二個(gè)就無(wú)需執(zhí)行了)。
          gitt不存在,其他都存在
          # which gitt && which git || which tree
          /usr/bin/tree
          
          # which gitt && (which git || which tree)
          無(wú)輸出
          
          # which which && (which git || which tree)
          /usr/bin/which
          /usr/bin/git
          
          # which which && which git || which tree
          /usr/bin/which
          /usr/bin/git
        • #### ~/.bashrc需要添加下面的指令,$-表示當(dāng)前 shell 的選項(xiàng)標(biāo)志(set命令可以設(shè)置)。
          #### 此腳本含義是,如果shell是以交互式(i)方式(tty)使用時(shí),則腳本繼續(xù)往下走,讀取后續(xù)的命令;
          #### 而如果shell是以非交互式方式(non-tty)方式使用時(shí),則return,提前退出此腳本。
          #### 比如sftp服務(wù)端就是以non-tty方式使用shell的,sftp服務(wù)端會(huì)讀取~/.bashrc腳本,
          #### (實(shí)際解決的一個(gè)問(wèn)題:堡壘機(jī)89.9無(wú)法上傳文件的問(wèn)題(sftp協(xié)議傳輸過(guò)程被bash中的echo語(yǔ)句污染導(dǎo)致))
          #### 如果這里的腳本有輸出字符,則會(huì)導(dǎo)致sftp服務(wù)端將這些字符發(fā)送給sftp客戶端,導(dǎo)致客戶端無(wú)法解析(packet too long)
          #### 導(dǎo)致無(wú)法上傳文件!可以參考此文:https://www.ittsystems.com/troubleshooting-received-message-too-long/#wbounce-modal
          # If not running interactively, don't do anything and return early
          [[ $- == *i* ]] || return
          
          #### [[ $- == *i* ]] 返回0或1
          #### 0代表true,返回0 表示是以交互方式運(yùn)行,不會(huì)執(zhí)行后面的return;
          #### 1代表false,返回1 表示是以非交互方式運(yùn)行,會(huì)執(zhí)行后面的return提前退出。
          
          另外也可以如下設(shè)置:
          # If not running interactively, don't do anything
          case $- in
              *i*) ;;
                *) return;;
          esac


      13. 更多的結(jié)構(gòu)化指令(for,while,until)

      ※,環(huán)境變量IFS(internal field separator內(nèi)部字段分隔符),定義了bash shell用作字段分隔符的一系列字符。默認(rèn)情況下bash shell會(huì)將 空格符、制表符、換行符 當(dāng)做字段分隔符。讀取文件中的內(nèi)容時(shí)常常需要按照需求定義這個(gè)環(huán)境變量里。可以按需要修改IFS的值,用法如下:

      • 以下自測(cè)時(shí)的環(huán)境為:Ubuntu 20.10
      • IFS=$'\n' //將換行符視為字段分隔符,$''($加單引號(hào))語(yǔ)法格式 是bash shell中的很特殊的一種用法
      • IFS=$"\n" // 將反斜杠和字母n作為字段分隔符
      • IFS='\n' // 同上,將反斜杠和字母n作為字段分隔符
      • IFS="\n" // 同上,將反斜杠和字母n作為字段分隔符
      • IFS=\n //將字母n作為字段分隔符
      • IFS=\\n  //將反斜杠和字母n作為字段分隔符
      • IFS=$'\n'$'\t' //將換行符和制表符作為字段分隔符
      • IFS=$'\n'.:;" //將換行符,點(diǎn)號(hào),冒號(hào),分號(hào),雙引號(hào)(都)作為字段分隔符
      • `echo "$IFS"|od -b`
      0000000 040 011 012 012  
      0000004
      直接輸出IFS是看不到值的,轉(zhuǎn)化為二進(jìn)制就可以看到了,"040"是空格,"011"是Tab,"012"是換行符"\n" 。最后一個(gè) 012 是因?yàn)?echo 默認(rèn)是會(huì)換行的。

      關(guān)于使用IFS將字符串拆成數(shù)組的說(shuō)明:

      • 比如字符串為:·a,\n  b,\n  ·即一個(gè)字母接一個(gè)換行符再接兩個(gè)空格這種形式的字符串。現(xiàn)在想把 a,b這兩個(gè)字母放在一個(gè)數(shù)組變量里面,可以定義`IFS=\ ,$'\n'`,即空格(需轉(zhuǎn)義)逗號(hào)換行符,而且這三個(gè)符號(hào)也沒(méi)有順序,但是不能把整體用引號(hào)引起來(lái)。

      ※,使用通配符讀取目錄

      可以使用for命令來(lái)自動(dòng)遍歷目錄中的文件。進(jìn)行此操作時(shí),必須在文件名或路徑名中使用通配符。它會(huì)強(qiáng)制shell使用文件擴(kuò)展匹配。文件擴(kuò)展匹配是生成 匹配指定通配符 的文件或路徑名的過(guò)程。

      windows@Tonus:~/shellScript$ pwd
      /home/windows/shellScript
      windows@Tonus:~/shellScript$ ll
      總用量 32
      drwxrwxr-x  2 windows windows  4096  3月 18 10:48  ./
      drwxr-xr-x 24 windows windows  4096  3月 18 10:48  ../
      -rw-rw-r--  1 windows windows     0  3月 18 10:09 'a b'
      -rwxrwxr-x  1 windows windows   510  3月 18 10:46  forShell*
      -rw-r--r--  1 windows windows 12288  3月 18 10:49  .forShell.swp
      -rwxrwxr-x  1 windows windows   502  3月 17 10:39  ifShell.sh*
      -rw-rw-r--  1 windows windows     0  3月 18 10:10  log
      -rw-rw-r--  1 windows windows    59  3月 18 10:25  sortFile
      windows@Tonus:~/shellScript$ vi forShell 
      
      for file in /home/windows/shellScript/*
      do
          # 這里$file需要用雙引號(hào)引起來(lái),不然含有空格的文件或目錄會(huì)報(bào)錯(cuò),Linux下的文件或目錄可以含有空格
          if [ -f "$file" ];then
              echo "$file " is a file
          elif test -d "$file";then
              echo "$file " is a directory
          elif [[ "$file" == t* ]];then
              continue;# 如果文件名稱以t開(kāi)頭,則跳過(guò)本次循環(huán)繼續(xù)下次循環(huán)。同樣的 break 表示結(jié)束本次循環(huán),后面的語(yǔ)句將不再進(jìn)行。
          fi
      done
      for line in *Shell*;do echo $line;done;
      
      windows@Tonus:~/shellScript$ ./forShell 
      /home/windows/shellScript/a b  is a file
      /home/windows/shellScript/forShell  is a file
      /home/windows/shellScript/ifShell.sh  is a file
      /home/windows/shellScript/log  is a file
      /home/windows/shellScript/sortFile  is a file
      =================================
      forShell
      ifShell.sh
      windows@Tonus:~/shellScript$ 

      ※,C語(yǔ)言風(fēng)格的for 命令:C語(yǔ)言風(fēng)格的 for 命令有些部分并沒(méi)有遵循bash shell標(biāo)準(zhǔn)的 for 命令

      • 變量賦值可以有空格(例子中的 i = 1);
      • 條件中的變量不以美元符開(kāi)頭;
      • 迭代過(guò)程的算式未用 expr 命令格式(expr i <= 10)。
      for (( i = 1; i <= 10; i++ ))
      do
          echo "The next number is $i"
      done
      
      # 使用多個(gè)變量
      for (( a=1, b=10; a <= 10; a++, b-- ))
      do
          echo "$a - $b"
      done

      `for ((i = 1;i < 10;i++));do echo line$i>>data.txt; done`

      ※,實(shí)例

      1. 查找可執(zhí)行文件

      #!/bin/bash
      # finding files in the PATH
      IFS=:
      for folder in $PATH
      do
          echo "$folder:"
          for file in $folder/*
          do  
              if [ -x $file ]
              then
                  echo " $file"
              fi  
          done
      done

      2. 創(chuàng)建多個(gè)用戶

      #!/bin/bash
      # process new user accounts
      input="users.csv"
      while IFS=',' read -r userid name
      do
      echo "adding $userid"
      useradd -c "$name" -m $userid
      done < "$input"
      
      #注釋:read 命令會(huì)自動(dòng)讀取users.csv文本文件的下一行內(nèi)容,所以不需要專門(mén)再寫(xiě)一個(gè)循環(huán)來(lái)處理。當(dāng)read 命令返回 FALSE 時(shí)(也就是讀取完整個(gè)文件時(shí)), while 命令就會(huì)退出。
      #要想把數(shù)據(jù)從文件中送入 while 命令,只需在 while 命令尾部使用一個(gè)重定向符就可以了!
      $ cat users.csv
      rich,Richard Blum
      christine,Christine Bresnahan
      barbara,Barbara Blum
      tim,Timothy Bresnahan

      3. 讀取用戶輸入,錯(cuò)了重新輸入

      ## $'\n' 換行
      while [[ $os_type -ne 1  && $os_type -ne 2 ]];do
          read -p $'1)centos7   2)ubuntu20\n請(qǐng)選擇當(dāng)前操作系統(tǒng),輸入1或2\n' os_type
      done


      14,處理用戶輸入

      ※,獲取當(dāng)前shell執(zhí)行腳本的名字的幾種方法

      當(dāng)我們編寫(xiě)shell腳本時(shí),有時(shí)候需要獲取當(dāng)前執(zhí)行腳本的名字,最常用的應(yīng)該就是$0了,但是根據(jù)執(zhí)行腳本的方式不同,這種方式是有缺陷的,我們執(zhí)行腳本的可能方式有:

      • ./script.sh
      • . script.sh
      • source script.sh

      這幾種方式的$0不盡相同,先總結(jié)如下:

      方法 描述
      $0 only works when user executes “./script.sh”
      $BASH_ARGV only works when user executes “. script.sh” or “source script.sh”
      ${BASH_SOURCE[0]} works on both cases.
      readlink -f useful when symbolic link is used

      ※,命令行參數(shù):添加在命令后的數(shù)據(jù)

      • 位置參數(shù)(positional paramter):$0(腳本名稱), $1....$9, ${10}, ${11}....
        basename 命令會(huì)返回不包含路徑的腳本名
        $ cat test5b.sh
        #!/bin/bash
        # Using basename with the $0 parameter
        #
        name=$(basename $0)
        echo
        echo The script name is: $name
        #
        $ bash /home/Christine/test5b.sh
        The script name is: test5b.sh
        $
        $ ./test5b.sh
        The script name is: test5b.sh
        $ 
      •  if [ -n "$1" ] // 使用 -n 檢查參數(shù)是否存在

      • if [ $# -ne 2 ] //檢查變量個(gè)數(shù)是否不等于2,特殊變量 $# 含有腳本運(yùn)行時(shí)攜帶的命令行參數(shù)的個(gè)數(shù)。
        • 理論上講,$#代表變量總個(gè)數(shù),那么${$#}代表的就是最后一個(gè)參數(shù)。但實(shí)際上是${!#}代表最后一個(gè)參數(shù),可能是因?yàn)閎ash中 ${}中不能使用$符號(hào)。
      • $* 和 $@ 變量可以用來(lái)輕松訪問(wèn)所有的參數(shù)(不包含$0,$0是腳本名稱,永遠(yuǎn)不會(huì)變。$0出現(xiàn)在shell的函數(shù)中也是表示腳本名稱,不會(huì)改變)。這兩個(gè)變量都能夠在單個(gè)變量中存儲(chǔ)所有的命令行參數(shù)。

        • $* 變量會(huì)將命令行上提供的所有參數(shù)當(dāng)作一個(gè)單詞保存。這個(gè)單詞包含了命令行中出現(xiàn)的每一個(gè)參數(shù)值。基本上 $* 變量會(huì)將這些參數(shù)視為一個(gè)整體,而不是多個(gè)個(gè)體。
        • $@ 變量會(huì)將命令行上提供的所有參數(shù)當(dāng)作同一字符串中的多個(gè)獨(dú)立的單詞。這樣你就能夠遍歷所有的參數(shù)值,得到每個(gè)參數(shù)。這通常通過(guò) for 命令完成。
          $ cat test12.sh
          #!/bin/bash
          # testing $* and $@
          #
          echo
          count=1
          #
          for param in "$*"
          do
          echo "\$* Parameter #$count = $param"
          count=$[ $count + 1 ]
          done
          #
          echo
          count=1
          #
          for param in "$@"
          do
          echo "\$@ Parameter #$count = $param"
          count=$[ $count + 1 ]
          done
          $
          $ ./test12.sh rich barbara katie jessica
          $* Parameter #1 = rich barbara katie jessica
          $@ Parameter #1 = rich
          $@ Parameter #2 = barbara
          $@ Parameter #3 = katie
          $@ Parameter #4 = jessica
          $
      • 使用shift命令移動(dòng)參數(shù):默認(rèn)情況下它會(huì)將每個(gè)參數(shù)變量向左移動(dòng)一個(gè)位置。所以,變量 $3的值會(huì)移到 $2 中,變量 $2 的值會(huì)移到 $1 中,而變量 $1 的值則會(huì)被刪除(注意,變量 $0 的值,也就是程序名,不會(huì)改變)。這是遍歷命令行參數(shù)的另一個(gè)好方法,尤其是在你不知道到底有多少參數(shù)時(shí)。你可以只操作第一個(gè)參數(shù),移動(dòng)參數(shù),然后繼續(xù)操作第一個(gè)參數(shù)。例子如下:
        $ cat test13.sh
        #!/bin/bash
        # demonstrating the shift command
        echo
        count=1
        while [ -n "$1" ]
        do
        echo "Parameter #$count = $1"
        count=$[ $count + 1 ]
        shift
        done
        $
        $ ./test13.sh rich barbara katie jessica
        Parameter #1 = rich
        Parameter #2 = barbara
        Parameter #3 = katie
        Parameter #4 = jessica
        $
        • 使用 shift 命令的時(shí)候要小心。如果某個(gè)參數(shù)被移出,它的值就被丟棄了,無(wú)法再恢復(fù)。
        • 可以一次性移動(dòng)多個(gè)位置,只需要給 shift 命令提供一個(gè)參數(shù),指明要移動(dòng)的位置數(shù)就行了。如 shift 2

      • 1

      ※,命令行選項(xiàng):選項(xiàng)是跟在單破折線后面的單個(gè)字母,它能改變命令的行為。(處理命令行選項(xiàng)有三種方法:1像參數(shù)一樣處理,2getopt命令,3getopts命令)

      1. 首先,命令行選項(xiàng)和命令行參數(shù)地位是一樣的。都會(huì)占用$1這些位置參數(shù)。如果一個(gè)腳本同時(shí)使用了命令行參數(shù)和命令行選項(xiàng),Linux區(qū)分它們的標(biāo)準(zhǔn)方式是用特殊字符雙破折線(--)。shell會(huì)用雙破折線來(lái)表明選項(xiàng)列表結(jié)束,剩余的則被當(dāng)做命令行參數(shù)。

      2. 處理帶值的選項(xiàng)。(腳本中兼容下即可,具體見(jiàn)下面代碼)

      #!/bin/bash
      # $1必須用雙引號(hào)
      while [ -n "$1" ];do
          case "$1" in
              -a) echo "found -a option";;
              -b) echo "found -b option with paramter $2"
                  shift;;
              -c) echo "found -c option";;
              -d) echo "found -d option";;
              --) shift
                  break;;
               *) echo "$1 is not a option";;
          esac
          shift
      done
      echo
      count=1
      echo $@
      for param in "$@";do
          # 必須用引號(hào)包起來(lái)
          echo "#param$count = $param"
          count=$((count + 1))
      done

       

      3. 將多個(gè)選項(xiàng)放進(jìn)一個(gè)參數(shù)中,

      ※,使用 getopt 命令處理命令行選項(xiàng)和參數(shù)(注意:getopt 命令有一個(gè)更高級(jí)的版本叫作 getopts)

      ·getopt optstring parameters· // 在 optstring 中列出你要在腳本中用到的每個(gè)命令行選項(xiàng)字母。然后,在每個(gè)需要參數(shù)值的選項(xiàng)字母后加一個(gè)冒號(hào)。 getopt 命令會(huì)基于你定義的 optstring 解析提供的參數(shù)。

      例子:

      $ getopt ab:cd -a -b test1 -cd test2 test3
      -a -b test1 -c -d -- test2 test3
      $

      optstring 定義了四個(gè)有效選項(xiàng)字母:a、b、c和d。冒號(hào)(:)被放在了字母b后面,因?yàn)閎選項(xiàng)需要一個(gè)參數(shù)值。當(dāng)getopt命令運(yùn)行時(shí),它會(huì)檢查提供的參數(shù)列表( -a -b test1 -cd test2 test3 ),并基于提供的 optstring 進(jìn)行解析。注意,它會(huì)自動(dòng)將 -cd 選項(xiàng)分成兩個(gè)單獨(dú)的選項(xiàng),并插入雙破折線來(lái)分隔行中的額外參數(shù)。

      如果指定了一個(gè)不在 optstring 中的選項(xiàng),默認(rèn)情況下, getopt 命令會(huì)產(chǎn)生一條錯(cuò)誤消息。

      $ getopt ab:cd -a -b test1 -cde test2 test3
      getopt: invalid option -- e
      -a -b test1 -c -d -- test2 test3
      $
      如果想忽略這條錯(cuò)誤消息,可以在命令后加 -q 選項(xiàng)。
      $ getopt -q ab:cd -a -b test1 -cde test2 test3
      -a -b 'test1' -c -d -- 'test2' 'test3'
      $

      使用getopt命令的腳本如下:

      #!/bin/bash
      # 將原始腳本的命令行參數(shù)傳給 getopt 命令,之后再將 getopt 命令的輸出傳給 set 命令,用 getopt 格式化后的命令行參數(shù)來(lái)替換原始的命令行參數(shù)set -- $(getopt -q ab:cd "$@")
      # $1必須用雙引號(hào)
      while [ -n "$1" ];do
          case "$1" in
              -a) echo "found -a option";;
              -b) echo "found -b option with paramter $2"
                  shift;;
              -c) echo "found -c option";;
              -d) echo "found -d option";;
              --) shift
                  break;;
               *) echo "$1 is not a option";;
          esac
          shift
      done
      echo
      count=1
      echo $@
      for param in "$@";do
          # 必須用引號(hào)包起來(lái),否則#開(kāi)頭被視為注釋
          echo "#param$count = $param"
          count=$((count + 1))
      done

      windows@Tonus:~/shellScript$ bash paramShell.sh -a -b good p1 p2 p3 -cd
      found -a option
      found -b option with paramter 'good'
      found -c option
      found -d option

      'p1' 'p2' 'p3'
      #param1 = 'p1'
      #param2 = 'p2'
      #param3 = 'p3'
      windows@Tonus:~/shellScript$

      在 getopt 命令中仍然隱藏著一個(gè)小問(wèn)題。看看這個(gè)例子。
      $ ./test18.sh -a -b test1 -cd "test2 test3" test4
      Found the -a option
      Found the -b option, with parameter value 'test1'
      Found the -c option
      Parameter #1: 'test2
      Parameter #2: test3'
      Parameter #3: 'test4'
      $
      getopt 命令并不擅長(zhǎng)處理帶空格和引號(hào)的參數(shù)值。它會(huì)將空格當(dāng)作參數(shù)分隔符,而不是根據(jù)雙引號(hào)將二者當(dāng)作一個(gè)參數(shù)。更高級(jí)的 getopts 命令可以解決這個(gè)問(wèn)題

      ※,使用更高級(jí)的 getopts:getopts 命令(注意是復(fù)數(shù))內(nèi)建于bash shell,擴(kuò)展了 getopt 命令。 

      getopts 命令的格式如下:
      ·getopts optstring variable· // optstring 值類似于 getopt 命令中的那個(gè)。有效的選項(xiàng)字母都會(huì)列在 optstring 中,如果選項(xiàng)字母要求有個(gè)參數(shù)值,就加一個(gè)冒號(hào)。要去掉錯(cuò)誤消息的話,可以在 optstring 之前加一個(gè)冒號(hào)。 getopts 命令將當(dāng)前參數(shù)保存在命令行中定義的 variable 中。

      每次調(diào)用getopts 時(shí),它一次只處理命令行上檢測(cè)到的一個(gè)參數(shù)。處理完所有的參數(shù)后,它會(huì)退出并返回一個(gè)大于0的退出狀態(tài)碼。這讓它非常適合用解析命令行所有參數(shù)的循環(huán)中.

      getopts 命令會(huì)用到兩個(gè)環(huán)境變量。如果選項(xiàng)需要跟一個(gè)參數(shù)值, OPTARG 環(huán)境變量就會(huì)保存這個(gè)值。 OPTIND 環(huán)境變量保存了參數(shù)列表中 getopts 正在處理的參數(shù)位置。這樣你就能在處理完選項(xiàng)之后繼續(xù)處理其他命令行參數(shù)了.

      • 將選項(xiàng)字母和參數(shù)值放在一起使用,而不用加空格.   ./test19.sh -abtest1
      • getopts可以在參數(shù)值中包含空格../test19.sh -b "test1 test2" -a
      • getopts 還能夠?qū)⒚钚猩险业降乃形炊x的選項(xiàng)統(tǒng)一輸出成問(wèn)號(hào).

      getopts 命令知道何時(shí)停止處理選項(xiàng)(getopt命令會(huì)格式化選項(xiàng)和參數(shù),通過(guò)雙破折號(hào)--來(lái)區(qū)分選項(xiàng)和參數(shù),而getopts命令只處理選項(xiàng)。),并將參數(shù)留給你處理。在 getopts 處理每個(gè)選項(xiàng)時(shí),它會(huì)將 OPTIND 環(huán)境變量值增一(OPTIND變量初始值為1)。在 getopts 完成處理時(shí),你可以使用 shift 命令和 OPTIND 值來(lái)移動(dòng)參數(shù)。

      #!/bin/bash
      echo $OPTIND#初始值為1
      while getopts :abc: opt;do
          case "$opt" in
              a) echo "Found the -a option";;
              b) echo "Found the -b option";;
              c) echo "found the -c option with paramter $OPTARG";;
              *) echo "unknown option $opt";;
          esac
      done
      shift $[$OPTIND - 1] #初始值為1 所以-1
      echo $@
      count=1
      for param in "$@";do
          echo "param#$count: $param"
          # 以下三種寫(xiě)法皆可
          #count=$[count+1]
          #count=$[$count +1]
          count=$((count+1))
      done

      windows@Tonus:~/shellScript$ bash getopts.sh -ac "good bad" bug -b bad ugly
      OPTIND初始值為:1
      Found the -a option
      found the -c option with paramter good bad
      bug -b bad ugly
      param#1: bug
      param#2: -b
      param#3: bad
      param#4: ugly
      windows@Tonus:~/shellScript$

      ※,獲取用戶輸入

      盡管命令行選項(xiàng)和參數(shù)是從腳本用戶處獲得輸入的一種重要方式,但有時(shí)還需要和用戶進(jìn)行交互,bash shell為此提供了read命令。read命令從標(biāo)準(zhǔn)輸入(鍵盤(pán))或另一個(gè)文件描述符中接收輸入,收到輸入后,read命令會(huì)將數(shù)據(jù)放進(jìn)一個(gè)變量。

      • ·read <variable>· // 將用戶輸入放進(jìn)變量variable中。
      • ·read -p "Please Enter Your Name: " <variable>· // -p指定提示文字
        • 產(chǎn)生換行符,使用·$''·語(yǔ)法 `read -p $'Please Enter \x0a your name\n'`// `\x0a和\n·都可以在`$''`中換行。echo命令也可以如此用
      • ·read <variable1> <variable2> ... · // read會(huì)自動(dòng)將用戶輸入的數(shù)據(jù)分配給指定的變量,如果變量不夠,則剩余的數(shù)據(jù)就全部分配給最后一個(gè)變量。
      • ·read · //不指定任何變量,則read命令會(huì)將它收到的任何數(shù)據(jù)都放進(jìn)特殊環(huán)境變量 REPLY 中。
        read -p "IwillUseREPLY2saveWhatUSaid:"
        echo you just said $REPLY
      • ·read -t <seconds>· // -t參數(shù)指定了read命令等待輸入的秒數(shù),當(dāng)計(jì)時(shí)器過(guò)期后,read命令會(huì)返回一個(gè)非零退出狀態(tài)碼。
        if read -p "IwillUseREPLY2saveWhatUSaid:" -t 3;then
        echo you just said: $REPLY
        else echo ;echo "timeout error!too slow"
        fi
      • ·read -n1· //-n參數(shù)指定一個(gè)數(shù)字,當(dāng)read命令讀取指定的長(zhǎng)度的用戶輸入字符后就自動(dòng)退出,然后將輸入的數(shù)據(jù)賦值給變量。
        #!/bin/bash
        # getting just one character of input
        #
        read -n1 -p "Do you want to continue [Y/N]? " answer
        case $answer in
        Y | y) echo
        echo "fine, continue on…";;
        N | n) echo
        echo OK, goodbye
        exit;;
        esac
        echo "This is the end of the script"
      • ·read -s <variable>· // -s 選項(xiàng)可以避免在 read 命令中輸入的數(shù)據(jù)出現(xiàn)在顯示器上(實(shí)際上,數(shù)據(jù)會(huì)被顯示,只是read 命令會(huì)將文本顏色設(shè)成跟背景色一樣)。
      • ·cat <file> | read -p "read from a file" line· // 從文件中讀取一行。每次調(diào)用 read 命令,它都會(huì)從文件中讀取一行文本。當(dāng)文件中再?zèng)]有內(nèi)容時(shí), read 命令會(huì)退出并返回非零退出狀態(tài)碼。讀取全部行使用while循環(huán),代碼如下:
        #!/bin/bash
        count=1
        cat log | while read line;do #while 循環(huán)會(huì)持續(xù)通過(guò) read 命令處理文件中的行,直到 read 命令以非零退出狀態(tài)碼退出
        echo "Line $count: $line"
            count=$[$count+1]
        done
        echo "finish processing the file"


      第15章:呈現(xiàn)數(shù)據(jù)

      ※,標(biāo)準(zhǔn)文件描述符

      Linux系統(tǒng)將每個(gè)對(duì)象當(dāng)作文件處理。這包括輸入和輸出進(jìn)程。Linux用文件描述符( file descriptor )來(lái)標(biāo)識(shí)每個(gè)文件對(duì)象。文件描述符是一個(gè)非負(fù)整數(shù),可以唯一標(biāo)識(shí)會(huì)話中打開(kāi)的文件。每個(gè)進(jìn)程一次最多可以有九個(gè)文件描述符。出于特殊目的,bash shell保留了前三個(gè)文件描述符( 0 、 1 和 2 )。0代表標(biāo)準(zhǔn)輸入STDIN,1代表標(biāo)準(zhǔn)輸出STDOUT,2代表標(biāo)準(zhǔn)錯(cuò)誤STDERR。這三個(gè)特殊文件描述符會(huì)處理腳本的輸入和輸出。shell用它們將shell默認(rèn)的輸入和輸出導(dǎo)向到相應(yīng)的位置

      1. stdin

      STDIN 文件描述符代表shell的標(biāo)準(zhǔn)輸入。對(duì)終端界面來(lái)說(shuō),標(biāo)準(zhǔn)輸入是鍵盤(pán)。shell從 STDIN文件描述符對(duì)應(yīng)的鍵盤(pán)獲得輸入,在用戶輸入時(shí)處理每個(gè)字符。
      在使用輸入重定向符號(hào)( ·<· 等同于`0<`)時(shí),Linux會(huì)用重定向指定的文件來(lái)替換標(biāo)準(zhǔn)輸入文件描述符。它會(huì)讀取文件并提取數(shù)據(jù),就如同它是鍵盤(pán)上鍵入的。

      • ·&0·代表標(biāo)準(zhǔn)輸入指向的文件,默認(rèn)是鍵盤(pán)(Linux中鍵盤(pán)也是文件)

      2. stdout

      STDOUT 文件描述符代表shell的標(biāo)準(zhǔn)輸出。在終端界面上,標(biāo)準(zhǔn)輸出就是終端顯示器。shell的所有輸出(包括shell中運(yùn)行的程序和腳本)會(huì)被定向到標(biāo)準(zhǔn)輸出中,也就是顯示器。
      默認(rèn)情況下,大多數(shù)bash命令會(huì)將輸出導(dǎo)向 STDOUT 文件描述符。你可以用輸出重定向來(lái)改變。通過(guò)輸出重定向符號(hào)(·>·等同于`1>`),通常會(huì)顯示到顯示器的所有輸出會(huì)被shell重定向到指定的重定向文件。你也可以將數(shù)據(jù)追加到某個(gè)文件。這可以用 >> 符號(hào)來(lái)完成。

      • ·&1·代表標(biāo)準(zhǔn)輸出指向的文件,默認(rèn)是顯示器(Linux中顯示器也是文件)

      3. stderr

      shell通過(guò)特殊的 STDERR 文件描述符來(lái)處理錯(cuò)誤消息。 STDERR 文件描述符代表shell的標(biāo)準(zhǔn)錯(cuò)誤輸出。shell或shell中運(yùn)行的程序和腳本出錯(cuò)時(shí)生成的錯(cuò)誤消息都會(huì)發(fā)送到這個(gè)位置。
      默認(rèn)情況下, STDERR 文件描述符會(huì)和 STDOUT 文件描述符指向同樣的地方(盡管分配給它們的文件描述符值不同)。也就是說(shuō),默認(rèn)情況下,錯(cuò)誤消息也會(huì)輸出到顯示器輸出中。但重定向兩者是分開(kāi)的。

      • ·&2·代表標(biāo)準(zhǔn)錯(cuò)誤指向的文件,默認(rèn)也是顯示器(Linux中顯示器也是文件)
      • 標(biāo)準(zhǔn)錯(cuò)誤重定向符號(hào)為:`2>`
      • --------------------------------------------------------------------
      • `cat < a.txt` //輸入重定向
      • `ls -al 1>data.txt 2>err.txt` //輸出和錯(cuò)誤分別重定向,1>必須在一起,不能有空格,否則會(huì)報(bào)錯(cuò)。2>也一樣。
      • ·ls -al &> b.txt· // &> 將STDOUT 和 STDERR的輸出重定向到同一個(gè)文件。錯(cuò)誤輸出優(yōu)先級(jí)更高,集中在文件前面。

      ※,在腳本中使用重定向(重定向輸入輸出):

      • ·echo "This is an error message >&2· // 腳本中的這句代碼會(huì)將消息內(nèi)容由標(biāo)準(zhǔn)輸出重定向至標(biāo)準(zhǔn)錯(cuò)誤,如果 ./script.sh 2>errLog,則這句代碼會(huì)將輸出寫(xiě)入errLog中。&2代表標(biāo)準(zhǔn)錯(cuò)誤指向的文件,默認(rèn)即顯示器。同理,&0代表標(biāo)準(zhǔn)輸入指向的文件,默認(rèn)即鍵盤(pán)。
      • ·ls testFile, badFile >&2 2>c.txt· // ·>&2· 等同于·1>&2·整體必須在一起,不能用空格,表示將標(biāo)準(zhǔn)輸出重定向至 標(biāo)準(zhǔn)錯(cuò)誤代表的文件。注意shell執(zhí)行命令時(shí)從前往后依次解釋,即>&2時(shí),由于此時(shí)標(biāo)準(zhǔn)錯(cuò)誤2指向的顯示器,所以標(biāo)準(zhǔn)輸出實(shí)際被重定向至顯示器,然后2> c.txt表示將標(biāo)準(zhǔn)錯(cuò)誤重定向至c.txt文件。最終結(jié)果是標(biāo)準(zhǔn)輸出至顯示器,標(biāo)準(zhǔn)錯(cuò)誤輸出至c.txt文件中。
      • ==========exec命令可以將不同的文件描述符(0到8)指定到不同的地方================
      • ·exec 1> d.txt· // exec命令可以告訴shell在腳本執(zhí)行期間將標(biāo)準(zhǔn)輸出重定向至d.txt文件中,exec 命令會(huì)啟動(dòng)一個(gè)新shell并將 STDOUT 文件描述符重定向到文件。腳本中發(fā)給 STDOUT 的所有輸出會(huì)被重定向到文件。
      • ·exec 0< e.txt· // 將 STDIN 從鍵盤(pán)重定向到e.txt文件中。
        #!/bin/bash
        # redirecting file input
        exec 0< testfile
        count=1
        while read line
        do
        echo "Line #$count: $line"
        count=$[ $count + 1 ]
        done
        $ bash test12
        Line #1: This is the first line.
        Line #2: This is the second line.
        Line #3: This is the third line.
        # read 命令讀取用戶在鍵盤(pán)上輸入的數(shù)據(jù)。將 STDIN 重定向到文件后,當(dāng) read 命令試圖從 STDIN 讀入數(shù)據(jù)時(shí),它會(huì)到文件去取數(shù)據(jù),而不是鍵盤(pán)。
        # 這是在腳本中從待處理的文件中讀取數(shù)據(jù)的絕妙辦法。Linux系統(tǒng)管理員的一項(xiàng)日常任務(wù)就是從日志文件中讀取數(shù)據(jù)并處理。這是完成該任務(wù)最簡(jiǎn)單的辦法。

      在腳本中重定向輸入和輸出時(shí),并不局限于這3個(gè)默認(rèn)的文件描述符。在shell中最多可以有9個(gè)打開(kāi)的文件描述符。其他6個(gè)從 3 ~ 8 的文件描述符均可用作輸入或輸出重定向。你可以將這些文件描述符中的任意一個(gè)分配給文件,然后在腳本中使用它們。

      • ·exec 3> f.txt· // 將文件描述符3指向文件f.txt
        • ·echo "hello world" >&3·//·>&3·必須是整體。這里可以看出,3以上的文件描述符實(shí)際還是要通過(guò)標(biāo)準(zhǔn)輸出等來(lái)發(fā)揮作用,比如這里是把標(biāo)準(zhǔn)輸出重定向到了文件描述符3所指向的文件。
        • `3>`是一個(gè)整體不能用空格,如果輸錯(cuò)成了如下命令:`exec 3 > f.txt`,那么這個(gè)命令實(shí)際把STDOUT重定向到了f.txt中了,因?yàn)橹囟ㄏ蚍?hào)`>`默認(rèn)是`1>`
      • ·exec 3>>g.txt· //將輸出追加到現(xiàn)有文件中。

      一旦重定向了 STDOUT 或 STDERR ,就很難再將它們重定向回原來(lái)的位置。如果你需要在重定向中來(lái)回切換的話,可以分配另外一個(gè)文件描述符給標(biāo)準(zhǔn)文件描述符,這意味著你可以將 STDOUT 的原來(lái)位置重定向到另一個(gè)文件描述符,然后再利用該文件描述符重定向回 STDOUT。例子:

      • #!/bin/bash
        # storing STDOUT, then coming back to it
        exec 3>&1
        exec 1>test14out
        echo "This should store in the output file"
        echo "along with this line."
        exec 1>&3
        echo "Now things should be back to normal"
        $
        $ ./test14
        Now things should be back to normal
        $ cat test14out
        This should store in the output file
        along with this line.

      同理,可以重定向輸入文件描述符,例子:

      • #!/bin/bash
        # redirecting input file descriptors
        exec 6<&0
        exec 0< testfile
        count=1
        while read line
        do
        echo "Line #$count: $line"
        count=$[ $count + 1 ]
        done
        exec 0<&6
        read -p "Are you done now? " answer
        case $answer in
        Y|y) echo "Goodbye";;
        N|n) echo "Sorry, this is the end.";;
        esac
        $ ./test15
        Line #1: This is the first line.
        Line #2: This is the second line.
        Line #3: This is the third line.
        Are you done now? y
        Goodbye
        文件描述符 6 用來(lái)保存 STDIN 的位置。然后腳本將 STDIN 重定向到一個(gè)文件。read 命令的所有輸入都來(lái)自重定向后的 STDIN (也就是輸入文件)
        在讀取了所有行之后,腳本會(huì)將 STDIN 重定向到文件描述符 6 ,從而將 STDIN 恢復(fù)到原先的位置。該腳本用了另外一個(gè) read 命令來(lái)測(cè)試 STDIN 是否恢復(fù)正常了。這次它會(huì)等待鍵盤(pán)的輸入。
      • 創(chuàng)建讀寫(xiě)文件描述符:盡管看起來(lái)可能會(huì)很奇怪,但是你也可以打開(kāi)單個(gè)文件描述符來(lái)作為輸入和輸出。可以用同一個(gè)文件描述符對(duì)同一個(gè)文件進(jìn)行讀寫(xiě)。不過(guò)用這種方法時(shí),你要特別小心。由于你是對(duì)同一個(gè)文件進(jìn)行數(shù)據(jù)讀寫(xiě),shell會(huì)維護(hù)一個(gè)內(nèi)部指針,指明在文件中的當(dāng)前位置。任何讀或?qū)懚紩?huì)從文件指針上次的位置開(kāi)始。如果不夠小心,它會(huì)產(chǎn)生一些令人瞠目的結(jié)果。例如
        $ cat test16
        #!/bin/bash
        # testing input/output file descriptor
        exec 3<> testfile
        read line <&3
        echo "Read: $line"
        echo "This is a test line" >&3
        $ cat testfile
        This is the first line.
        This is the second line.
        This is the third line.
        $ ./test16
        Read: This is the first line.
        $ cat testfile
        This is the first line.
        This is a test line
        ine.
        This is the third line.
        $

        當(dāng)腳本向文件中寫(xiě)入數(shù)據(jù)時(shí),它會(huì)從文件指針?biāo)幍奈恢瞄_(kāi)始。 read 命令讀取了第一行數(shù)據(jù),所以它使得文件指針指向了第二行數(shù)據(jù)的第一個(gè)字符。在 echo 語(yǔ)句將數(shù)據(jù)輸出到文件時(shí)它會(huì)將數(shù)據(jù)放在文件指針的當(dāng)前位置,覆蓋了該位置的已有數(shù)據(jù)。

      • 關(guān)閉文件描述符:如果你創(chuàng)建了新的輸入或輸出文件描述符,shell會(huì)在腳本退出時(shí)自動(dòng)關(guān)閉它們。如果需要手動(dòng)關(guān)閉文件描述符,可以它重定向到特殊符號(hào) ·&-·。如·exec 3>&-·會(huì)關(guān)閉文件描述符 3 ,不再在腳本中使用它。一旦關(guān)閉了文件描述符,就不能在腳本中向它寫(xiě)入任何數(shù)據(jù),否則shell會(huì)生成錯(cuò)誤消息。在關(guān)閉文件描述符時(shí)還要注意另一件事。如果隨后你在腳本中打開(kāi)了同一個(gè)輸出文件(即又重定向至這個(gè)文件),shell會(huì)用一個(gè)新文件來(lái)替換已有文件。這意味著如果你輸出數(shù)據(jù),它就會(huì)覆蓋已有文件,可能會(huì)造成數(shù)據(jù)丟失,謹(jǐn)慎。?
      • =========================exec命令解釋============================
      • 使用 exec 命令可以并不啟動(dòng)新的 Shell,而是使用執(zhí)行命令替換當(dāng)前的 Shell 進(jìn)程,并且將老進(jìn)程的環(huán)境清理掉,而且 exec 命令后的其他命令將不再執(zhí)行
      • exec 命令通常用在 Shell 腳本程序中,可以調(diào)用其他的命令。如果在當(dāng)前終端中使用命令,則當(dāng)指定的命令執(zhí)行完畢后會(huì)立即退出終端。
      • exec [-cl] [-a name] [command [arguments]]·
        -c  #在空環(huán)境中執(zhí)行指定的命令
        -l  #在傳遞給command的第零個(gè)arg的開(kāi)頭放置一個(gè)破折號(hào)
        -a  #Shell將name作為第零個(gè)參數(shù)傳遞給command

      ※,列出打開(kāi)的文件描述符:你能用的文件描述符只有9個(gè),你可能會(huì)覺(jué)得這沒(méi)什么復(fù)雜的。但有時(shí)要記住哪個(gè)文件描述符被重定向到了哪里很難。為了幫助你理清條理,bash shell提供了 `lsof` 命令。lsof 命令會(huì)列出整個(gè)Linux系統(tǒng)打開(kāi)的所有文件描述符。這是個(gè)有爭(zhēng)議的功能,因?yàn)樗鼤?huì)向非系統(tǒng)管理員用戶提供Linux系統(tǒng)的信息。鑒于此,許多Linux系統(tǒng)隱藏了該命令,這樣用戶就不會(huì)一不小心就發(fā)現(xiàn)了。

      lsof(list open files)可以列出當(dāng)前系統(tǒng)中進(jìn)程打開(kāi)的所有文件,在Linux環(huán)境下,我們可以理解為一切(包括網(wǎng)絡(luò)套接口)皆文件。lsof 一般需要訪問(wèn)核心內(nèi)存和各種文件,所以必須以 root 用戶的身份運(yùn)行它才能夠充分地發(fā)揮其功能。

      lsof 的默認(rèn)輸出

      • COMMAND 正在運(yùn)行的命令名的前9個(gè)字符
      • PID 進(jìn)程的PID
      • TID:任務(wù) ID。
      • USER 進(jìn)程屬主的登錄名
      • FD 文件描述符號(hào)以及訪問(wèn)類型( r 代表讀, w 代表寫(xiě), u 代表讀寫(xiě))
        • cwd:應(yīng)用程序當(dāng)前工作目錄,這是該應(yīng)用程序啟動(dòng)的目錄,除非它本身對(duì)這個(gè)目錄進(jìn)行更改    
          txt:該類型的文件是程序代碼,如應(yīng)用程序二進(jìn)制文件本身或共享庫(kù),如上列表中顯示的 /sbin/init 程序
          lnn:庫(kù)引用(AIX)
          err:FD 信息錯(cuò)誤
          jld:監(jiān)獄目錄(FreeBSD)
          ltx:共享庫(kù)文本(代碼和數(shù)據(jù))
          mxx:十六進(jìn)制內(nèi)存映射類型號(hào) xx
          m86:DOS合并映射文件
          mem:內(nèi)存映射文件
          mmap:內(nèi)存映射設(shè)備
          pd:父目錄
          rtd:根目錄
          tr:內(nèi)核跟蹤文件(OpenBSD)
          v86:VP/ix 映射文件
          0:標(biāo)準(zhǔn)輸出
          1:標(biāo)準(zhǔn)輸入
          2:標(biāo)準(zhǔn)錯(cuò)誤
          文件描述符后一般還跟著文件狀態(tài)模式:
          r:只讀模式
          w:寫(xiě)入模式
          u:讀寫(xiě)模式
          空格:文件的狀態(tài)模式為 unknow,且沒(méi)有鎖定
          -:文件的狀態(tài)模式為 unknow,且被鎖定

          同時(shí)在文件狀態(tài)模式后面,還跟著相關(guān)的鎖:
          N:對(duì)于未知類型的 Solaris NFS 鎖
          r:文件部分的讀鎖
          R:整個(gè)文件的讀鎖
          w:文件的部分寫(xiě)鎖
          W:整個(gè)文件的寫(xiě)鎖
          u:任何長(zhǎng)度的讀寫(xiě)鎖
          U:用于未知類型的鎖
          x:用于部分文件上的 SCO OpenServer Xenix 鎖
          X:用于整個(gè)文件上的 SCO OpenServer Xenix 鎖
          space:無(wú)鎖

      • TYPE 文件的類型,常見(jiàn)的文件類型有:   
        • REG:普通文件
          DIR:表示目錄
          CHR:表示字符類型
          BLK:塊設(shè)備類型
          UNIX:UNIX 域套接字
          FIFO:先進(jìn)先出隊(duì)列
          IPv4:IPv4 套接字
      • DEVICE 設(shè)備的設(shè)備號(hào)(主設(shè)備號(hào)和從設(shè)備號(hào))
      • SIZE 如果有的話,表示文件的大小或文件偏移量(以字節(jié)為單位)
      • NODE 本地文件的節(jié)點(diǎn)號(hào)
      • NAME 打開(kāi)文件的確切名稱

      ·lsof· 用法見(jiàn) 此博文,lsof是一個(gè)很強(qiáng)大的命令。lsof能完成ps和netstat命令所能做的一切,并且還有其他額外的很多功能。理解一些關(guān)于lsof如何工作的關(guān)鍵性東西是很重要的。最重要的是,當(dāng)你給它傳遞選項(xiàng)時(shí),默認(rèn)行為是對(duì)結(jié)果進(jìn)行“或”運(yùn)算。因此,如果你正是用-i來(lái)拉出一個(gè)端口列表,同時(shí)又用-p來(lái)拉出一個(gè)進(jìn)程列表,那么默認(rèn)情況下你會(huì)獲得兩者的結(jié)果。

      • `man lsof` //所有的用法都在這個(gè)manual手冊(cè)里,比網(wǎng)上的資料要好的多!
      • `lsof` // 沒(méi)有任何選項(xiàng):lsof列出活躍進(jìn)程的所有打開(kāi)文件
      • ·-a· : 結(jié)果進(jìn)行“與”運(yùn)算(而不是“或”)
      • ·^·lsof很多命令的參數(shù)值可以使用非(^)來(lái)取反,見(jiàn)下文例子
      • -n, -P參數(shù)可以大大提升lsof的速度。解析host name和port名稱需要耗費(fèi)大量時(shí)間。
        • ·-n· -n參數(shù)不解析host name。
        • ·-P· 大寫(xiě)的p。不解析端口名。
      • `-l` : 小寫(xiě)L。在輸出顯示用戶ID而不是用戶名,比如root用戶名對(duì)應(yīng)用戶ID為0.
      • `-t`: 僅獲取進(jìn)程ID
      • `lsof <fileName>` 顯示與指定文件交互的所有一切
      • ·lsof <dirName>· 顯示與指定目錄交互的所有一切。一些使用場(chǎng)景如下:
        • 我們會(huì)遇到磁盤(pán)卸載報(bào)umount: /home: device is busy之類的提示,此時(shí)可以使用lsof命令找出占用磁盤(pán)的進(jìn)程·lsof /home·,然后直接kill掉此進(jìn)程,磁盤(pán)成功卸載
          • 當(dāng)然除了kill進(jìn)程外也可以使用如下方法:
          • `umount -l /home` 強(qiáng)行解除掛載,-l, --lazy              detach the filesystem now, and cleanup all later
          • `fuser -mv -k /home`直接殺死占用磁盤(pán)的進(jìn)程
        • Lsof解決文件已刪除空間未釋放問(wèn)題。文件已刪除,但是仍有進(jìn)程在占用這些文件,因此空間仍然沒(méi)有釋放。可以使用·lsof -n |grep deleted·查看占用文件的進(jìn)程,然后直接kill掉。
        • 巧用losf恢復(fù)已刪除文件:前提是文件的進(jìn)程必須存在。步驟如下: 假設(shè)被刪除的文件為lsof.log
          • ·lsof | grep lsof.log·查找出進(jìn)程ID。
            [root@192 ~]# lsof |grep lsof.log
            tail      9933      root    3r      REG                8,2        10     391018 /root/lsof.log (deleted)
          • PID:9933 FD:3 那我們有直接進(jìn)入/proc/9933/fd/3查看一下,發(fā)現(xiàn)文件描述符3軟連接到lsof.log文件,并且已經(jīng)被刪除。此時(shí)文件描述符3中依然有文件中的內(nèi)容,只要·cat 3 > /root/lsof.log·即可恢復(fù)
      • ·lsof -p <pid>· // 查看進(jìn)程<pid>打開(kāi)的文件。
        • ·lsof -p 1,2,3,4· //進(jìn)程ID可以有多個(gè),用逗號(hào)隔開(kāi)。
      • `lsof -c <command>` // 列出由command命令打開(kāi)的文件
        • ·lsof -c ^docker· //列出所有非docker命令打開(kāi)的文件。
      • `lsof -u <userName/UID>`  列出由 用戶名稱或用戶ID 打開(kāi)的所有文件
        • ·lsof -u root·
        • `lsof -u ^root` 列出所有非root打開(kāi)的文件
      • `lsof -g` 輸出列表中新增一項(xiàng)PGID。PGID定義:進(jìn)程可以組成進(jìn)程組(setpgrp系統(tǒng)調(diào)用),進(jìn)程組可以簡(jiǎn)化向所有組內(nèi)進(jìn)程發(fā)送信號(hào)的操作。進(jìn)程組ID叫做PGID,進(jìn)程組內(nèi)的所有進(jìn)程都有相同的PGID,等于該組組長(zhǎng)的PID。
        • ·lsof -g <PGID>· 列出屬于<PGID>的文件。
      • `lsof -d 4` 顯示使用fd為4的進(jìn)程
      • ·lsof +d /usr/local/· 顯示目錄下被進(jìn)程開(kāi)啟的文件
      • `lsof +D /usr/local/` 同上,但是會(huì)搜索目錄下的目錄,時(shí)間較長(zhǎng)
      •  

      獲取網(wǎng)絡(luò)信息:lsof -i[46] [protocol][@hostname|@hostaddr][:serviceList|:portList]

      • `lsof -i`  獲取所有網(wǎng)絡(luò)連接相關(guān)信息
      • ·:service·如:ssh代表的就是端口,所有的service名稱保存在文件`/etc/services`中
      • ·lsof -i tcp· 僅顯示TCP連接(同理可獲得UDP連接)
      • ·lsof -i :22` OR `lsof -i :ssh`顯示與指定端口相關(guān)的網(wǎng)絡(luò)信息
        • ·lsof -i:22,3306· 用逗號(hào)分割多個(gè)端口列表
      • `lsof -i @10.8.0.238` 顯示指定主機(jī)的連接信息,此主機(jī)可以是源主機(jī)也可以是目的主機(jī)。
      • ·lsof -i @10.8.0.238:22·顯示基于主機(jī)與端口的連接
      • ·lsof -i -s tcp:LISTEN· //找出正等候連接的端口。
        • 查看man lsof可以看到這種用法:-s [p:s],p代表protocol,s代表狀態(tài)。單獨(dú)的-s參數(shù)是列出文件大小,而這種用法和單獨(dú)的-s沒(méi)有任何關(guān)系。這種用法可已過(guò)濾出各種狀態(tài)的連接
        • `lsof -i -s tcp:^listen` // 所有非LISTEN的tcp連接。
        • ·lsof -i -s udp:Idle·
        • ·lsof  -i -s TCP:ESTABLISHED·
      •  

      ※,阻止命令輸出:如果在運(yùn)行在后臺(tái)的腳本出現(xiàn)錯(cuò)誤消息,shell會(huì)通過(guò)電子郵件將它們發(fā)給進(jìn)程的屬主。要解決這個(gè)問(wèn)題,可以將 STDERR 重定向到一個(gè)叫作null文件的特殊文件。null文件跟它的名字很像,文件里什么都沒(méi)有。shell輸出到null文件的任何數(shù)據(jù)都不會(huì)保存,全部都被丟掉了。在Linux系統(tǒng)上null文件的標(biāo)準(zhǔn)位置是/dev/null。你重定向到該位置的任何數(shù)據(jù)都會(huì)被丟掉,不會(huì)顯示。

      也可以在輸入重定向中將/dev/null作為輸入文件。由于/dev/null文件不含有任何內(nèi)容,程序員通常用它來(lái)快速清除現(xiàn)有文件中的數(shù)據(jù),而不用先刪除文件再重新創(chuàng)建,如

      $ cat testfile
      This is the first line.
      This is the second line.
      This is the third line.
      $ cat /dev/null > testfile
      $ cat testfile
      $

      ,創(chuàng)建臨時(shí)文件:mktemp 命令可以在/tmp目錄中創(chuàng)建一個(gè)唯一的臨時(shí)文件。shell會(huì)創(chuàng)建這個(gè)文件,但不用默認(rèn)的 umask 值。它會(huì)將文件的讀和寫(xiě)權(quán)限分配給文件的屬主,并將你設(shè)成文件的屬主。一旦創(chuàng)建了文件,你就在腳本中有了完整的讀寫(xiě)權(quán)限,但其他人沒(méi)法訪問(wèn)它(當(dāng)然,root用戶除外)。

      • 要用 mktemp 命令在本地目錄中創(chuàng)建一個(gè)臨時(shí)文件,你只要指定一個(gè)文件名模板就行了。模板可以包含任意文本文件名,在文件名末尾加上6個(gè) X 就行了。mktemp 命令會(huì)用6個(gè)字符碼替換這6個(gè) X ,從而保證文件名在目錄中是唯一的。·mktemp Everest.XXXXXX·
      • ·mktemp` // 在 /tmp文件夾中創(chuàng)建一個(gè)臨時(shí)文件,系統(tǒng)隨機(jī)命名
      • ·mktemp Everest.XXXXXX· // 必須是6個(gè)X。Everest是模板前綴。 在當(dāng)前文件夾下創(chuàng)建一個(gè)模板前綴的臨時(shí)文件。
      • ·mktemp -t Everest.XXXXXX· // -t選項(xiàng)在/tmp下創(chuàng)建一個(gè)模板前綴的臨時(shí)文件.
      • `mktemp -d  Everest.XXXXXX` //-d創(chuàng)建文件夾而非文件
      • ·mktemp -dt Everest.XXXXXX· // 在/tmp文件夾下創(chuàng)建一個(gè)文件夾

      ※,記錄消息:將輸出同時(shí)發(fā)送到顯示器和日志文件,不用將輸出重定向兩次,只要用特殊的 tee 命令就行。tee 命令相當(dāng)于管道的一個(gè)T型接頭。它將從 STDIN 過(guò)來(lái)的數(shù)據(jù)同時(shí)發(fā)往兩處。一處是STDOUT ,另一處是 tee 命令行所指定的文件名。這樣既能將數(shù)據(jù)保存在文件中,也能將數(shù)據(jù)顯示在屏幕上。

      • ·tee filename· 
      • ·date | tee testFile· // 由于 tee 會(huì)重定向來(lái)自 STDIN 的數(shù)據(jù),你可以用它配合管道命令來(lái)重定向命令輸出。注意,默認(rèn)情況下, tee 命令會(huì)在每次使用時(shí)覆蓋輸出文件內(nèi)容。
      • ·date | tee -a testFile· // 如果你想將數(shù)據(jù)追加到文件中,必須用 -a 選項(xiàng)。

      ※,實(shí)例:從csv文件中讀取數(shù)據(jù),然后創(chuàng)建SQL語(yǔ)句插入MySQL數(shù)據(jù)庫(kù)。

      $cat test23
      #!/bin/bash
      # read file and create INSERT statements for MySQL
      outfile='members.sql'
      IFS=','
      while read lname fname address city state zip
      do
      cat >> $outfile << EOF
      INSERT INTO members (lname,fname,address,city,state,zip) VALUES
      ('$lname', '$fname', '$address', '$city', '$state', '$zip');
      EOF
      done < ${1}
      $

      腳本中出現(xiàn)了三個(gè)重定向!!!運(yùn)行腳本時(shí),顯示器上不會(huì)出現(xiàn)任何輸出,但是在member.sql文件中生成了insert語(yǔ)句。

      • while循環(huán)的done語(yǔ)句后的重定向。當(dāng)運(yùn)行程序 test23 時(shí), $1 代表第一個(gè)命令行參數(shù)。它指明了待讀取數(shù)據(jù)的文件(csv文件)。 read 語(yǔ)句會(huì)使用 IFS 字符解析讀入的文本,我們?cè)谶@里將 IFS 指定為逗號(hào)
      • cat >> $outfile << EOF。這條語(yǔ)句中包含兩個(gè)重定向,一個(gè)輸出追加重定向(雙大于號(hào))和一個(gè)輸入追加重定向(雙小于號(hào))。意思是 cat命令的輸入和輸出都被重定向了。cat命令的輸出重定向至$outfile指定的文件(追加), cat命令的輸入也不再取自標(biāo)準(zhǔn)輸入,而是被重定向至一個(gè)內(nèi)聯(lián)輸入重定向(EOF標(biāo)記了內(nèi)聯(lián)輸入的開(kāi)始和結(jié)束)。


      第16章:控制腳本

      ※,處理信號(hào):Linux利用信號(hào)與運(yùn)行在系統(tǒng)中的進(jìn)程進(jìn)行通信。在Linux中,進(jìn)程之間通過(guò)信號(hào)來(lái)通信。進(jìn)程的信號(hào)就是預(yù)定義好的一個(gè)消息,進(jìn)程能識(shí)別它并決定忽略還是作出反應(yīng)。進(jìn)程如何處理信號(hào)是由開(kāi)發(fā)人員通過(guò)編程來(lái)決定的。大多數(shù)編寫(xiě)完善的程序都能接收和處理標(biāo)準(zhǔn)Unix進(jìn)程信號(hào)

      默認(rèn)情況下,bash shell會(huì)忽略收到的任何 SIGQUIT (3) 和 SIGTERM (5) 信號(hào)(正因?yàn)檫@樣,交互式shell才不會(huì)被意外終止)。但是bash shell會(huì)處理收到的 SIGHUP (1) 和 SIGINT (2) 信號(hào)。如果bash shell收到了 SIGHUP 信號(hào),比如當(dāng)你要離開(kāi)一個(gè)交互式shell,它就會(huì)退出。但在退出之前,它會(huì)將 SIGHUP 信號(hào)傳給所有由該shell所啟動(dòng)的進(jìn)程(包括正在運(yùn)行的shell腳本)。通過(guò) SIGINT 信號(hào),可以中斷shell。Linux內(nèi)核會(huì)停止為shell分配CPU處理時(shí)間。這種情況發(fā)生時(shí),shell會(huì)將 SIGINT 信號(hào)傳給所有由它所啟動(dòng)的進(jìn)程,以此告知出現(xiàn)的狀況。

      kill命令:根據(jù)pid殺掉單獨(dú)一個(gè)進(jìn)程。默認(rèn)信號(hào):SIGTERM

      • ·kill -l· //小寫(xiě)的L, 列出所有的信號(hào)。幾個(gè)常見(jiàn)的信號(hào),前面的數(shù)字是其代號(hào)
        • 1) SIGHUP : 掛起進(jìn)程
        • 2) SIGINT : 終止進(jìn)程
        • 9) SIGKILL : 無(wú)條件終止進(jìn)程
      • ·kill pid` 默認(rèn)向進(jìn)程pid發(fā)送一個(gè)TERM(15)的信號(hào),15相比9比較優(yōu)雅的終止一個(gè)進(jìn)程。
      • ·kill -15 <pid>`
      • `kill -term <pid>` OR `kill -TERM <pid>`
      • `kill -SIGTERM <pid>` //注意此處-SIGTERM不能小寫(xiě)
      • ·kill -0 <pid>`// -0參數(shù)不會(huì)向進(jìn)程發(fā)送任何信號(hào),不會(huì)殺掉進(jìn)程,而是檢查進(jìn)程是否存在,如果存在則返回0,如果不存在則返回1。

      killall命令:根據(jù)名稱(精確匹配或正則(-r參數(shù)))批量殺掉進(jìn)程。默認(rèn)信號(hào):SIGTERM。

      • `killall xxxx`  killall 命令非常強(qiáng)大,它支持通過(guò)進(jìn)程名而不是PID來(lái)結(jié)束進(jìn)程。 killall 命令也支持通配符,這在系統(tǒng)因負(fù)載過(guò)大而變得很慢時(shí)很有用。慎用!
      • `killall <p-name>`// 殺掉所有名名稱精確匹配為p-name的進(jìn)程,這里殺掉的是名稱精確匹配為p-name的進(jìn)程,比如寫(xiě)ssh殺不掉sshd進(jìn)程。
        • 進(jìn)程名稱p-name指的shell命令或者是運(yùn)行的二進(jìn)制文件名稱,文件的路徑名稱不屬于進(jìn)程名稱的一部分
          [root@sungrow27 grafanaloki]$ps -ef |grep grafanaloki
          root      342485 1733488  0 17:10 pts/3    00:00:00 grep --color=auto grafanaloki
          root      374301 2766094  0 7月11 pts/2   00:18:17 /data1/tong/grafanaloki/bin/loki-linux-amd64 -config.file=/data1/tong/grafanaloki/config/loki-local-config.yaml
          root     2419533 2766094  1 7月12 pts/2   00:26:48 /data1/tong/grafanaloki/bin/promtail-linux-amd64 -config.file=/data1/tong/grafanaloki/config/promtail-local-config.yaml
          [root@sungrow27 grafanaloki]$killall -0 grafanaloki
          grafanaloki: 未找到進(jìn)程
          [root@sungrow27 grafanaloki]$killall -0 -r loki
          [root@sungrow27 grafanaloki]$
        • 當(dāng)直接運(yùn)行shell腳本時(shí),shell腳本的名稱并不是進(jìn)程的名稱。當(dāng)以bash <script.sh>方式運(yùn)行shell腳本時(shí),會(huì)有一個(gè)名為bash的進(jìn)程。
      • `killall -i <p-name>` // -i選項(xiàng),交互式進(jìn)行。
      • ·killall -r <p-regular-name>`//殺掉正則匹配為p-regular-name的所有進(jìn)程。這里的正則為:*代表0或多個(gè),.代表單個(gè)字符,?代表0或1個(gè)。
      • `killall -0 <name>` //-0參數(shù)不會(huì)向進(jìn)程發(fā)送任何信號(hào),不會(huì)殺掉進(jìn)程,而是檢查進(jìn)程是否存在,如果存在則返回0,如果不存在則返回1。
      • `killall -0 -r cr.*` //檢查正則名稱為cr.*的進(jìn)程是否存在。

      pkill命令:默認(rèn)信號(hào):SIGTERM。pkill命令和killall命令類似,都是根據(jù)名稱批量處理一批進(jìn)程。不同點(diǎn)在于pkill不加選項(xiàng)默認(rèn)就是正則名稱,killall不加參數(shù)默認(rèn)是精確匹配。

      pkill命令和另一個(gè)命令pgrep是同源命令,pgrep用名稱(正則名稱)或其他屬性查找進(jìn)程并打印到標(biāo)準(zhǔn)輸出,pkill則對(duì)匹配到的所有進(jìn)程發(fā)送指定的信號(hào),默認(rèn)是SIGTERM信號(hào)。

      pgrep, pkill - look up or signal processes based on name and other attributes

      • ·pkill -15 cr` //殺掉正則匹配cr的所有進(jìn)程。如cron進(jìn)程。
      • ·pkill -15 -u root cr· // 殺掉正則匹配cr并且屬于root用戶的所有進(jìn)程。
      • ·pkill -15 ^cr$· 殺掉正則匹配^cr$的所有進(jìn)程,即名稱精確為cr的所有進(jìn)程。
      • ·pkill -0 cr· // -0參數(shù)不會(huì)向進(jìn)程發(fā)送任何信號(hào),不會(huì)殺掉進(jìn)程,而是檢查進(jìn)程是否存在,如果存在則返回0,如果不存在則返回1。
      • `pgrep ssh` // 查找正則名稱為ssh的所有進(jìn)程
      • ·pgrep -u root ^ssh$· //查找正則名稱為^ssh$且屬于用戶root的所有進(jìn)程
      • `pgrep firefox -f` //-f參數(shù)即--full。此參數(shù)use full process name to match,所謂的full process name就是帶著所有啟動(dòng)參數(shù)的名稱,也就是ps -ef中cmd列的所有描述字符。
      • ·pkill -15 firefox -f· // -f參數(shù)同上

      ※,生成信號(hào):bash shell允許用鍵盤(pán)上的組合鍵生成兩種基本的Linux信號(hào)。這個(gè)特性在需要停止或暫停失控程序時(shí)非常方便。

      • Ctrl+C組合鍵會(huì)生成 SIGINT 信號(hào)。中斷進(jìn)程。
      • Ctrl+Z組合鍵會(huì)生成一個(gè) SIGTSTP 信號(hào),停止shell中運(yùn)行的任何進(jìn)程。停止(stopping)進(jìn)程跟終止(terminating)進(jìn)程不同:停止進(jìn)程會(huì)讓程序繼續(xù)保留在內(nèi)存中,并能從上次停止的位置繼續(xù)運(yùn)行。你可以在進(jìn)程運(yùn)行期間暫停進(jìn)程,而無(wú)需終止它。盡管有時(shí)這可能會(huì)比較危險(xiǎn)(比如,腳本打開(kāi)了一個(gè)關(guān)鍵的系統(tǒng)文件的文件鎖),但通常它可以在不終止進(jìn)程的情況下使你能夠深入腳本內(nèi)部一窺究竟

      ※,捕獲信號(hào):也可以不忽略信號(hào),在信號(hào)出現(xiàn)時(shí)捕獲它們并執(zhí)行其他命令。trap 命令允許你來(lái)指定shell腳本要監(jiān)看并從shell中攔截的Linux信號(hào)。如果腳本收到了 trap 命令中列出的信號(hào),該信號(hào)不再由shell處理,而是交由本地處理。

      • ·trap commands signals· // 在 trap 命令行上,你只要列出想要shell執(zhí)行的命令,以及一組用格分開(kāi)的待捕獲的信號(hào)。你可以用數(shù)值或Linux信號(hào)名來(lái)指定信號(hào)。
      • `trap "echo ' Sorry! I have trapped Ctrl-C'" SIGINT` // 本例中用到的 trap 命令會(huì)在每次檢測(cè)到 SIGINT 信號(hào)時(shí)顯示一行簡(jiǎn)單的文本消息。捕獲這些信號(hào)會(huì)阻止用戶用bash shell組合鍵Ctrl+C來(lái)停止程序。

      除了在shell腳本中捕獲信號(hào),你也可以在shell腳本退出時(shí)進(jìn)行捕獲。這是在shell完成任務(wù)時(shí)執(zhí)行命令的一種簡(jiǎn)便方法。要捕獲shell腳本的退出,只要在 trap 命令后加上 EXIT 信號(hào)就行,如·trap "echo Goodbye..." EXIT·。

      也可以刪除已設(shè)置好的捕獲。只需要在 trap 命令與希望恢復(fù)默認(rèn)行為的信號(hào)列表之間加上兩個(gè)破折號(hào)就行了。·trap -- SIGINT·

      ※,以后臺(tái)模式運(yùn)行腳本:

      • 以后臺(tái)模式運(yùn)行shell腳本非常簡(jiǎn)單。只要在命令后加個(gè) & 符就行了。當(dāng) & 符放到命令后時(shí),它會(huì)將命令和bash shell分離開(kāi)來(lái),將命令作為系統(tǒng)中的一個(gè)獨(dú)立的后臺(tái)進(jìn)程運(yùn)行。注意:當(dāng)后臺(tái)進(jìn)程運(yùn)行時(shí),它仍然會(huì)使用終端顯示器來(lái)顯示 STDOUT 和 STDERR 消息。
      • 在終端會(huì)話中使用后臺(tái)進(jìn)程時(shí)一定要小心。在 ps 命令的輸出中,每一個(gè)后臺(tái)進(jìn)程都和其所在的終端會(huì)話(如 pts/0 )聯(lián)系在一起。如果終端會(huì)話退出,那么后臺(tái)進(jìn)程也會(huì)隨之退出。
      • 如果希望運(yùn)行在后臺(tái)模式的腳本在登出控制臺(tái)后能夠繼續(xù)運(yùn)行,可以使用 nohup 命令。nohup 命令運(yùn)行了另外一個(gè)命令來(lái)阻斷所有發(fā)送給該進(jìn)程的 SIGHUP 信號(hào)。這會(huì)在退出終端會(huì)話時(shí)阻止進(jìn)程退出。如·nohup ./test1.sh &·  在你使用 nohup 命令時(shí),如果關(guān)閉該會(huì)話,腳本會(huì)忽略終端會(huì)話發(fā)過(guò)來(lái)的 SIGHUP 信號(hào)。由于 nohup 命令會(huì)解除終端與進(jìn)程的關(guān)聯(lián),進(jìn)程也就不再同 STDOUT 和 STDERR 聯(lián)系在一起。為了保存該命令產(chǎn)生的輸出, nohup 命令會(huì)自動(dòng)將 STDOUT 和 STDERR 的消息重定向到一個(gè)名為nohup.out的文件中。

      ※,nohup、&、setsid 讓命令在后臺(tái)可靠運(yùn)行

      當(dāng)用戶注銷(logout)或者網(wǎng)絡(luò)斷開(kāi)時(shí),當(dāng)前終端會(huì)收到 SIGHUP(hangup)信號(hào)從而關(guān)閉其所有子進(jìn)程。因此,解決辦法就有兩種:要么讓進(jìn)程忽略 HUP 信號(hào)(nohup命令),要么讓進(jìn)程運(yùn)行在新的會(huì)話里從而成為不屬于此終端的子進(jìn)程(setsid命令)

      nohup 和 & 測(cè)試 https://mp.weixin.qq.com/s/nyT-FPdIUdJUiUCYVGEnTg

      • 直接運(yùn)行命令,程序運(yùn)行,顯示輸出信息,按 ctrl + c 程序會(huì)受到SIGINT(signal interrupt)信號(hào),程序停止運(yùn)行。關(guān)閉session窗口,程序收到SIGHUP(signal hangup),程序停止運(yùn)行。
      • 以&結(jié)尾運(yùn)行命令,程序在后臺(tái)運(yùn)行,輸出信息會(huì)顯示在前臺(tái)。ctrl + c 程序繼續(xù)運(yùn)行,關(guān)閉session,程序停止運(yùn)行。
      • 以 nohup 運(yùn)行命令,讓命令忽略SIGHUP(hangup)信號(hào),標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤缺省會(huì)被重定向到名稱為nohup.out的文件中(如果無(wú)權(quán)限則會(huì)寫(xiě)入${HOME}/nohup.out)。ctrl + c程序停止運(yùn)行,關(guān)閉session,程序繼續(xù)運(yùn)行。
      • nohup和&同時(shí)運(yùn)行命令,程序在后臺(tái)運(yùn)行,輸出寫(xiě)入nohup.out文件。ctrl +c程序繼續(xù)運(yùn)行,關(guān)閉session,程序繼續(xù)運(yùn)行。也就是說(shuō) 平日線上經(jīng)常使用nohup和&配合來(lái)啟動(dòng)程序同時(shí)免疫SIGINT和SIGHUP信號(hào)。

      在實(shí)際工作中遇到一個(gè)奇怪的案例,使用Jumpserver堡壘機(jī)連接某個(gè)服務(wù)器,使用nohup npm run start &啟動(dòng)成功后,直接關(guān)閉窗口(即session)后發(fā)現(xiàn)npm啟動(dòng)的node程序也沒(méi)了,和期望的不太一致,原理不太清楚,查找了下資料(此文)找到解決方法:

      • nohup npm run start &運(yùn)行后啟動(dòng)了node進(jìn)程
      • 然后按任意鍵進(jìn)入shell
      • 然后在當(dāng)前shell中輸入exit命令退出當(dāng)前shell
      • 然后關(guān)閉session(即關(guān)閉窗口)發(fā)現(xiàn)node進(jìn)程依然存在,但是發(fā)現(xiàn)了一個(gè)現(xiàn)象:此時(shí)的node進(jìn)程的ID和剛開(kāi)始的node進(jìn)程的ID不一樣了。

      setsid: 在新的session中執(zhí)行命令,使命令的進(jìn)程不屬于接收SIGHUP信號(hào)的終端會(huì)話的子進(jìn)程,那么就不會(huì)受到當(dāng)前終端的SIGHUP信號(hào)的影響。

      • setsid [options] <program> [arguments...]
      • ·setsid ./gogs web· //不需要在后面加&

      ※,作業(yè)控制

      • ·jobs· // 查看shell當(dāng)前正在處理的作業(yè)
      • `jobs -l` // 小寫(xiě)L,查看pid
      • jobs 命令輸出中的加號(hào)和減號(hào)。帶加號(hào)的作業(yè)會(huì)被當(dāng)做默認(rèn)作業(yè)。在使用作業(yè)控制命令時(shí),如果未在命令行指定任何作業(yè)號(hào),該作業(yè)會(huì)被當(dāng)成作業(yè)控制命令的操作對(duì)象。當(dāng)前的默認(rèn)作業(yè)完成處理后,帶減號(hào)的作業(yè)成為下一個(gè)默認(rèn)作業(yè)。任何時(shí)候都只有一個(gè)帶加號(hào)的作業(yè)和一個(gè)帶減號(hào)的作業(yè),不管shell中有多少個(gè)正在運(yùn)行的作業(yè)。

      ※,重啟停止的作業(yè):在bash作業(yè)控制中,可以將已停止的作業(yè)作為后臺(tái)進(jìn)程或前臺(tái)進(jìn)程重啟。前臺(tái)進(jìn)程會(huì)接管你當(dāng)前工作的終端,所以在使用該功能時(shí)要小心了

      • ·bg· //以后臺(tái)模式重啟默認(rèn)作業(yè)
      • `bg <jobsId>`
      • ·fg· // 以前臺(tái)模式重啟默認(rèn)作業(yè)
      • `fg <jobsId>`

      ※,調(diào)整謙讓度:

      在多任務(wù)操作系統(tǒng)中(Linux就是),內(nèi)核負(fù)責(zé)將CPU時(shí)間分配給系統(tǒng)上運(yùn)行的每個(gè)進(jìn)程。調(diào)度優(yōu)先級(jí)(scheduling priority)是內(nèi)核分配給進(jìn)程的CPU時(shí)間(相對(duì)于其他進(jìn)程)。在Linux系統(tǒng)中,由shell啟動(dòng)的所有進(jìn)程的調(diào)度優(yōu)先級(jí)默認(rèn)都是相同的。調(diào)度優(yōu)先級(jí)是個(gè)整數(shù)值,從 -20(最高優(yōu)先級(jí))到+19(最低優(yōu)先級(jí))。默認(rèn)情況下,bash shell以優(yōu)先級(jí)0來(lái)啟動(dòng)所有進(jìn)程。

      • ·ps -p 8088 -o pid,ppid,ni,cmd· // PS命令-o參數(shù)指定輸出列名,注意用逗號(hào)分割,中間不能有空格!!!!ni代表的即是此進(jìn)程優(yōu)先級(jí)。
      • ·nice -n 10 ./test.sh > test.log &· // nice 命令允許你設(shè)置命令啟動(dòng)時(shí)的調(diào)度優(yōu)先級(jí).必須將 nice 命令和要啟動(dòng)的命令放在同一行中。nice 命令阻止普通系統(tǒng)用戶來(lái)提高命令的優(yōu)先級(jí)。
      • `renice -n 10 -p 8088` //  renice 命令可以改變系統(tǒng)上已運(yùn)行命令的優(yōu)先級(jí)。注意:①非root只能對(duì)屬于你的進(jìn)程執(zhí)行 renice且只能通過(guò) renice 降低進(jìn)程的優(yōu)先級(jí);②root用戶可以隨意降低或提高任何進(jìn)程的優(yōu)先級(jí)。

      ※,定時(shí)任務(wù)

      1, 用 at 命令來(lái)計(jì)劃執(zhí)行作業(yè)。atd 守護(hù)進(jìn)程會(huì)檢查系統(tǒng)上的一個(gè)特殊目錄(通常位于/var/spool/at)來(lái)獲取用 at 命令提交的作業(yè)在你使用 at 命令時(shí),該作業(yè)會(huì)被提交到作業(yè)隊(duì)列(job queue)。作業(yè)隊(duì)列會(huì)保存通過(guò) at 命令提交的待處理的作業(yè)。

      • ·at [-f filename] time· // 默認(rèn)情況下, at 命令會(huì)將 STDIN 的輸入放到隊(duì)列中。你可以用 -f 參數(shù)來(lái)指定用于讀取命令(腳本文件)的文件名。
      • `atq` // atq 命令可以查看系統(tǒng)中有哪些at作業(yè)在等待。
      • ·atrm <at作業(yè)號(hào)>· // 用 atrm 命令來(lái)刪除等待中的作at業(yè)

      2, 安排需要定期執(zhí)行的腳本。Linux系統(tǒng)使用cron程序來(lái)安排要定期執(zhí)行的作業(yè)。cron程序會(huì)在后臺(tái)運(yùn)行并檢查一個(gè)特殊的表(被稱作cron時(shí)間表),以獲知已安排執(zhí)行的作業(yè)。

      • min hour dayofmonth month dayofweek command  // cron時(shí)間表采用一種特別的格式來(lái)指定作業(yè)何時(shí)運(yùn)行。

        如何設(shè)置一個(gè)在每個(gè)月的最后一天執(zhí)行的命令,因?yàn)槟銦o(wú)法設(shè)置
        dayofmonth的值來(lái)涵蓋所有的月份。這個(gè)問(wèn)題困擾著Linux和Unix程序員,也激發(fā)了不少解
        決辦法。常用的方法是加一條使用 date 命令的 if-then 語(yǔ)句來(lái)檢查明天的日期是不是01:
        00 12 * * * if [ ` date +%d -d tomorrow ` = 01 ] ; then ; command
        它會(huì)在每天中午12點(diǎn)來(lái)檢查是不是當(dāng)月的最后一天,如果是,cron將會(huì)運(yùn)行該命令
      • 每個(gè)系統(tǒng)用戶(包括root用戶)都可以用自己的cron時(shí)間表來(lái)運(yùn)行安排好的任務(wù)。Linux提供了 crontab 命令來(lái)處理cron時(shí)間表.

        • ·crontab -l· // 要列出已有的cron時(shí)間表
        • `crontab -e` // 默認(rèn)情況下,用戶的cron時(shí)間表文件并不存在。要為cron時(shí)間表添加條目,可以用 -e 選項(xiàng)。
      • 如果你創(chuàng)建的腳本對(duì)精確的執(zhí)行時(shí)間要求不高,用預(yù)配置的cron腳本目錄會(huì)更方便。有4個(gè)基本目錄:hourly、daily、monthly和weekly。

        • `ls /etc/cron.*ly` // 如果腳本需要每天運(yùn)行一次,只要將腳本復(fù)制到daily目錄,cron就會(huì)每天執(zhí)行它。
        •  
      • anacron程序: 如果某個(gè)作業(yè)在cron時(shí)間表中安排運(yùn)行的時(shí)間已到,但這時(shí)候Linux系統(tǒng)處于關(guān)機(jī)狀態(tài),那么這個(gè)作業(yè)就不會(huì)被運(yùn)行。當(dāng)系統(tǒng)開(kāi)機(jī)時(shí),cron程序不會(huì)再去運(yùn)行那些錯(cuò)過(guò)的作業(yè)。而anacron知道某個(gè)作業(yè)錯(cuò)過(guò)了執(zhí)行時(shí)間,它會(huì)盡快運(yùn)行該作業(yè)。anacron程序只會(huì)處理位于cron目錄的程序,比如/etc/cron.monthly。。它用時(shí)間戳來(lái)決定作業(yè)是否在正確的計(jì)劃間隔內(nèi)運(yùn)行了。每個(gè)cron目錄都有個(gè)時(shí)間戳文件,該文件位于/var/spool/anacron。anacron時(shí)間表的基本格式和cron時(shí)間表略有不同:
        • period delay identifier command // period條目定義了作業(yè)多久運(yùn)行一次,以天為單位。注意,anacron不會(huì)運(yùn)行位于/etc/cron.hourly的腳本。這是因?yàn)閍nacron程序不會(huì)處理執(zhí)行時(shí)間需求小于一天的腳本。

      ※,shell腳本獲取后臺(tái)運(yùn)行任務(wù)的返回值

      當(dāng)使用·&·把進(jìn)程放入后臺(tái)以后,如果需要了解進(jìn)程的執(zhí)行情況,可以使用wait函數(shù)。默認(rèn)情況下wait會(huì)等待任意子進(jìn)程結(jié)束但是不會(huì)返回子進(jìn)程的返回值。而以子進(jìn)程的pid作為參數(shù)調(diào)用wait時(shí),wait便能夠返回該子進(jìn)程的退出狀態(tài)了。

      例一:

      #!/bin/bash
      ./etcdkeeper -p 9081 &
      wait $! # 注意:如果上面 的命令是個(gè)死循環(huán),那么這里也會(huì)卡住。
      status=$?
      if [[ status -gt 0 ]]; then
          echo "程序運(yùn)行出錯(cuò)"
      else 
          echo "程序已啟動(dòng)"
      fi

      例二:

      #!/bin/bash
      dir=`dirname $`
      $dir/test01.sh &
      $dir/test02.sh &
      echo '' > $dir/tmp.log
      for pid in $(jobs -p)
      do
      wait $pid
      status=$?
      if [ $status !=  ];then
      echo "$pid status is $status have some error!" >> $dir/tmp.log
      else
      echo "$pid status is $status success!" >> $dir/tmp.log
      fi
      done

       



       

      第17章:使用函數(shù)

      ※,創(chuàng)建函數(shù):bash shell中有兩種定義函數(shù)的方式:

      ·function name {      // 注意:無(wú)括號(hào)!name 屬性定義了賦予函數(shù)的唯一名稱。腳本中定義的每個(gè)函數(shù)都必須有一個(gè)唯一的名稱
      commands

      OR

      ·name () {
      commands

      ※,使用/調(diào)用 函數(shù):要在腳本中使用函數(shù),只需要像其他shell命令一樣,在行中指定函數(shù)名就行了。注意函數(shù)調(diào)用前必須已經(jīng)定義了此函數(shù)。

      #!/bin/bash
      function myFun {
          echo "good"
      }
      myFun # 此處調(diào)用函數(shù)myFun

      ※,返回值:bash shell會(huì)把函數(shù)當(dāng)作一個(gè)小型腳本,運(yùn)行結(jié)束時(shí)會(huì)返回一個(gè)退出狀態(tài)碼。有3種不同的方法來(lái)為函數(shù)生成退出狀態(tài)碼。

      ①默認(rèn)退出狀態(tài)碼:默認(rèn)情況下,函數(shù)的退出狀態(tài)碼是函數(shù)中最后一條命令返回的退出狀態(tài)碼,在函數(shù)執(zhí)行結(jié)束后,可以用標(biāo)準(zhǔn)變量 $? 來(lái)確定函數(shù)的退出狀態(tài)碼。

      #!/bin/bash
      function myFun {
          echo "good"
          ls nonexistsfile
      }
      myFun # 此處調(diào)用函數(shù)myFun
      echo "myFun exits status is: $?"
      
      windows@Tonus:~/shellScript$ bash myfun.sh 
      good
      ls: 無(wú)法訪問(wèn) 'nonexistsfile': 沒(méi)有那個(gè)文件或目錄
      myFun exits status is: 2
      windows@Tonus:~/shellScript$ 

      ②return 命令來(lái)返回?cái)?shù)值。 return 命令可以基于函數(shù)的結(jié)果,通過(guò)編程的方式將函數(shù)的退出狀態(tài)碼設(shè)為特定值(return返回的值只能在0-255之間)。

      • return和exit的返回值能用echo $?獲取
        •  記住,函數(shù)一結(jié)束就取返回值;如果在用 $? 變量提取函數(shù)返回值之前執(zhí)行了其他命令,函數(shù)的返回值就會(huì)丟失。記住, $?變量會(huì)返回執(zhí)行的最后一條命令的退出狀態(tài)碼。
      •  記住,退出狀態(tài)碼必須是0~255。任何大于256的值都會(huì)被取模,即除以256的余數(shù)。如return 257 實(shí)際會(huì)被返回1.
      • return 的作用是退出當(dāng)前函數(shù),不退出整個(gè)腳本
      • 函數(shù)中return 后面的命令一概不執(zhí)行。這點(diǎn)和Java一樣,函數(shù)中return語(yǔ)句后面的都不會(huì)再執(zhí)行
      • exit代表退出整個(gè)腳本
      •  
      windows@Tonus:~/shellScript$ cat myfun.sh 
      #!/bin/bash
      function myFun {
          echo "good"
          ls nonexistsfile
          return 33
      }
      myFun # 此處調(diào)用函數(shù)myFun
      echo "myFun exits status is: $?"
      windows@Tonus:~/shellScript$ bash myfun.sh 
      good
      ls: 無(wú)法訪問(wèn) 'nonexistsfile': 沒(méi)有那個(gè)文件或目錄
      myFun exits status is: 33
      windows@Tonus:~/shellScript$ 

      ③函數(shù)中使用echo語(yǔ)句返回(輸出):正如可以將命令的輸出保存到shell變量中一樣,也可以用這種技術(shù)來(lái)獲得任何類型的函數(shù)輸出,并將其保存到變量中(使用命令替換,即反引號(hào)或$() ,只要涉及到將函數(shù)結(jié)果保存至變量中都需要使用命令替換!)。如: result=$(myFun), 這個(gè)命令會(huì)將myFun函數(shù)的輸出保存到變量$result中。

      ※,傳參調(diào)用函數(shù):可以在函數(shù)中使用shell變量(如$0, $1..., $@, $*, $#等等),對(duì)其賦值以及從中取值。這樣你就能將任何類型的數(shù)據(jù)從主體腳本程序的腳本函數(shù)中傳入傳出

      windows@Tonus:~/shellScript$ cat myfun.sh
      #!/bin/bash
      function myFun {
      echo 參數(shù)為$1
      }
      myFun param1 #此處調(diào)用函數(shù)myFun,并傳參
      result=$(myFun hello) # 傳參調(diào)用函數(shù),并將函數(shù)結(jié)果保存至變量中。使用$()后,myFun函數(shù)體內(nèi)的echo打印并不會(huì)打印了。
      echo $result
      windows@Tonus:~/shellScript$ bash myfun.sh
      參數(shù)為param1
      參數(shù)為hello
      windows@Tonus:~/shellScript$

      ※,作用域

      1,默認(rèn)情況下,你在腳本中定義的任何變量都是全局變量。在函數(shù)外定義的變量可在函數(shù)內(nèi)正常訪問(wèn)。函數(shù)內(nèi)默認(rèn)的變量也是全局變量,可以將函數(shù)內(nèi)的變量定義為局部變量,只要在變量前加local關(guān)鍵字即可,局部變量只會(huì)在函數(shù)內(nèi)有效,可以和外部某變量重名,但是兩個(gè)變量是獨(dú)立的。注意下面一種情況, 使用 變量替換時(shí)的一種情況:

      #!/bin/bash
      myfun(){
          name="Everest"
          echo "hello world"
      }
      myfun #調(diào)用函數(shù)myfun,執(zhí)行后將會(huì)使name變量可見(jiàn),而且也會(huì)打印 hello world
      echo $name #這里會(huì)打印出來(lái)name的值。
      
      # "這也是一種定義函數(shù)的語(yǔ)法,注意沒(méi)有小括號(hào)"
      function myfun1 {
          hobby="football"
          echo "hi world"
      }
      #注意變量替換中先執(zhí)行myfun1,這是在fork的一個(gè)子進(jìn)程中執(zhí)行的,不會(huì)使hobby變量對(duì)當(dāng)前腳本可見(jiàn)。
      # 也不會(huì)打印hi world
      myfun1Value=$(myfun1) 
      echo hobby:$hobby #打印空, hobby無(wú)值
      echo $myfun1Value # 打印 hi world
      
      windows@Tonus:~/shellScript$ bash functionShell.sh  // bash + 腳本文件的方式運(yùn)行腳本時(shí),腳本可以沒(méi)有x可執(zhí)行權(quán)限!
      hello world
      Everest
      hobby:
      hi world

      ※,函數(shù)遞歸

      階乘示例

      #!/bin/bash
      factorial() {
          if [ $1 -eq 1 ];then
              echo 1
          else
              local temp=$[ $1 - 1 ]
              local result=$(factorial $temp)
              echo $[ $result * $1 ]
          fi
      }
      
      read -p "Enter the Value:" value
      echo $(factorial $value)

      ※,創(chuàng)建庫(kù): bash shell允許創(chuàng)建函數(shù)庫(kù)文件,然后在多個(gè)腳本中引用該庫(kù)文件

      ★,source命令:source命令可以使Shell讀入指定的Shell程序文件并依次執(zhí)行文件中的所有語(yǔ)句。source命令通常用于重新執(zhí)行剛修改的初始化文件,使之立即生效,而不必注銷并重新登錄。

      • ·source filename·:這個(gè)命令其實(shí)只是簡(jiǎn)單地讀取腳本里面的語(yǔ)句依次在當(dāng)前shell里面執(zhí)行,沒(méi)有建立新的子shell。那么腳本里面所有新建、改變變量的語(yǔ)句都會(huì)保存在當(dāng)前shell里面。
      • 當(dāng)shell腳本具有可執(zhí)行權(quán)限時(shí),用·sh filename·與·./filename·執(zhí)行腳本是沒(méi)有區(qū)別的。 兩者都會(huì)重新建立一個(gè)子shell,在子shell中執(zhí)行腳本里面的語(yǔ)句,該子shell繼承父shell的環(huán)境變量,但子shell新建的、改變的變量不會(huì)被帶回父shell,除非使用export。

      1. 第一步是創(chuàng)建一個(gè)包含腳本中所需函數(shù)的公用庫(kù)文件。如創(chuàng)建一個(gè)名為 funlib 的公共庫(kù)文件。

      $ cat funlib
      
      #!/bin/bash
      function addem {
          echo $[ $1 + $2 ]
      }
      
      multiem(){
          echo $(($1 * $2))
      }

      下一步是在用到這些函數(shù)的腳本文件中包含 funlib 庫(kù)文件。從這里開(kāi)始,事情就變復(fù)雜了。問(wèn)題出在shell函數(shù)的作用域上。和環(huán)境變量一樣,shell函數(shù)僅在定義它的shell會(huì)話內(nèi)有效。如果你在shell命令行界面的提示符下運(yùn)行 funlib 腳本,shell會(huì)創(chuàng)建一個(gè)新的shell并在其中運(yùn)行這個(gè)腳本。它會(huì)為那個(gè)新shell定義這三個(gè)函數(shù),但當(dāng)你運(yùn)行另外一個(gè)要用到這些函數(shù)的腳本時(shí),它們是無(wú)法使用的。這同樣適用于腳本。如果你嘗試像普通腳本文件那樣運(yùn)行庫(kù)文件,函數(shù)并不會(huì)出現(xiàn)在腳本中。下面是錯(cuò)誤適用庫(kù)文件的示例

      $ cat badTest.sh
      
      #!/bin/bash
      # using a library file the wrong way
      ./myfuncs # 像普通腳本文件那樣運(yùn)行庫(kù)文件,函數(shù)并不會(huì)出現(xiàn)在腳本中。錯(cuò)誤的使用庫(kù)文件!
      result=$(addem 10 15)
      echo "The result is $result"
      $
      $ ./badtest4
      ./badtest4: addem: command not found
      The result is
      $

      使用函數(shù)庫(kù)的關(guān)鍵在于 source 命令。 source 命令會(huì)在當(dāng)前shell上下文中執(zhí)行命令,而不是創(chuàng)建一個(gè)新shell。可以用 source 命令來(lái)在shell腳本中運(yùn)行庫(kù)文件腳本。這樣腳本就可以使用庫(kù)中的函數(shù)了。source 命令有個(gè)快捷的別名,稱作點(diǎn)操作符(dot operator)。這里有個(gè)用 funlib 庫(kù)文件創(chuàng)建腳本的例子.

      $ cat test14
      #!/bin/bash
      # using functions defined in a library file
      # source ./funlib #和下面一行等同。
      . ./funlib #假設(shè)庫(kù)文件和此腳本位于同一目錄。source后,funlib中的函數(shù)就像在當(dāng)前文件中定義的一樣。
      value1=10
      value2=5
      result1=$(addem $value1 $value2) # 如上文所述,變量替換中$()執(zhí)行的命令是在fork的一個(gè)子進(jìn)程shell中進(jìn)行的,$()中的命令聲明的變量等不會(huì)對(duì)當(dāng)前shell有效。
      result2=$(multem $value1 $value2)
      result3=$(divem $value1 $value2)
      echo "The result of adding them is: $result1"
      echo "The result of multiplying them is: $result2"
      echo "The result of dividing them is: $result3"
      $
      $ ./test14
      The result of adding them is: 15
      The result of multiplying them is: 50
      The result of dividing them is: 2
      $

      ※,在命令行上使用函數(shù)

      1. 方法一:在命令行上創(chuàng)建函數(shù)。可以單行定義函數(shù)(此時(shí)每個(gè)命令后面必須添加分號(hào)以便shell知道命令的起止)。也可以采用多行方式定義函數(shù),在定義時(shí),bash shell會(huì)使用次提示符來(lái)提示輸入更多命令。用這種方法,你不用在每條命令的末尾放一個(gè)分號(hào),只要按下回車鍵就行。在函數(shù)的尾部使用花括號(hào),shell就會(huì)知道你已經(jīng)完成了函數(shù)的定義。警告:在命令行上創(chuàng)建函數(shù)時(shí)要特別小心。如果你給函數(shù)起了個(gè)跟內(nèi)建命令或另一個(gè)命令相同的名字,函數(shù)將會(huì)覆蓋原來(lái)的命令。

      2. 方法二:在 ~/.bashrc文件中定義函數(shù)。bash shell在每次啟動(dòng)時(shí)都會(huì)在主目錄下查找這個(gè)文件,不管是交互式shell還是從現(xiàn)有shell中啟動(dòng)的新shell。這是一種創(chuàng)建實(shí)用工具的簡(jiǎn)便方法,不管 PATH 環(huán)境變量設(shè)置成什么,都可以直接拿來(lái)使用。

      • 直接定義函數(shù)。
      • 讀取函數(shù)庫(kù)文件。在.bashrc中 可以用 source 命令(或者它的別名點(diǎn)操作符)將庫(kù)文件中的函數(shù)添加到你的.bashrc腳本中。

      這樣在新打開(kāi)的shell命令行中都可以直接使用 庫(kù)文件中的函數(shù)了。如果已經(jīng)打開(kāi)了shell,再在 .bashrc 中添加庫(kù)文件,那么需要在當(dāng)前shell中 使用source命令: ·source ~/.bashrc·。這樣就可以在當(dāng)前shell中直接使用庫(kù)文件函數(shù)了。

      ※,實(shí)例

      函數(shù)的應(yīng)用絕不僅限于創(chuàng)建自己的函數(shù)自?shī)首詷?lè)。在開(kāi)源世界中,共享代碼才是關(guān)鍵,而這一點(diǎn)同樣適用于腳本函數(shù)。你可以下載大量各式各樣的函數(shù),并將其用于自己的應(yīng)用程序中。本節(jié)介紹了如何下載、安裝、使用GNU shtool shell腳本函數(shù)庫(kù)。shtool庫(kù)提供了一些簡(jiǎn)單的shell腳本函數(shù),可以用來(lái)完成日常的shell功能,例如處理臨時(shí)文件和目錄或者格式化輸出顯示。

      下面是GNU shtool shell腳本函數(shù)庫(kù)的使用方法:

      1. 下載和安裝:shtool軟件包的下載地址是: ftp://ftp.gnu.org/gnu/shtool/shtool-2.0.8.tar.gz 。在Linux中可以使用 wget <url> 或 curl <url> --output <file> 來(lái)下載。下載完成解壓即可。

       

      •  wget是個(gè)專職的下載利器,簡(jiǎn)單,專一,極致;而curl可以下載,但是長(zhǎng)項(xiàng)不在于下載,而在于模擬提交web數(shù)據(jù),POST/GET請(qǐng)求,調(diào)試網(wǎng)頁(yè),等等。
      • 在下載上,也各有所長(zhǎng),wget可以遞歸,支持?jǐn)帱c(diǎn);而curl支持URL中加入變量,因此可以批量下載。

       

      2. 構(gòu)建庫(kù)。shtool文件必須針對(duì)特定的Linux環(huán)境進(jìn)行配置。配置工作必須使用標(biāo)準(zhǔn)的 configure 和make 命令,這兩個(gè)命令常用于C編程環(huán)境。要構(gòu)建庫(kù)文件,只要輸入:

      $ ./confifgure
      $ make

      configure 命令會(huì)檢查構(gòu)建shtool庫(kù)文件所必需的軟件。一旦發(fā)現(xiàn)了所需的工具,它會(huì)使用工具路徑修改配置文件。make 命令負(fù)責(zé)構(gòu)建shtool庫(kù)文件。最終的結(jié)果( shtool )是一個(gè)完整的庫(kù)軟件包。你也可以使用 make 命令測(cè)試這個(gè)庫(kù)文件,如下:

      $ make test
      Running test suite:
      echo...........ok
      mdate..........ok
      table..........ok
      prop...........ok
      move...........ok
      install........ok
      mkdir..........ok
      mkln...........ok
      mkshadow.......ok
      fixperm........ok
      rotate.........ok
      tarball........ok
      subst..........ok
      platform.......ok
      arx............ok
      slo............ok
      scpp...........ok
      version........ok
      path...........ok
      OK: passed: 19/19
      $

      測(cè)試模式會(huì)測(cè)試shtool庫(kù)中所有的函數(shù)。如果全部通過(guò)測(cè)試,就可以將庫(kù)安裝到Linux系統(tǒng)中的公用位置(即:使用make install命名安裝到公用位置),這樣所有的腳本就都能夠使用這個(gè)庫(kù)了。要完成安裝,需要使用 make 命令的install 選項(xiàng)。不過(guò)你得以root用戶的身份運(yùn)行該命令。(可以不用su切換root用戶,最近發(fā)現(xiàn) ·sudo -s <command>·很好用,sudo -s make install 。可以查看sudo -h)

      $ su
      Password:
      # make install
      ./shtool mkdir -f -p -m 755 /usr/local
      ./shtool mkdir -f -p -m 755 /usr/local/bin
      ./shtool mkdir -f -p -m 755 /usr/local/share/man/man1
      ./shtool mkdir -f -p -m 755 /usr/local/share/aclocal
      ./shtool mkdir -f -p -m 755 /usr/local/share/shtool
      ...
      ./shtool install -c -m 644 sh.version /usr/local/share/shtool/sh.version
      ./shtool install -c -m 644 sh.path /usr/local/share/shtool/sh.path
      #
      現(xiàn)在就能在自己的shell腳本中使用這些函數(shù)了。

      shtool函數(shù)的使用格式:shtool [options] [function [options] [args]]。 如

      • `shtool --help`
      • `shtool platform`
      • `shtool prop -p "waiting..."`//顯示一個(gè)進(jìn)度條







      結(jié)束標(biāo)記

      posted on 2021-08-25 00:21  everest33  閱讀(314)  評(píng)論(0)    收藏  舉報(bào)

      主站蜘蛛池模板: 成年午夜免费韩国做受视频| 国产不卡一区二区在线视频| 国产无码高清视频不卡| 五河县| 九九热精品视频在线免费| 日本成人午夜一区二区三区| 日韩蜜桃AV无码中文字幕不卡高清一区二区| 国产亚洲国产精品二区| 国产99在线 | 欧美| 国产成人高清亚洲一区91| 亚洲中文字幕久久精品品| bt天堂新版中文在线| 日韩有码国产精品一区| 福利在线视频一区二区| 熟女性饥渴一区二区三区| 毛片tv网站无套内射tv网站| 视频二区中文字幕在线| 亚洲av无码牛牛影视在线二区 | 欧美日韩中文字幕久久伊人| 干老熟女干老穴干老女人| 成全高清在线播放电视剧| 天天综合色一区二区三区| 欧美中文字幕无线码视频| 国产精品中文字幕二区| 国产视色精品亚洲一区二区 | 亚洲V天堂V手机在线| 四会市| 国产成人无码A区在线观看视频| 国产精品亚洲аv无码播放| 偷拍美女厕所尿尿嘘嘘小便| 国产精品多p对白交换绿帽| 屁屁影院ccyy备用地址| 久久这里只精品热免费99| 日区中文字幕一区二区| 自拍偷在线精品自拍偷免费| 中文字幕无码久久精品| 被拉到野外强要好爽| 国产亚洲一区二区三区av| 色综合久久蜜芽国产精品| 亚洲国产精品久久久久婷婷图片| 无码人妻一区二区三区在线视频|