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,文件比較。
| 比較(-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 |
|
例子 |
|
| 比較(==,!=, >等操作符兩邊需要空格!) | 描述 |
| 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 |
| 例子 |
|
| -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)困惑)
|
| file1 -nt file2 | 檢查file1 是否比 file2 新,注意這個(gè)命令不會(huì)判斷file1和file2是否存在,使用時(shí)需要首先判斷文件是否存在,否則會(huì)得到錯(cuò)誤的結(jié)果。 |
| file1 -ot file2 | 檢查file1 是否比 file2 舊,同上注意事項(xiàng)。 |
| 例子 |
|
※,符合條件測(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`
關(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 套接字
- REG:普通文件
- 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 | grep lsof.log·查找出進(jìn)程ID。
- 我們會(huì)遇到磁盤(pán)卸載報(bào)umount: /home: device is busy之類的提示,此時(shí)可以使用lsof命令找出占用磁盤(pán)的進(jìn)程·lsof /home·,然后直接kill掉此進(jìn)程,磁盤(pán)成功卸載
- ·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)程。
- 進(jìn)程名稱p-name指的shell命令或者是運(yùn)行的二進(jìn)制文件名稱,文件的路徑名稱不屬于進(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)
浙公網(wǎng)安備 33010602011771號(hào)