一、什么是shell?
shell是一個(gè)用 C 語言編寫的程序,它是用戶使用 linux 的橋梁。Shell 既是一種命令語言,又是一種程序設(shè)計(jì)語言。
Shell 是指一種應(yīng)用程序,這個(gè)應(yīng)用程序提供了一個(gè)界面,用戶通過這個(gè)界面訪問操作系統(tǒng)內(nèi)核的服務(wù)。
Shell屬于內(nèi)置的腳本,程序開發(fā)的效率非常高,依賴于功能強(qiáng)大的命令可以迅速地完成開發(fā)任務(wù)(批處理)語法簡(jiǎn)單,代碼寫起來比較輕松,簡(jiǎn)單易學(xué)。
二、shell的分類
cat /etc/shells
在linux中有很多類型的shell,不同的shell具備不同的功能,shell還決定了腳本中函數(shù)的語法,Linux中默認(rèn)的shell是 /bash/bash(重點(diǎn)),流行的shell有ash、bash、ksh、csh、zsh等,不同的shell都有自己的特點(diǎn)以及用途。
編寫規(guī)范:
#!/bin/bash [指定告知系統(tǒng)當(dāng)前這個(gè)腳本要使用的shell解釋器]
Shell相關(guān)指令
文件命名規(guī)范:
*.sh
.sh是linux下bash shell 的默認(rèn)后綴
Bash 常用快捷鍵
快捷鍵 |
作用 |
ctrl+A |
把光標(biāo)移動(dòng)到命令行開頭。如果我們輸入的命令過長(zhǎng),想要把光標(biāo)移動(dòng)到命令行開頭時(shí)使用。 |
ctrl+E |
把光標(biāo)移動(dòng)到命令行結(jié)尾。 |
ctrl+C |
強(qiáng)制終止當(dāng)前的命令。 |
ctrl+L |
清屏,相當(dāng)于clear命令。 |
ctrl+U |
刪除或剪切光標(biāo)之前的命令。我輸入了一行很長(zhǎng)的命令,不用使用退格鍵一個(gè)一個(gè)字符的刪除,使用這個(gè)快捷鍵會(huì)更加方便 |
ctrl+K |
刪除或剪切光標(biāo)之后的內(nèi)容。 |
ctrl+Y |
粘貼ctrl+U或ctul+K剪切的內(nèi)容。 |
ctrl+R |
在歷史命令中搜索,按下ctrl+R之后,就會(huì)出現(xiàn)搜索界面,只要輸入搜索內(nèi)容,就會(huì)從歷史命令中搜索。 |
ctrl+D |
退出當(dāng)前終端。 |
ctrl+Z |
暫停,并放入后臺(tái)。這個(gè)快捷鍵牽扯工作管理的內(nèi)容,我們?cè)谙到y(tǒng)管理章節(jié)詳細(xì)介紹。 |
ctrl+S |
暫停屏幕輸出。 |
ctrl+Q |
恢復(fù)屏幕輸出。 |
輸入輸出重定向
linux 的標(biāo)準(zhǔn)輸入與輸出
設(shè)備 |
設(shè)備名 |
文件描述符 |
類型 |
鍵盤 |
/dev/stdin |
0 |
標(biāo)準(zhǔn)輸入 |
顯示器 |
/dev/stdout |
1 |
標(biāo)準(zhǔn)輸出 |
顯示器 |
/dev/stderr |
2 |
標(biāo)準(zhǔn)錯(cuò)誤輸出 |
輸入重定向
輸入重定向:是指不使用系統(tǒng)提供的標(biāo)準(zhǔn)輸入端口,而進(jìn)行重新的指定。換言之,輸入重定向就是不使用標(biāo)準(zhǔn)輸入端口輸入文件,而是使用指定的文件作為標(biāo)準(zhǔn)輸入設(shè)備。(重定向簡(jiǎn)單理解就是使用 “<”符來修改標(biāo)準(zhǔn)輸入設(shè)備)
類型 |
符號(hào)(語法) |
功能 |
標(biāo)準(zhǔn)輸入 |
命令<文件1 |
命令把文件1的內(nèi)容作為標(biāo)準(zhǔn)輸入設(shè)備 |
標(biāo)識(shí)符限定輸入 |
命令<<標(biāo)識(shí)符 |
命令把標(biāo)準(zhǔn)輸入中讀入內(nèi)容,直到遇到“標(biāo)識(shí)符”分解符為止 |
輸入輸出重定向(同時(shí)使用) |
命令< 文件1 >文件2 |
命令把文件1的內(nèi)容作為標(biāo)準(zhǔn)輸入,把文件2作為標(biāo)準(zhǔn)輸出。 |
輸出重定向
輸出重定向:(通俗的講,重定向輸出就是把要輸出的文件信息寫入到一個(gè)文件中去,而不是將要輸出的文件信息輸出到控制臺(tái)(顯示屏),在linux中,默認(rèn)的標(biāo)準(zhǔn)輸出設(shè)備是控制臺(tái)(或稱為顯示器),用戶輸出的信息默認(rèn)情況下都會(huì)顯示到控制臺(tái).
&表示全部文件,文件不管對(duì)錯(cuò),1表示標(biāo)準(zhǔn)輸出文件,2表示標(biāo)準(zhǔn)錯(cuò)誤輸出。
類型 |
符號(hào)(語法) |
功能 |
標(biāo)住輸出重定向 |
命令 > 文件 |
以覆蓋方式,把命令的正確輸出內(nèi)容輸出到指定的文件或設(shè)備當(dāng)中 |
標(biāo)住輸出重定向 |
命令 >> 文件 |
以追加方式,把命令的正確輸出內(nèi)容輸出到指定的文件或設(shè)備當(dāng)中 |
標(biāo)準(zhǔn)錯(cuò)誤輸出重定向 |
錯(cuò)誤命令 2> 文件 |
以覆蓋方式,把命令的錯(cuò)誤輸出輸出到指定的文件或設(shè)備當(dāng)中 |
標(biāo)準(zhǔn)錯(cuò)誤輸出重定向 |
錯(cuò)誤命令 2>> 文件 |
以追加方式,把命令的錯(cuò)誤輸出輸出到指定的文件或設(shè)備當(dāng)中 |
正確輸出和錯(cuò)誤輸出同時(shí)保存 |
命令 > 文件 2>&1 |
以覆蓋的方式,把正確輸出和錯(cuò)誤輸出都保存到同一個(gè)文件當(dāng)中。 |
正確輸出和錯(cuò)誤輸出同時(shí)保存 |
命令 >> 文件 2>&1 |
以追加的方式,把正確輸出和錯(cuò)誤輸出都保存到同一個(gè)文件當(dāng)中。 |
正確輸出和錯(cuò)誤輸出同時(shí)保存 |
命令 &> 文件 |
以覆蓋的方式,把正確輸出和錯(cuò)誤輸出都保存到同一個(gè)文件當(dāng)中。 |
正確輸出和錯(cuò)誤輸出同時(shí)保存 |
命令 &>> 文件 |
以追加的方式,把正確輸出和錯(cuò)誤輸出都保存到同一個(gè)文件當(dāng)中。 |
正確輸出和錯(cuò)誤輸出同時(shí)保存 |
命令 >> 文件1 2>>文件2 |
把正確的輸出追加到文件1中,把錯(cuò)誤的輸出追加到文件2中。 |
/dev/null 文件
如果希望執(zhí)行某個(gè)命令,但又不希望在屏幕上顯示輸出結(jié)果,那么可以將輸出重定向到 /dev/null中.
[root@localhost ~]$ command > dev/null
多命令順序執(zhí)行
多命令執(zhí)行符 |
作用 |
格式 |
; |
命令1 ;命令2 |
多個(gè)命令順序執(zhí)行,命令之間沒有任何邏輯聯(lián)系 |
&& |
命令1 && 命令2 |
當(dāng)命令1正確執(zhí)行(? = 0 ) , 則命令2才會(huì)執(zhí)行;當(dāng)命令 1執(zhí)行不正確(?=0),則命令2不會(huì)執(zhí)行 |
|| |
命令1 || 命令2 |
當(dāng)命令1執(zhí)行不正確(?≠0),則命令2才會(huì)執(zhí)行;當(dāng)命令1正確執(zhí)行(?≠0),則命令2不會(huì)執(zhí)行. |
shell腳本的執(zhí)行
[root@localhost ~]$ vim test.sh
#!/bin/bash
echo “hello world”
兩種方式執(zhí)行shell腳本
第一種:給文件增加執(zhí)行權(quán)限
[root@localhost ~]$ chmod u+x test.sh
[root@localhost ~]$ ./test.sh #絕對(duì)路徑或相對(duì)路徑執(zhí)行
第二種(了解):通過Bash調(diào)用執(zhí)行腳本
[root@localhost ~]$ bash test.sh
三、shell變量
什么是變量?
在一個(gè)腳本周期內(nèi),其值可以發(fā)生改變的量就是變量。
1. 變量的命名規(guī)則:
在定義變量時(shí),有一些規(guī)則需要遵守:
l 命名只能使用英文字母,數(shù)字和下劃線,首個(gè)字符不能以數(shù)字開頭。
l 等號(hào)左右兩側(cè)不能有空格,可以使用下劃線“_”,變量的值如果有空格,需要使用單引號(hào)或雙引號(hào)包括。如:“test=“hello world!””。其中雙引號(hào)括起來的內(nèi)容“$”,“(”和反引號(hào)都擁有特殊含義,而單引號(hào)括起來的內(nèi)容都是普通字符。
l 不能使用標(biāo)點(diǎn)符號(hào),不能使用bash里的關(guān)鍵字(可用help命令查看保留關(guān)鍵字)。
l 環(huán)境變量建議大寫,便于區(qū)分
l 如果需要增加變量的值,那么可以進(jìn)行變量值的疊加。不過變量需要用雙引號(hào)包含"$變量名"或用${變量名}包含變量名。
<!---->
[root@localhost ~]$ test=123
[root@localhost ~]$ test="$test"456
[root@localhost ~]$ echo $test
123456
#疊加變量test,變量值變成了123456
[root@localhost ~]$ test=${test}789
[root@localhost ~]$ echo $test
123456789
#再疊加變量test,變量值編程了123456789
關(guān)于單雙引號(hào)的問題:
雙引號(hào)能夠識(shí)別變量,雙引號(hào)能夠?qū)崿F(xiàn)轉(zhuǎn)義(類似于“*”)
單引號(hào)是不能識(shí)別變量,只會(huì)原樣輸出,單引號(hào)是不能轉(zhuǎn)義的
shell中特殊符號(hào)
符號(hào) |
作用 |
'' |
單引號(hào)。在單引號(hào)中所有的特殊符號(hào),如“$”和”(反引號(hào))都沒有特殊含義。單引號(hào)括起來的都是普通字符,會(huì)原樣輸出 |
"" |
雙引號(hào)。在雙引號(hào)中特殊符號(hào)都沒有特殊含義.“$”表示“調(diào)用變量的值”,“`”(esc鍵下面)表示“引用命令”,“”表示“轉(zhuǎn)義符”。 |
`` |
反引號(hào)。反引號(hào)括起來的內(nèi)容是系統(tǒng)命令,在Bash中會(huì)先執(zhí)行它。和()作用一 樣, 不過推薦使用(),因?yàn)榉匆?hào)非常容易看錯(cuò)。 |
$() |
和反引號(hào)作用一樣,用來引用系統(tǒng)命令。(推薦使用) |
() |
用于一串命令執(zhí)行時(shí),()中的命令會(huì)在子Shell中運(yùn)行 |
{} |
用于一串命令執(zhí)行時(shí),{ }中的命令會(huì)在當(dāng)前Shell中執(zhí)行。也可以用于變量變形與替換。 |
[ ] |
用于變量的測(cè)試。 |
# |
在Shell腳本中,#開頭的行代表注釋。 |
$ |
用于調(diào)用變量的值,如需要調(diào)用變量name的值時(shí),需要用$name的方式得到變量的值。 |
|
轉(zhuǎn)義符,跟在之后的特殊符號(hào)將失去特殊含義,變?yōu)槠胀ㄗ址H?將輸出“$”符號(hào),而不當(dāng)做是變量引用。 |
單引號(hào)和雙引號(hào)
[root@localhost ~]$ name=sc
#定義變量name 的值是sc
[root@localhost ~]$ echo '$name'
$name
#如果輸出時(shí)使用單引號(hào),則$name被認(rèn)為是字符串
[root@localhost ~]$ echo "$name"
sc
#如果輸出時(shí)使用雙引號(hào),則會(huì)輸出變量name的值 sc
[root@localhost ~]$ echo `date`
2018年10月21日星期一18:16:33 CST
#反引號(hào)括起來的命令會(huì)正常執(zhí)行
[root@localhost ~]$ echo '`date`'
`date`
#但是如果反引號(hào)命令被單引號(hào)括起來,那么這個(gè)命令不會(huì)執(zhí)行,―date`會(huì)被當(dāng)成普通字符輸出
[root@localhost ~]$ echo "`date'"
2018年10月21日星期一18:14:21 CST
#如果是雙引號(hào)括起來,那么這個(gè)命令又會(huì)正常執(zhí)行
反引號(hào)
[root@localhost ~]$ echo ls
ls
#如果命令不用反引號(hào)包含,命令不會(huì)執(zhí)行,而是直接輸出
[root@localhost ~]$ echo `ls`
anaconda-ks.cfginstall.loginstall.log.syslog sh test testfile
#只有用反引號(hào)包括命令,這個(gè)命令才會(huì)執(zhí)行
[root@localhost ~]$ echo $(date)
2018年10月21日星期一18:25:09 CST
#使用$(命令)的方式也是可以的
2. 變量的分類
l 用戶自定義變量: 這種變量是最常見的變量,由用戶自由定義變量名和變量的值。
l 環(huán)境變量: 這種變量中主要保存的是和系統(tǒng)操作環(huán)境相關(guān)的數(shù)據(jù),比如當(dāng)前登錄用戶,用戶的家目錄,命令的提示符等。不是太好理解吧,那么大家還記得在windows中,同一臺(tái)電腦可以有多個(gè)用戶登錄,而且每個(gè)用戶都可以定義自己的桌面樣式和分辨率,這些其實(shí)就是Windows的操作環(huán)境,可以當(dāng)做是Windows的環(huán)境變量來理解。環(huán)境變量的變量名可以自由定義,但是一般對(duì)系統(tǒng)起作用的環(huán)境變量的變量名是系統(tǒng)預(yù)先設(shè)定好的。
l 位置參數(shù)變量: 這種變量主要是用來向腳本當(dāng)中傳遞參數(shù)或數(shù)據(jù)的,變量名不能自定義,變量作用是固定的。
l 預(yù)定義變量: 是Bash中已經(jīng)定義好的變量,變量名不能自定義,變量作用也是固定的。
變量分類 |
名稱 |
作用 |
內(nèi)容 |
用戶自定義變量 |
自定義 |
自定義 |
自定義 |
用戶自定義環(huán)境變量 |
自定義 |
自定義 |
自定義 |
系統(tǒng)自帶環(huán)境變量(/etc/profile) |
確定 |
確定 |
自定義 |
位置參數(shù)變量 |
確定 |
自定義 |
自定義 |
預(yù)定義變量 |
確定 |
自定義 |
自定義 |
2.1 用戶自定義變量
2.1.1 變量定義
[root@localhost ~]$ 2name="shen chao"
-bash: 2name=shen chao: command not found
#變量名不能用數(shù)字開頭
[root@localhost ~]$ name = "shenchao"
-bash: name: command not found
#等號(hào)左右兩側(cè)不能有空格
[root@localhost ~]$ name=shen chao
-bash: chao: command not found
#變量的值如果有空格,必須用引號(hào)包含
2.1.2 變量調(diào)用
[root@localhost ~]$ name="shen chao"
#定義變量name
[root@localhost ~]$ echo $name #調(diào)用變量使用 $變量名
shen chao
#輸出變量name的值
2.1.3 變量查看
[root@localhost ~]$ set [選項(xiàng)]
選項(xiàng):
-u:如果設(shè)定此選項(xiàng),調(diào)用未聲明變量時(shí)會(huì)報(bào)錯(cuò)(默認(rèn)無任何提示)
-x:如果設(shè)定此選項(xiàng),在命令執(zhí)行之前,會(huì)把命令先輸出一次
+<參數(shù)> :取消某個(gè)set曾啟動(dòng)的參數(shù)。
[root@localhost ~]$ set
BASH=/bin/bash
…省略部分輸出…
name='shen chao'
#直接使用set 命令,會(huì)查詢系統(tǒng)中所有的變量,包含用戶自定義變量和環(huán)境變量
[root@localhost ~]$ set -u
[root@localhost ~]$ echo $file
-bash: file: unbound variable
#當(dāng)設(shè)置了-u選項(xiàng)后,如果調(diào)用沒有設(shè)定的變量會(huì)有報(bào)錯(cuò)。默認(rèn)是沒有任何輸出的。
[root@localhost ~]$ set -x
[root@localhost ~]$ ls
+ls --color=auto
anaconda-ks.cfginstall.loginstall.log.syslog sh tdir testtestfile
#如果設(shè)定了-x選項(xiàng),會(huì)在每個(gè)命令執(zhí)行之前,先把命令輸出一次
[root@localhost ~]$ set +x
#取消啟動(dòng)的x參數(shù)
2.1.4 變量刪除
[root@localhost ~]$ unset 變量名
2.2 環(huán)境變量
2.2.1 環(huán)境變量設(shè)置
[root@localhost ~]$ export age="18"
#使用export聲明的變量即是環(huán)境變量
2.2.2 環(huán)境變量查詢和刪除
env命令和set命令的區(qū)別:
set命令可以查看所有變量,而env命令只能查看環(huán)境變量。
[root@localhost ~]$ unset gender #刪除環(huán)境變量gender
[root@localhost ~]$ env | grep gender
2.2.3 系統(tǒng)默認(rèn)環(huán)境變量
[root@localhost ~]$ env
HOSTNAME=localhost.localdomain #主機(jī)名
SHELL=/bin/bash #當(dāng)前的shell
TERM=linux #終端環(huán)境
HISTSIZE=1000 #歷史命令條數(shù)
SSH_CLIENT=192.168.4.15 22 #當(dāng)前操作環(huán)境是用ssh連接的,這里記錄客戶端ip
SSH_TTY=/dev/pts/1 #ssh連接的終端時(shí)pts/1
USER=root #當(dāng)前登錄的用戶
..........更多參數(shù)可以使用set和env命令查看.............
2.3 位置參數(shù)變量
位置參數(shù)變量 |
作用 |
$n |
n為數(shù)字,$0表示當(dāng)前 Shell腳本程序的名稱,$1-9代表 第一到第九個(gè)參數(shù) , 十以上的參數(shù)需要用大括號(hào)包含,如9代表第一到第九個(gè)參數(shù),十以上的參數(shù)需要用大括號(hào)包含,如9代表第一到第九個(gè)參數(shù),十以上的參數(shù)需要用大括號(hào)包含,如${10} |
$* |
這個(gè)變量代表命令行中所有的參數(shù),$把所有的參數(shù)看成一個(gè)整體 |
$@ |
這個(gè)變量也代表命令行中所有的參數(shù),不過$@把每個(gè)參數(shù)區(qū)分對(duì)待 |
$# |
這個(gè)變量代表命令行中所有參數(shù)的個(gè)數(shù) |
$1 是你給你寫的shell腳本傳的第一個(gè)參數(shù),$2 是你給你寫的shell腳本傳的第二個(gè)參數(shù)…
[root@localhost sh]$ vim test.sh
#!/bin/sh
echo "shell腳本本身的名字: $0"
echo "傳給shell的第一個(gè)參數(shù): $1"
echo "傳給shell的第二個(gè)參數(shù): $2"
保存退出后,你在Test.sh所在的目錄下輸入 bash Test.sh 1 2
結(jié)果輸出:
shell腳本本身的名字: Test.sh
傳給shell的第一個(gè)參數(shù): 1
傳給shell的第二個(gè)參數(shù): 2
$*會(huì)把接收的所有參數(shù)當(dāng)成一個(gè)整體對(duì)待,而$@則會(huì)區(qū)分對(duì)待接收到的所有參數(shù)。舉個(gè)例子:
[root@localhost sh]$ vi parameter2.sh
#!/bin/bash
for i in"$*"
#定義for循環(huán),in后面有幾個(gè)值,for會(huì)循環(huán)多少次,注意“$*”要用雙引號(hào)括起來
#每次循環(huán)會(huì)把in后面的值賦予變量i
#Shell把$*中的所有參數(shù)看成是一個(gè)整體,所以這個(gè)for循環(huán)只會(huì)循環(huán)一次
do
echo "The parameters is: $i"
#打印變量$i的值
done
x=1
#定義變量x的值為1
for y in"$@"
#同樣in后面的有幾個(gè)值,for循環(huán)幾次,每次都把值賦予變量y
#可是Shel1中把“$@”中的每個(gè)參數(shù)都看成是獨(dú)立的,所以“$@”中有幾個(gè)參數(shù),就會(huì)循環(huán)幾次
do
echo "The parameter$x is: $y"
#輸出變量y的值
x=$(( $x +1 ))
#然變量x每次循環(huán)都加1,為了輸出時(shí)看的更清楚
done
2.4 預(yù)定義變量
預(yù)定義變量 |
作用 |
$? |
最后一次執(zhí)行的命令的返回狀態(tài)。如果這個(gè)變量的值為0,證明上一個(gè)命令正確執(zhí)行;如果這個(gè)變量的值為非О(具體是哪個(gè)數(shù),由命令自己來決定),則證明上一個(gè)命令執(zhí)行不正確了。 |
$$ |
當(dāng)前進(jìn)程的進(jìn)程號(hào)(PID) |
$! |
后臺(tái)運(yùn)行的最后一個(gè)進(jìn)程的進(jìn)程號(hào)(PID) |
先來看看”$?”這個(gè)變量,舉個(gè)例子說明
[root@localhost sh]$ ls
count.sh hello.sh parameter2.sh parameter.sh
#ls命令正確執(zhí)行
[root@localhost sh]$ echo $?
#預(yù)定義變量“$?”的值是0,證明上一個(gè)命令執(zhí)行正確
[root@localhost sh]$ ls install.log
ls:無法訪問install.log:沒有那個(gè)文件或目錄
#當(dāng)前目錄中沒有install.log文件,所以ls命令報(bào)錯(cuò)了
[root@localhost sh]$ echo $?
2
#變量“$?”返回一個(gè)非0的值,證明上一個(gè)命令沒有正確執(zhí)行
#至于錯(cuò)誤的返回值到底是多少,是在編寫ls命令時(shí)定義好的,如果碰到文件不存在就返回?cái)?shù)值2
再來說明下”$$”和”$!”這兩個(gè)預(yù)定義變量
[root@localhost sh]$ vi variable.sh
#!/bin/bash
echo "The current process is $$"
#輸出當(dāng)前進(jìn)程的PID.
#這個(gè)PID就是variable.sh這個(gè)腳本執(zhí)行時(shí),生成的進(jìn)程的PID
find /root -name hello.sh &
#使用find命令在root目錄下查找hello.sh文件
#符號(hào)&的意思是把命令放入后臺(tái)執(zhí)行,工作管理我們?cè)谙到y(tǒng)管理章節(jié)會(huì)詳細(xì)介紹
echo "The last one Daemon process is $!"
#輸出這個(gè)后臺(tái)執(zhí)行命令的進(jìn)程的PID,也就是輸出find命令的PID號(hào)
3. 只讀變量
[root@localhost sh]$ vi readonly.sh
#!/bin/bash
a=10
#語法:readonly 變量名
readonly a
a=20 #會(huì)報(bào)錯(cuò)readonly variable
echo $a
4. 接受鍵盤輸入
[root@localhost ~]$ read [選項(xiàng)][變量名]
選項(xiàng):
-a 后跟一個(gè)變量,該變量會(huì)被認(rèn)為是個(gè)數(shù)組,然后給其賦值,默認(rèn)是以空格為分割符。
-p: “提示信息”:在等待read輸入時(shí),輸出提示信息
-t: 秒數(shù):read命令會(huì)一直等待用戶輸入,使用此選項(xiàng)可以指定等待時(shí)間
-n: 數(shù)字:read命令只接受指定的字符數(shù),就會(huì)執(zhí)行
-s: 隱藏輸入的數(shù)據(jù),適用于機(jī)密信息的輸入
-d: 后面跟一個(gè)標(biāo)志符,其實(shí)只有其后的第一個(gè)字符有用,作為結(jié)束的標(biāo)志。
-e: 在輸入的時(shí)候可以使用命令補(bǔ)全功能。
變量名:
變量名可以自定義,如果不指定變量名,會(huì)把輸入保存入默認(rèn)變量REPLY.
如果只提供了一個(gè)變量名,則整個(gè)輸入行賦予該變量.
如果提供了一個(gè)以上的變量名,則輸入行分為若干字,一個(gè)接一個(gè)地賦予各個(gè)變量,而命令行上的最后一個(gè)變量取得剩余的所有字
寫個(gè)例子來解釋下read命令:
[root@localhost sh]$ vi read.sh
#!/bin/bash
read -t 30 -p "Please input your name: " name
#提示“請(qǐng)輸入姓名”并等待30 秒,把用戶的輸入保存入變量name 中
echo "Name is $name"
#看看變量“$name”中是否保存了你的輸入
read -s -t 30 -p "Please enter your age: " age
#提示“請(qǐng)輸入年齡”并等待30秒,把用戶的輸入保存入變量age中
#年齡是隱私,所以我們用“-s”選項(xiàng)隱藏輸入
echo -e "n"
#調(diào)整輸出格式,如果不輸出換行,一會(huì)的年齡輸出不會(huì)換行
echo "Age is $age"
read -n 1 -t 30 -p "Please select your gender[M/F]:" gender
#提示“請(qǐng)選擇性別”并等待30秒,把用戶的輸入保存入變量gender
#使用“-n1”選項(xiàng)只接收一個(gè)輸入字符就會(huì)執(zhí)行(都不用輸入回車)
echo -e "n"
echo "Sex is $gender"
四、shell 運(yùn)算符
在shell中,運(yùn)算符和其他編程腳本語言一樣,常見的有算數(shù)運(yùn)算符、關(guān)系運(yùn)算符、邏輯運(yùn)算符、字符串運(yùn)算符、文件測(cè)試運(yùn)算符等
1. 算數(shù)運(yùn)算符
原生bash不支持簡(jiǎn)單的數(shù)學(xué)運(yùn)算,但是可以通過其他命令來實(shí)現(xiàn),例如 awk 和 expr,expr 最常用。
expr 是一款表達(dá)式計(jì)算工具,使用它能完成表達(dá)式的求值操作。
例如,兩個(gè)數(shù)相加(注意使用的是反引號(hào) ` 而不是單引號(hào) '):
[root@localhost ~]$ vi computer.sh
#!/bin/bash
val=`expr 2 + 2`
echo "兩數(shù)之和為 : $val"
#注意
#表達(dá)式和運(yùn)算符之間要有空格,例如 2+2 是不對(duì)的,必須寫成 2 + 2,這與我們熟悉的大多數(shù)編程語言不一樣。
#完整的表達(dá)式要被 ` ` 包含,注意這個(gè)字符不是常用的單引號(hào),在 Esc 鍵下邊。
下表列出了常用的算術(shù)運(yùn)算符,假定變量 a 為 10,變量 b 為 20
運(yùn)算符 |
說明 |
舉例 |
+ |
加法 |
expr $a + $b 結(jié)果為 30。 |
- |
減法 |
expr $a - $b 結(jié)果為 -10。 |
* |
乘法 |
expr $a * $b 結(jié)果為 200。 |
/ |
除法 |
expr $b / $a 結(jié)果為 2。 |
% |
取余 |
expr $b % $a 結(jié)果為 0。 |
= |
賦值 |
a=$b 將把變量 b 的值賦給 a。 |
== |
相等。用于比較兩個(gè)數(shù)字,相同則返回 true(真)。 |
[ $a == $b ] 返回 false(假)。 |
!= |
不相等。用于比較兩個(gè)數(shù)字,不相同則返回 true。 |
[ $a != $b ] 返回 true。 |
注意:條件表達(dá)式要放在方括號(hào)之間,并且要有空格,必須寫成 [ $a == $b ]。
[root@localhost ~]$ vi computers.sh
#!/bin/bash
a=10
b=20
echo ' '
echo 'a+b= ' `expr $a + $b`
echo 'a-b= ' `expr $a - $b`
echo 'a*b= ' `expr $a * $b`
echo 'a/b= ' `expr $a / $b`
echo 'a%b= ' `expr $a % $b`
#判斷是否相等
if [ $a == $b ]
then
echo 'a等于b'
else
echo 'a不等于b'
fi
2. 關(guān)系運(yùn)算符
關(guān)系運(yùn)算符只支持?jǐn)?shù)字,不支持字符串,除非字符串的值是數(shù)字。
下表列出了常用的關(guān)系運(yùn)算符,假定變量 a 為 10,變量 b 為 20:
運(yùn)算符 |
單詞 |
說明 |
舉例 |
-eq |
equal |
檢測(cè)兩個(gè)數(shù)是否相等,相等返回 true。 |
[ $a -eq $b ] 返回 false。 |
-ne |
not equal |
檢測(cè)兩個(gè)數(shù)是否相等,不相等返回 true。 |
[ $a -ne $b ] 返回 true。 |
-gt |
great than |
檢測(cè)左邊的數(shù)是否大于右邊的,如果是,則返回 true。 |
[ $a -gt $b ] 返回 false。 |
-lt |
less than |
檢測(cè)左邊的數(shù)是否小于右邊的,如果是,則返回 true。 |
[ $a -lt $b ] 返回 true。 |
-ge |
great than or equal |
檢測(cè)左邊的數(shù)是否大于等于右邊的,如果是,則返回 true。 |
[ $a -ge $b ] 返回 false。 |
-le |
less than or equal |
檢測(cè)左邊的數(shù)是否小于等于右邊的,如果是,則返回 true。 |
[ $a -le $b ] 返回 true。 |
[root@localhost ~]$ [ 10 -gt 10 ]
[root@localhost ~]$ echo $?
1
[root@localhost ~]$ [ 10 -eq 10 ]
[root@localhost ~]$ echo $?
0
案例:判斷當(dāng)前輸入的用戶是否存在。如果存在則提示“用戶存在”否則提示“用戶不存在”。
如果要在shell腳本使用linux命令,可以使用$()包裹命令
例如:disk_size=$(df -h | awk ‘NR==2 {print $5}’)
[root@localhost ~]$ vim demo.sh
#!/bin/bash
#接受用戶的輸入
read -p '請(qǐng)輸入需要查詢的用戶名:' username
#獲取指定用戶名在passwd文件中出現(xiàn)的次數(shù)
count=$(cat /etc/passwd | grep $username | wc -l)
#count=`cat /etc/passwd | grep $username | wc -l`
#判斷出現(xiàn)的次數(shù),如果次數(shù)=0則用戶不存在,反之存在
if [ $count == 0 ]
then
echo '用戶不存在'
else
echo '用戶存在'
fi
3. 邏輯運(yùn)算符
下表列出了常用的布爾運(yùn)算符,假定變量 a 為 10,變量 b 為 20:
運(yùn)算符 |
說明 |
舉例 |
! |
非運(yùn)算,表達(dá)式為 true 則返回 false,否則返回 true。 |
[ ! false ] 返回 true。 |
-o |
或(或者)運(yùn)算,有一個(gè)表達(dá)式為 true 則返回 true。 |
[ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
-a |
與(并且)運(yùn)算,兩個(gè)表達(dá)式都為 true 才返回 true。 |
[ $a -lt 20 -a $b -gt 100 ] 返回 false。 |
或運(yùn)算:一個(gè)為真即為真,全部為假才是假
與運(yùn)算:一個(gè)為假即為假,全部為真才是真
4. 字符串運(yùn)算符
下表列出了常用的字符串運(yùn)算符,假定變量 a 為 “abc”,變量 b 為 “efg”:
運(yùn)算符 |
說明 |
舉例 |
= |
檢測(cè)兩個(gè)字符串是否相等,相等返回 true。 |
[ $a = $b ] 返回 false。 |
!= |
檢測(cè)兩個(gè)字符串是否相等,不相等返回 true。 |
[ $a != $b ] 返回 true。 |
-z |
檢測(cè)字符串長(zhǎng)度是否為0,為0返回 true。 |
[ -z $a ] 返回 false。 |
-n |
檢測(cè)字符串長(zhǎng)度是否為0,不為0返回 true。 |
[ -n $a ] 返回 true。 |
str |
檢測(cè)字符串是否為空,不為空返回 true。 |
[ $a ] 返回 true。 |
5. 文件測(cè)試運(yùn)算符(重點(diǎn))
文件測(cè)試運(yùn)算符用于檢測(cè) Unix/Linux 文件的各種屬性。
操作符 |
說明 |
舉例 |
-b file |
檢測(cè)文件是否是塊設(shè)備文件,如果是,則返回 true。 |
[ -b $file ] 返回 false。 |
-c file |
檢測(cè)文件是否是字符設(shè)備文件,如果是,則返回 true。 |
[ -c $file ] 返回 false。 |
-d file |
檢測(cè)文件是否是目錄,如果是,則返回 true。 |
[ -d $file ] 返回 false。 |
-f file |
檢測(cè)文件是否是普通文件(既不是目錄,也不是設(shè)備文件),如果是,則返回 true。 |
[ -f $file ] 返回 true。 |
-g file |
檢測(cè)文件是否設(shè)置了 SGID 位,如果是,則返回 true。 |
[ -g $file ] 返回 false。 |
-k file |
檢測(cè)文件是否設(shè)置了粘著位(Sticky Bit),如果是,則返回 true。 |
[ -k $file ] 返回 false。 |
-p file |
檢測(cè)文件是否是有名管道,如果是,則返回 true。 |
[ -p $file ] 返回 false。 |
-u file |
檢測(cè)文件是否設(shè)置了 SUID 位,如果是,則返回 true。 |
[ -u $file ] 返回 false。 |
-r file |
檢測(cè)文件是否可讀,如果是,則返回 true。 |
[ -r $file ] 返回 true。 |
-w file |
檢測(cè)文件是否可寫,如果是,則返回 true。 |
[ -w $file ] 返回 true。 |
-x file |
檢測(cè)文件是否可執(zhí)行,如果是,則返回 true。 |
[ -x $file ] 返回 true。 |
-s file |
檢測(cè)文件是否為空(文件大小是否大于0),不為空返回 true。 |
[ -s $file ] 返回 true。 |
-e file |
檢測(cè)文件(包括目錄)是否存在,如果是,則返回 true。 |
[ -e $file ] 返回 true。 |
注意:權(quán)限幾個(gè)判斷,如果只有一個(gè)部分符合,則認(rèn)為是有權(quán)限的。
五、流程控制
1. if條件判斷
1.1 單分支if條件
語法:
if [ 條件判斷式 ]
then
程序
fi
案例:統(tǒng)計(jì)根分區(qū)使用率
[root@localhost ~]$ vi sh/if1.sh
#!/bin/bash
#統(tǒng)計(jì)根分區(qū)使用率
rate=$(df -h | grep "/dev/sda2" | awk '{print $5}’| cut -d "%"-f1)
#把根分區(qū)使用率作為變量值賦予變量rate
if [ $rate -ge 80 ]
#判斷rate的值如果大于等于80,則執(zhí)行then程序
then
echo "Warning!/dev/sda3 is fu11!!"
#打印警告信息。在實(shí)際工作中,也可以向管理員發(fā)送郵件。
fi
案例:創(chuàng)建目錄
[root@localhost ~]$ vi sh/add_dir.sh
#!/bin/bash
#創(chuàng)建目錄,判斷是否存在,存在就結(jié)束,反之創(chuàng)建
echo "當(dāng)前腳本名稱為$0"
DIR="/media/cdrom"
if [ ! -e $DIR ]
then
mkdir -p $DIR
fi
echo "$DIR 創(chuàng)建成功"
1.2 雙分支if條件語句
語法:
if [ 條件判斷式 ]
then
條件成立時(shí),執(zhí)行的程序
else
條件不成立時(shí),執(zhí)行的另一個(gè)程序
fi
案例1:備份MySQL數(shù)據(jù)庫(kù)
[root@localhost ~]$ vi sh/bakmysql.sh
#!/bin/bash
#備份mysql數(shù)據(jù)庫(kù)。
ntpdate asia.pool.ntp.org &>/dev/null
#同步系統(tǒng)時(shí)間
date=$(date +%y%m%d)
#把當(dāng)前系統(tǒng)時(shí)間按照“年月日”格式賦子變量date
size=$(du -sh /var/lib/mysql)
#統(tǒng)計(jì)mysql數(shù)據(jù)庫(kù)的大小,并把大小賦予size變量
if [ -d /tmp/dbbak ]
#判斷備份目錄是否存在,是否為目錄
then
#如果判斷為真,執(zhí)行以下腳本
echo "Date : $date!" > /tmp/dbbak/dbinfo.txt
#把當(dāng)前日期寫入臨時(shí)文件
echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt
#把數(shù)據(jù)庫(kù)大小寫入臨時(shí)文件
cd /tmp/dbbak
#進(jìn)入備份目錄
tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &> /dev/null
#打包壓縮數(shù)據(jù)庫(kù)與臨時(shí)文件,把所有輸出丟入垃圾箱(不想看到任何輸出)
rm -rf /tmp/dbbak/dbinfo.txt
#刪除臨時(shí)文件
else
mkdir /tmp/dbbak
#如果判斷為假,則建立備份目錄
echo "Date : $date!" > /tmp/dbbak/dbinfo.txt
echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt
#把日期和數(shù)據(jù)庫(kù)大小保存如臨時(shí)文件
cd /tmp/dbbak
tar -zcf mysql-lib-$date.tar. gz dbinfo.txt /var/lib/mysql &> /dev/null
#壓縮備份數(shù)據(jù)庫(kù)與臨時(shí)文件
rm -rf/tmp/dbbak/dbinfo.txt
#刪除臨時(shí)文件
fi
案例2:判斷Apache是否啟動(dòng),如果沒有啟動(dòng)則自動(dòng)啟動(dòng)
[root@localhost ~]$ vi sh/autostart.sh
#!/bin/bash
#判斷apache是否啟動(dòng),如果沒有啟動(dòng)則自動(dòng)啟動(dòng)
port=$(nmap -sT 192.168.4.210 | grep tcp | grep http | awk '{print $2}')
#使用nmap命令掃描服務(wù)器,并截取 apache服務(wù)的狀態(tài),賦予變量port
#只要狀態(tài)是open,就證明正常啟動(dòng)
if [ "$port" == "open"]
#如果變量port的值是“open”
then
echo "$(date) httpd is ok!” >> /tmp/autostart-acc.log
#則證明apache 正常啟動(dòng),在正常日志中寫入一句話即可
else
/etc/rc.d/init.d/httpd start &>/dev/null
#否則證明apache沒有啟動(dòng),自動(dòng)啟動(dòng)apache
echo "$(date) restart httpd !!" >> /tmp/autostart-err.log
#并在錯(cuò)誤日志中記錄自動(dòng)啟動(dòng)apche 的時(shí)間
fi
nmap端口掃描命令,格式如下:
[root@localhost ~]$ nmap -sT 域名或IP
選項(xiàng):
-s 掃描
-T 掃描所有開啟的TCP端口
#知道了nmap命令的用法,我們?cè)谀_本中使用的命令就是為了截取http的狀態(tài),只要狀態(tài)是“or.
#就證明apache啟動(dòng)正常,否則證明apache啟動(dòng)錯(cuò)誤。來看看腳本中命令的結(jié)果:
[root@localhost ~]$ nmap -sT 192.168.4.210 | grep tcp | grep http | awk ' fprint $2}’
#掃描指定計(jì)算機(jī),提取包含tcp 的行,在提取包含httpd 的行,截取第二列open
#把截取的值賦予變量port
1.3 多分支if條件語句
語法:
if [ 條件判斷式1 ]
then
當(dāng)條件判斷式1成立時(shí),執(zhí)行程序1
elif [ 條件判斷式2 ]
then
當(dāng)條件判斷式2成立時(shí),執(zhí)行程序2
…省略更多條件…
else
當(dāng)所有條件都不成立時(shí),最后執(zhí)行此程序
fi
案例:判斷用戶輸入的是什么文件
[root@localhost ~]$ vi sh/if-elif.sh
#!/bin/bash
#判斷用戶輸入的是什么文件
read -p "Please input a filename: " file
#接收鍵盤的輸入,并賦予變量file
if [ -z "$file” ]
#判斷file變量是否為空
then
echo "Error, please input a filename"
#如果為空,執(zhí)行程序1,也就是輸出報(bào)錯(cuò)信息
exit 1
#退出程序,并返回值為Ⅰ(把返回值賦予變量$P)
elif [ ! -e "$file” ]
#判斷file的值是否存在
then
echo "Your input is not a file!"
#如1果不存在,則執(zhí)行程序2
exit 2
#退出程序,把并定義返回值為2
elif [ -f "$file” ]
#判斷file的值是否為普通文件
then
echo "$file is a regulare file!”
#如果是普通文件,則執(zhí)行程序3
elif [ -d "$file” ]
#到斷file的值是否為目錄文件
then
echo "$file is a directory!"
#如果是目錄文件,網(wǎng)執(zhí)行程序4
else
echo "$file is an other file!”
#如果以上判斷都不是,則執(zhí)行程序5
fi
2. 多分支case條件語句
case語句和if…elif…else語句一樣都是多分支條件語句,不過和if多分支條件語句不同的是,case語句只能判斷一種條件關(guān)系,而if語句可以判斷多種條件關(guān)系。
語法:
case $變量名 in
"值1")
如果變量的值等于值1,則執(zhí)行程序1
;;
"值2")
如果變量的值等于值2,則執(zhí)行程序2
::
…省略其他分支…
*)
如果變量的值都不是以上的值,則執(zhí)行此程序
;;
esac
這個(gè)語句需要注意以下內(nèi)容:
l case語句,會(huì)取出變量中的值,然后與語句體中的值逐一比較。如果數(shù)值符合,則執(zhí)行對(duì)應(yīng)的程序,如果數(shù)值不符,則依次比較下一個(gè)值。如果所有的值都不符合,則執(zhí)行 “*)” (*代表所有其他值)中的程序。
l case語句以“case”開頭,以“esac”結(jié)尾。
每一個(gè)分支程序之后要通過“;;”雙分號(hào)結(jié)尾,代表該程序段結(jié)束(千萬不要忘記,每次寫case語句,都不要忘記雙分號(hào))。
案例:
[root@localhost ~]$ vi sh/if-case.sh
#!/bin/bash
read -p "請(qǐng)輸入一個(gè)字符,并按Enter確認(rèn):" KEY
case "$KEY" in
[a-z]|[A-Z])
echo "您輸入的是字母"
;;
[0-9])
echo "您輸入的是數(shù)字"
;;
*)
echo "您輸入的是其他字符"
;;
esac
3. for循環(huán)
for循環(huán)是固定循環(huán),也就是在循環(huán)時(shí)已經(jīng)知道需要進(jìn)行幾次的循環(huán),有時(shí)也把for循環(huán)稱為計(jì)數(shù)循環(huán)。for的語法有如下兩種:
語法一:
for 變量 in 值1 值2 值3 …(可以是一個(gè)文件等)
do
程序
done
這種語法中for循環(huán)的次數(shù),取決于in后面值的個(gè)數(shù)(空格分隔),有幾個(gè)值就循環(huán)幾次,并且每次循環(huán)都把值賦予變量。
也就是說,假設(shè)in后面有三個(gè)值,for會(huì)循環(huán)三次,第一次循環(huán)會(huì)把值1賦予變量,第二次循環(huán)會(huì)把值2賦予變量,以此類推。
語法二:
for (( 初始值;循環(huán)控制條件;變量變化 ))
do
程序
done
語法二中需要注意:
初始值:在循環(huán)開始時(shí),需要給某個(gè)變量賦予初始值,如i=1;
循環(huán)控制條件:用于指定變量循環(huán)的次數(shù),如i<=100,則只要i的值小于等于100,循環(huán)就會(huì)繼續(xù);
變量變化:每次循環(huán)之后,變量該如何變化,如i=i+1。代表每次循環(huán)之后,變量i的值都加1。
語法一舉例:打印時(shí)間
[root@localhost ~]$ vi sh/for.sh
#!/bin/bash
#打印時(shí)間
for time in morning noon afternoon evening
do
echo "This time is $time!"
done
語法一舉例:批量解壓縮腳本
[root@localhost ~]$ vi sh/auto-tar. sh
#!/bin/bash
#批量解壓縮腳本
cd/lamp
#進(jìn)入壓縮包目錄
ls *.tar.gz > ls.log
#把所有.tar.gz結(jié)尾的文件的文件覆蓋到ls.log 臨時(shí)文件中
for i in $(cat ls.log) `
#或者這樣寫for i in `cat ls.log`
#讀取ls.log文件的內(nèi)容,文件中有多少個(gè)值,就會(huì)循環(huán)多少次,每次循環(huán)把文件名賦予變量i
do
tar -zxf $i &>/dev/null
#加壓縮,并把所有輸出都丟棄
done
rm -rf /lamp/ls.log
#刪除臨時(shí)文件ls.log
語法二舉例:從1加到100
[root@localhost ~]$ vi sh/add. sh
#!/bin/bash
#從1加到100
s=0
for (( i=1;i<=100;i=i+1 ))
#定義循環(huán)100 次
do
s=$(( $s+$i ))
#每次循環(huán)給變量s賦值
done
echo "The sum of 1+2+...+100 is : $s"
#輸出1加到100的和
語法二舉例:批量添加指定數(shù)量的用戶
[root@localhost ~]$ vi useradd.sh
#!/bin/bash
#批量添加指定數(shù)量的用戶
read -p "Please input user name: " -t 30 name
#讓用戶輸入用戶名,把輸入保存入變量name
read -p "Please input the number of users: " -t 30 num
#讓用戶輸入添加用戶的數(shù)量,把輸入保存入變量num
read -p "Please input the password of users: " -t 30 pass
#讓用戶輸入初始密碼,把輸入保存如變量pass
if [ ! -z "$name" -a ! -z "$num"-a ! -z "$pass"]
#判斷三個(gè)變量不為空
then
y=$(echo $num | sed 's/[0-9]//g')
#定義變量的值為后續(xù)命令的結(jié)果
#后續(xù)命令作用是,把變量num 的值替換為空。如果能替換為空,證明num 的值為數(shù)字
#如果不能替換為空,證明num的值為非數(shù)字。我們使用這種方法判斷變量num 的值為數(shù)字
if [ -z "$y"]
#如果變量y的值為空,證明num變量是數(shù)字
then
for (( i=1 ; i<=$num; i=i+1 ))
#循環(huán)num變量指定的次數(shù)
do
/usr/sbin/useradd $name$i &>/dev/null
#添加用戶,用戶名為變量name 的值加變量i的數(shù)字
echo $pass | /usr/bin/passwd --stdin $name$i &>/dev/null
#給用戶設(shè)定初始密碼為變量pass 的值
done
fi
fi
語法二舉例:批量刪除用戶
[root@localhost ~]$ vi sh/userdel.sh
#!/bin/bash
#批量刪除用戶
user=$(cat /etc/passwd | grep " /bin/bash"|grep -v "root"Icut -d ":" -f 1)
#讀取用戶信息文件,提取可以登錄用戶,取消root用戶,截取第一列用戶名
for i in $user
#循環(huán),有多少個(gè)普通用戶,循環(huán)多少次
do
userdel -r $i
#每次循環(huán),刪除指定普通用戶
done
4. while循環(huán)
語法:
while [ 條件判斷式 ]
do
程序
done
案例:1加到100
[root@localhost ~]$ vi sh/addnum.sh
#!/bin/bash
#從1加到100
i=1
s=0
#給變量i和變量s賦值
while [ $i -le 100 ]
#如果變量i的值小于等于100,則執(zhí)行循環(huán)
do
s=$(( $s+$i ))
i=$(( $i+1 ))
done
echo "The sum is: $s"
案例:輸入的數(shù)值進(jìn)行比較判斷
[root@localhost ~]$ vi sh/addnum.sh
#!/bin/bash
PRICE=$(expr $RANDOM % 1000)
TIMES=0
echo "商品的價(jià)格為0-999之間,猜猜看是多少?"
while true
do
read -p "請(qǐng)輸入您猜的價(jià)格:" INT
let TIMES++
if [ $INT -eq $PRICE ] ; then
echo "恭喜您猜對(duì)了,實(shí)際價(jià)格是 $PRICE"
echo "您總共猜了 $TIMES 次"
exit 0
elif [ $INT -gt $PRICE ] ; then
echo "太高了"
else
echo "太低了"
fi
done
5. until循環(huán)
和while循環(huán)相反,until循環(huán)時(shí)只要條件判斷式不成立則進(jìn)行循環(huán),并執(zhí)行循環(huán)程序。一旦循環(huán)條件成立,則終止循環(huán)。
語法:
until [ 條件判斷式 ]
do
程序
done
案例一:1加到100
[root@localhost ~]$ vi sh/until.sh
#!/bin/bash
#從1加到100
i=1
s=0
#t給變量i和變量s賦值
until [ $i -gt 100 ]
#循環(huán)直到變量i的值大于100,就停止循環(huán)
do
s=$(( $s+$i ))
i=$(( $i+1 ))
done
echo "The sum is: $s"
6. 函數(shù)
語法:
function 函數(shù)名 () {
程序
}
案例:接收用戶輸入的數(shù)字,然后從1加到這個(gè)數(shù)字
[root@localhost ~]$ vi sh/function.sh
#!/bin/bash
#接收用戶輸入的數(shù)字,然后從1加到這個(gè)數(shù)字
function sum () {
#定義函數(shù)sum
s=0
for (( i=0; i<=$num;i=i+1 ))
#循環(huán)直到i大于$1為止。$1是函數(shù)sum 的第一個(gè)參數(shù)
#在函數(shù)中也可以使用位置參數(shù)變量,不過這里的$1指的是函數(shù)的第一個(gè)參數(shù)
do
s=$(( $i+$s ))
done
echo "The sum of 1+2+3...+$1 is :$s"
#輸出1加到$1的和
}
read -p "Please input a number: " -t 30 num
#接收用戶輸入的數(shù)字,并把值賦予變量num
y=$(echo $num | sed 's/[0-9]//g')
#把變量num的值替換為空,并賦予變量y
if [ -z "$y"]
#判斷變量y是否為空,以確定變量num中是否為數(shù)字
then
sum $num
#調(diào)用sum函數(shù),并把變量num的值作為第一個(gè)參數(shù)傳遞給sum函數(shù)
else
echo "Error!! Please input a number!"
#如果變量num 的值不是數(shù)字,則輸出報(bào)錯(cuò)信息
fi
7. 特殊流程控制語句
7.1 exit語句
系統(tǒng)是有exit命令的,用于退出當(dāng)前用戶的登錄狀態(tài)。可是在Shell腳本中,exit語句是用來退出當(dāng)前腳本的。也就是說,在Shell腳本中,只要碰到了exit語句,后續(xù)的程序就不再執(zhí)行,而直接退出腳本。
exit的語法如下:
exit [返回值]
如果exit命令之后定義了返回值,那么這個(gè)腳本執(zhí)行之后的返回值就是我們自己定義的返回值。可以通過查詢$?這個(gè)變量,來查看返回值。如果exit之后沒有定義返回值,腳本執(zhí)行之后的返回值是執(zhí)行exit 語句之前,最后執(zhí)行的一條命令的返回值。寫一個(gè)exit 的例子:
[root@localhost ~]$ vi sh/exit.sh
#!/bin/bash
#演示exit的作用
read -p "Please input a number: " -t 30 num
#接收用戶的輸入,并把輸入賦予變量num
y=$ (echo $num | sed 's/[0-9]//g')
#如果變量num 的值是數(shù)字,則把num的值替換為空,否則不替換
#把替換之后的值賦予變量y
[ -n "$y" ] && echo "Error! Please input a number!" && exit 18
#判斷變量y的值如果不為空,輸出報(bào)錯(cuò)信息,退出腳本,退出返回值為18
echo "The number is: $num"
#如果沒有退出加班,則打印變量num中的數(shù)字
7.2 break語句
當(dāng)程序執(zhí)行到break語句時(shí),會(huì)結(jié)束整個(gè)當(dāng)前循環(huán)。而continue 語句也是結(jié)束循環(huán)的語句,不過continue 語句單次當(dāng)前循環(huán),而下次循環(huán)會(huì)繼續(xù)。
案例:
[root@localhost ~]$ vi sh/break.sh
#!/bin/bash
#演示break 跳出循環(huán)
for (( i=1;i<=10; i=i+1 ))
#循環(huán)十次
do
if ["$i" -eq 4 ]
#如果變量i的值等于4
then
break
#退出整個(gè)循環(huán)
fi
echo $i
#輸出變量i的值
done
執(zhí)行下這個(gè)腳本,因?yàn)橐坏┳兞縤的值等于4,整個(gè)循環(huán)都會(huì)跳出,所以應(yīng)該只能循環(huán)三次:
[root@localhost ~]$ chmod 755 sh/break.sh
[root@localhost ~]#sh/break.sh
1
2
3
7.3 continue語句
continue也是結(jié)束流程控制的語句。如果在循環(huán)中,continue語句只會(huì)結(jié)束單次當(dāng)前循環(huán)。
案例:
[root@localhost ~]$ vi sh/break.sh
#!/bin/bash
#演示continue
for (( i=1;i<=10;i=i+1 ))
#循環(huán)十次
do
if ["$i" -eq 4 ]
#如果變量i的值等于4
then
continue
#退出換成continue
fi
echo $i
#輸出變量i的值
done
執(zhí)行下這個(gè)腳本:
[root@localhost ~]$ chmod 755 sh/continue.sh
[root@localhost ~]#sh/break.sh
1
2
3
5
6
7
8
9
10
#少了4這個(gè)輸出
六、字符截取、替換和處理命令
正則表達(dá)式
元字符 |
描述 |
示例 |
|
轉(zhuǎn)義符,將特殊字符進(jìn)行轉(zhuǎn)義,忽略其特殊意義 |
a.b匹配a.b,但不能匹配ajb,.被轉(zhuǎn)義為特殊意義 |
^ |
匹配行首,awk中,^則是匹配字符串的開始 |
^tux匹配以tux開頭的行 |
$ |
匹配行尾,awk中,$則是匹配字符串的結(jié)尾 |
tux$匹配以tux結(jié)尾的行 |
. |
匹配除換行符n之外的任意單個(gè)字符 |
ab.匹配abc或abd,不可匹配abcd或abde,只能匹配單字符 |
[ ] |
匹配包含在[字符]之中的任意一個(gè)字符 |
coo[kl]可以匹配cook或cool |
[^] |
匹配[^字符]之外的任意一個(gè)字符 |
123[^45]不可以匹配1234或1235,1236、1237都可以 |
[-] |
匹配[]中指定范圍內(nèi)的任意一個(gè)字符,要寫成遞增 |
[0-9]可以匹配1、2或3等其中任意一個(gè)數(shù)字 |
? |
匹配之前的項(xiàng)1次或者0次 |
colou?r可以匹配color或者colour,不能匹配colouur |
+ |
匹配之前的項(xiàng)1次或者多次 |
sa-6+匹配sa-6、sa-666,不能匹配sa- |
* |
匹配之前的項(xiàng)0次或者多次 |
co*l匹配cl、col、cool、coool等 |
() |
匹配表達(dá)式,創(chuàng)建一個(gè)用于匹配的子串 |
ma(tri)?匹配max或maxtrix |
{n} |
匹配之前的項(xiàng)n次,n是可以為0的正整數(shù) |
[0-9]{3}匹配任意一個(gè)三位數(shù),可以擴(kuò)展為[0-9][0-9][0-9] |
{n,} |
之前的項(xiàng)至少需要匹配n次 |
[0-9]{2,}匹配任意一個(gè)兩位數(shù)或更多位數(shù)不支持{n,}{n,}{n,} |
{n,m} |
指定之前的項(xiàng)至少匹配n次,最多匹配m次,n<=m |
[0-9]{2,5}匹配從兩位數(shù)到五位數(shù)之間的任意一個(gè)數(shù)字 |
| |
交替匹配|兩邊的任意一項(xiàng) |
ab(c|d)匹配abc或abd |
1 字符截取、替換命令
1.1 cut 列提取命令
[root@localhost ~]$ cut [選項(xiàng)] 文件名
選項(xiàng):
-f 列號(hào): 提取第幾列
-d 分隔符: 按照指定分隔符分割列
-n取消分割多字節(jié)字符
-c 字符范圍: 不依賴分隔符來區(qū)分列,而是通過字符范圍(行首為0)來進(jìn)行字段提取。“n-”表示從第n個(gè)字符到行尾;“n-m”從第n個(gè)字符到第m個(gè)字符;“一m”表示從第1個(gè)字符到第m個(gè)字符。
--complement補(bǔ)足被選擇的字節(jié)、字符或字段
--out-delimiter指定輸出內(nèi)容是的字段分割符
cut命令的默認(rèn)分隔符是制表符,也就是“tab”鍵,不過對(duì)空格符可是支持的不怎么好啊。我們先建立一個(gè)測(cè)試文件,然后看看cut命令的作用吧:
[root@localhost ~]$ vi student.txt
idnamegendermark
1limingm86
2scm67
3tgn90
[root@localhost ~]$ cut -f 2 student.txt
#提取第二列內(nèi)容
那如果想要提取多列呢?只要列號(hào)直接用“,”分開,命令如下:
[root@localhost ~]$ cut -f 2,3 student.txt
如果我想用cut命令截取df命令的第一列和第三列,就會(huì)出現(xiàn)這樣的情況:
[root@localhost~]$ df -h | cut -d " " -f 1,3
Filesystem
/dev/sda2
tmpfs
/dev/sda1
1.2 awk 編程
參考:
linux文本處理三劍客:grep,sed及awk
1.2.1 awk 概述
AWK 是一種處理文本文件的語言,是一個(gè)強(qiáng)大的文本分析工具。
1.2.2 printf 格式化輸出
[root@localhost ~]$ printf ‘輸出類型輸出格式’ 輸出內(nèi)容
輸出類型:
%c: ASCII字符.顯示相對(duì)應(yīng)參數(shù)的第一個(gè)字符
%-ns: 輸出字符串,減號(hào)“-”表示左對(duì)齊(默認(rèn)右對(duì)齊),n是數(shù)字指代輸出幾個(gè)字符,幾個(gè)參數(shù)就寫幾個(gè)%-ns
%-ni: 輸出整數(shù),n是數(shù)字指代輸出幾個(gè)數(shù)字
%f: 輸出小數(shù)點(diǎn)右邊的位數(shù)
%m.nf: 輸出浮點(diǎn)數(shù),m和n是數(shù)字,指代輸出的整數(shù)位數(shù)和小數(shù)位數(shù)。如%8.2f代表共輸出8位數(shù),其中2位是小數(shù),6位是整數(shù)。
輸出格式:
a: 輸出警告聲音
b: 輸出退格鍵,也就是Backspace鍵
f: 清除屏幕
n: 換行
r: 回車,也就是Enter鍵
t: 水平輸出退格鍵,也就是Tab 鍵
v: 垂直輸出退格鍵,也就是Tab 鍵
為了演示printf命令,我們需要修改下剛剛cut命令使用的student.txt文件,文件內(nèi)容如下:
[root@localhost ~]$ vi student.txt
ID Name php Linux MySQL Average
1 AAA 66 66 66 66
2 BBB 77 77 77 77
3 CCC 88 88 88 88
#printf格式輸出文件
[root@localhost ~]$ printf '%st %st %st %st %st %st n’ $(cat student.txt)
#%s分別對(duì)應(yīng)后面的參數(shù),6列就寫6個(gè)
ID Name php Linux MySQL Average
1 AAA 66 66 66 66
2 BBB 77 77 77 77
3 CCC 88 88 88 88
如果不想把成績(jī)當(dāng)成字符串輸出,而是按照整型和浮點(diǎn)型輸出,則要這樣:
[root@localhost ~]$ printf '%it %st %it %it %it %8.2ft n’ $(cat student.txt | grep -v Name)
1.2.3 awk 基本使用
[root@localhost ~]$ awk‘條件1{動(dòng)作1} 條件2{動(dòng)作2}…’ 文件名
條件(Pattern):
一般使用關(guān)系表達(dá)式作為條件。這些關(guān)系表達(dá)式非常多,例如:
x > 10 判斷變量x是否大于10
x == y 判斷變量x是否等于變量y
A ~ B 判斷字符串A中是否包含能匹配B表達(dá)式的子字符串
A !~ B 判斷字符串A中是否不包含能匹配B表達(dá)式的子字符串
動(dòng)作(Action) :
格式化輸出
流程控制語句
常用參數(shù):
-F指定輸入時(shí)用到的字段分隔符
-v自定義變量
-f從腳本中讀取awk命令
-m對(duì)val值設(shè)置內(nèi)在限制
我們這里先來學(xué)習(xí)awk基本用法,也就是只看看格式化輸出動(dòng)作是干什么的。
[root@localhost ~]$ awk '{printf $2 "t" $6 "n"}’ student.txt
#輸出第二列和第六列
比如剛剛截取df命令的結(jié)果時(shí),cut命令已經(jīng)力不從心了,我們來看看awk命令:
[root@localhost ~]$ df -h | awk '{print $1 "t" $3}'
#截取df命令的第一列和第三列
1.2.4 awk 的條件
條件的類型 |
條件 |
說明 |
awk保留字 |
BEGIN |
在awk程序一開始時(shí),尚未讀取任何數(shù)據(jù)之前執(zhí)行。BEGIN后的動(dòng)作只在程序開始時(shí)執(zhí)行一次 |
awk保留字 |
END |
在awk程序處理完所有數(shù)據(jù),即將結(jié)束時(shí)執(zhí)行。END后的動(dòng)作只在程序結(jié)束時(shí)執(zhí)行一次 |
關(guān)系運(yùn)算符 |
> |
大于 |
關(guān)系運(yùn)算符 |
< |
小于 |
關(guān)系運(yùn)算符 |
>= |
大于等于 |
關(guān)系運(yùn)算符 |
<= |
小于等于 |
關(guān)系運(yùn)算符 |
== |
等于。用于判斷兩個(gè)值是否相等,如果是給變量賦值,請(qǐng)使用“”號(hào) |
關(guān)系運(yùn)算符 |
!= |
不等于 |
關(guān)系運(yùn)算符 |
A~B |
判斷字符串A中是否包含能匹配B表達(dá)式的子字符串 |
關(guān)系運(yùn)算符 |
A!~B |
判斷字符串A中是否不包含能匹配B表達(dá)式的子字符串 |
正則表達(dá)式 |
/正則/ |
如果在"//"中可以寫入字符,也可以支持正則表達(dá)式 |
BEGIN
BEGIN是awk的保留字,是一種特殊的條件類型。BEGIN的執(zhí)行時(shí)機(jī)是“在 awk程序一開始時(shí),尚未讀取任何數(shù)據(jù)之前執(zhí)行”。一旦BEGIN后的動(dòng)作執(zhí)行一次,當(dāng)awk開始從文件中讀入數(shù)據(jù),BEGIN的條件就不再成立,所以BEGIN定義的動(dòng)作只能被執(zhí)行一次。
例如:
[root@localhost ~]$ awk 'BEGIN{printf "This is a transcript n" } {printf $2 "t" $6 "n"}’ student.txt
#awk命令只要檢測(cè)不到完整的單引號(hào)不會(huì)執(zhí)行,所以這個(gè)命令的換行不用加入“|”,就是一行命令
#這里定義了兩個(gè)動(dòng)作
#第一個(gè)動(dòng)作使用BEGIN條件,所以會(huì)在讀入文件數(shù)據(jù)前打印“這是一張成績(jī)單”(只會(huì)執(zhí)行一次)
#第二個(gè)動(dòng)作會(huì)打印文件的第二字段和第六字段
END
END也是awk保留字,不過剛好和BEGIN相反。END是在awk程序處理完所有數(shù)據(jù),即將結(jié)束時(shí)執(zhí)行。END后的動(dòng)作只在程序結(jié)束時(shí)執(zhí)行一次。例如:
[root@localhost ~]$ awk 'END{printf "The End n"} {printf $2 "t" $6 "n"}’ student.txt
#在輸出結(jié)尾輸入“The End”,這并不是文檔本身的內(nèi)容,而且只會(huì)執(zhí)行一次
關(guān)系運(yùn)算符
舉幾個(gè)例子看看關(guān)系運(yùn)算符。假設(shè)我想看看平均成績(jī)大于等于87分的學(xué)員是誰,就可以這樣輸入命令:
例子1:
[root@localhost ~]$ cat student.txt | grep -v Name | awk '$6 >= 87 {printf $2 "n"}'
#使用cat輸出文件內(nèi)容,用grep取反包含“Name”的行
#判斷第六字段(平均成績(jī))大于等于87分的行,如果判斷式成立,則打第六列(學(xué)員名$2)
加入了條件之后,只有條件成立動(dòng)作才會(huì)執(zhí)行,如果條件不滿足,則動(dòng)作則不運(yùn)行。通過這個(gè)實(shí)驗(yàn),大家可以發(fā)現(xiàn),雖然awk是列提取命令,但是也要按行來讀入的。這個(gè)命令的執(zhí)行過程是這樣的:
1)如果有BEGIN條件,則先執(zhí)行BEGIN定義的動(dòng)作。
2)如果沒有BEGIN條件,則讀入第一行,把第一行的數(shù)據(jù)依次賦予$0、$1、$2等變量。其中$0代表此行的整體數(shù)據(jù),$1代表第一字段,$2代表第二字段。
3)依據(jù)條件類型判斷動(dòng)作是否執(zhí)行。如果條件符合,則執(zhí)行動(dòng)作,否則讀入下一行數(shù)據(jù)。如果沒有條件,則每行都執(zhí)行動(dòng)作。
4)讀入下一行數(shù)據(jù),重復(fù)執(zhí)行以上步驟。
再舉個(gè)例子,如果我想看看Sc用戶的平均成績(jī)呢:
例子2:
[root@localhost ~]$ awk '$2 ~ /AAA/ {printf $6 "n"}' student.txt
#如果第二字段中輸入包含有“Sc”字符,則打印第六字段數(shù)據(jù)
85.66
這里要注意在awk中,使用“//”包含的字符串,awk命令才會(huì)查找。也就是說字符串必須用“//”包含,awk命令才能正確識(shí)別。
正則表達(dá)式
如果要想讓awk 識(shí)別字符串,必須使用“//”包含,例如:
例子1:
[root@localhost ~]$ awk '/Liming/ {print}’student.txt
#打印Liming的成績(jī)
當(dāng)使用df命令查看分區(qū)使用情況是,如果我只想查看真正的系統(tǒng)分區(qū)的使用狀況,而不想查看光盤和臨時(shí)分區(qū)的使用狀況,則可以:
例子2:
[root@localhost ~]$ df -h | awk '/sda[O-9]/ {printf $1 "t" $5 "n"}’
#查詢包含有sda數(shù)字的行,并打印第一字段和第五字段
1.2.5 awk 內(nèi)置變量
awk內(nèi)置變量 |
作用 |
$0 |
代表目前awk所讀入的整行數(shù)據(jù)。我們已知awk是一行一行讀入數(shù)據(jù)的,$0就代表當(dāng)前讀入行的整行數(shù)據(jù)。 |
$n |
代表目前讀入行的第n個(gè)字段。比如, $1表示第1個(gè)字段(列),$ 2表示第2個(gè)字段(列),如此類推 |
NF |
當(dāng)前行擁有的字段(列)總數(shù)。 |
NR |
當(dāng)前awk所處理的行,是總數(shù)據(jù)的第幾行。 |
FS |
用戶定義分隔符。awk的默認(rèn)分隔符是任何空格,如果想要使用其他分隔符(如“:”),就需要FS變量定義。 |
ARGC |
命令行參數(shù)個(gè)數(shù)。 |
ARGV |
命令行參數(shù)數(shù)組。 |
FNR |
當(dāng)前文件中的當(dāng)前記錄數(shù)(對(duì)輸入文件起始為1)。 |
OFMT |
數(shù)值的輸出格式(默認(rèn)為%.6g)。 |
OFS |
輸出字段的分隔符(默認(rèn)為空格)。 |
ORS |
輸出記錄分隔符(默認(rèn)為換行符)。 |
RS |
輸入記錄分隔符(默認(rèn)為換行符)。 |
awk常用統(tǒng)計(jì)實(shí)例
1、打印文件的第一列(域) :
awk '{print $1}' filename
2、打印文件的前兩列(域) :
awk '{print $1,$2}' filename
3、打印完第一列,然后打印第二列 :
awk '{print $1 $2}' filename
4、打印文本文件的總行數(shù) :
awk 'END{print NR}' filename
5、打印文本第一行 :
awk 'NR==1{print}' filename
6、打印文本第二行第一列 :
sed -n "2, 1p" filename | awk 'print $1'
1. 獲取第一列
ps -aux | grep watchdog | awk '{print $1}'
2. 獲取第一列,第二列,第三列
ps -aux | grep watchdog | awk '{print $1, $2, $3}'
3. 獲取第一行的第一列,第二列,第三列
ps -aux | grep watchdog | awk 'NR==1{print $1, $2, $3}'
4. 獲取行數(shù)NR
df -h | awk 'END{print NR}'
5. 獲取列數(shù)NF(這里是獲取最后一行的列數(shù),注意每行的列數(shù)可能是不同的)
ps -aux | grep watchdog | awk 'END{print NF}'
6. 獲取最后一列
ps -aux | grep watchdog | awk '{print $NF}'
7. 對(duì)文件進(jìn)行操作
awk '{print $1}' fileName
8. 指定分隔符(這里以:分割)
ps -aux | grep watchdog |awk -F':' '{print $1}'
9. 超出范圍不報(bào)錯(cuò)
ps -aux | grep watchdog | awk '{print $100}'
[root@localhost ~]$ cat /etc/passwd | grep "/bin/bash" | awk '{FS=":"} {printf $1 "t" $3 "n"}’
#查詢可以登錄的用戶的用戶名和UID
這里“:”分隔符生效了,可是第一行卻沒有起作用,原來我們忘記了“BEGIN”條件,那么再來試試;
[root@localhost ~]$ cat /etc/passwd | grep "/bin/bash" | awk 'BEGIN {FS=":"} {printf $1 "t" $3 "n"}’
[root@localhost ~]$ cat /etc/passwd | grep "/bin/bash" | awk 'BEGIN {FS=":"} {printf $1 "t" $3 "t 行號(hào):” NR "t 字段數(shù):" NF "n"}’
#解釋下awk命令
#開始執(zhí)行{分隔符是“:”}{輸出第一字段和第三字段輸出行號(hào)(NR值)字段數(shù)(NF值)}
root 0 行號(hào):1 字段數(shù):7
user1 501 行號(hào):2 字段數(shù):7
如果我只想看看sshd這個(gè)偽用戶的相關(guān)信息,則可以這樣使用:
[root@localhost ~]$ cat /etc/passwd | awk 'BEGIN {FS=":"} $1=="sshd" {printf $1 "t" $3 "t 行號(hào):" NR "t 字段數(shù):" NF "n"}’
#可以看到sshd 偽用戶的UID是74,是/etc/passwd_文件的第28行,此行有7個(gè)字段
1.2.6 awk 流程控制
我們?cè)賮砝孟聅tudent.txt文件做個(gè)練習(xí),后面的使用比較復(fù)雜,我們?cè)倏纯催@個(gè)文件的內(nèi)容:
[root@localhost ~]$ cat student.txt
ID Name php Linux MySQL Average
1 AAA 66 66 66 66
2 BBB 77 77 77 77
3 CCC 88 88 88 88
我們先來看看該如何在awk中定義變量與調(diào)用變量的值。假設(shè)我想統(tǒng)計(jì)PHP成績(jī)的總分,那么就應(yīng)該這樣:
[root@localhost ~]$ awk 'NR==2 {php1=$3}
NR==3 {php2=$3}
NR==4 {php3=$3;totle=phpl+php2+php3;print "totle php is " totle}’ student.txt
#統(tǒng)計(jì)PHIP成績(jī)的總分
我們解釋下這個(gè)命令。
"NR==2 {php1=$3}" (條件是NR==2,動(dòng)作是php1=$3)這句話是指如果輸入數(shù)據(jù)是第二行(第一行是標(biāo)題行),就把第二行的第三字段的值賦予變量"php1"。
"NR==3 {php2=$3}" 這句話是指如果輸入數(shù)據(jù)是第三行,就把第三行的第三字段的值賦予變量"php2"。
"NR==4 {php3=$3;totle=phpl+php2+php3;print "totle php is " totle}"("NR==4"是條件,后面{}中的都是動(dòng)作)這句話是指如果輸入數(shù)據(jù)是第四行﹐就把第四行的第三字段的值賦予變量"php3";然后定義變量totle的值是"php1+php2+php3";然后輸出"totle php is"關(guān)鍵字,后面加變量totle的值。
在awk編程中,因?yàn)槊钫Z句非常長(zhǎng),在輸入格式時(shí)需要注意以下內(nèi)容:
l 多個(gè)條件 {動(dòng)作} 可以用空格分割,也可以用回車分割。
l 在一個(gè)動(dòng)作中,如果需要執(zhí)行多個(gè)命令,需要用 “;” 分割,或用回車分割。
l 在awk中,變量的賦值與調(diào)用都不需要加入“$”符。
l 條件中判斷兩個(gè)值是否相同,請(qǐng)使用 “==”,以便和變量賦值進(jìn)行區(qū)分。
在看看該如何實(shí)現(xiàn)流程控制,假設(shè)如果Linux成績(jī)大于90,就是一個(gè)好男人(學(xué)PHP的表示壓力很大!) :
[root@localhost ~]$ awk '{if (NR>=2) {if ($4>60) printf $2 "is a good man!n"}}’ student.txt
#程序中有兩個(gè)if判斷,第一個(gè)判斷行號(hào)大于2,第二個(gè)判斷Linux成績(jī)大于90分
Liming is a good man !
Sc is a good man !
1.2.7 awk 函數(shù)
awk編程也允許在編程時(shí)使用函數(shù),我們講講awk的自定義函數(shù)。awk函數(shù)的定義方法如下:
function 函數(shù)名(參數(shù)列表){
函數(shù)體
}
我們定義一個(gè)簡(jiǎn)單的函數(shù),使用函數(shù)來打印student.txt的學(xué)員姓名和平均成績(jī),應(yīng)該這樣來寫函數(shù):
[root@localhost ~]$ awk 'function test(a,b) { printf a "t" b "n"}
#定義函數(shù)test,包含兩個(gè)參數(shù),函數(shù)體的內(nèi)容是輸出這兩個(gè)參數(shù)的值
{ test($2,$6) } ' student.txt
#調(diào)用函數(shù)test,并向兩個(gè)參數(shù)傳遞值。
Name Average
AAA 87.66
BBB 85.66
CCC 91.66
1.2.8 awk 中調(diào)用腳本
對(duì)于小的單行程序來說,將腳本作為命令行自變量傳遞給awk是非常簡(jiǎn)單的,而對(duì)于多行程序就比較難處理。當(dāng)程序是多行的時(shí)候,使用外部腳本是很適合的。首先在外部文件中寫好腳本,然后可以使用awk的-f選項(xiàng),使其讀入腳本并且執(zhí)行。
例如,我們可以先編寫一個(gè)awk腳本:
[root@localhost ~]$ vi pass.awk
BEGIN {FS=":"}
{ print $1 "t" $3}
然后可以使用“一f”選項(xiàng)來調(diào)用這個(gè)腳本:
[root@localhost ~]$ awk -f pass.awk /etc/passwd
rooto
bin1
daemon2
…省略部分輸出…
1.3 sed 文本選取、替換、刪除、新增的命令
sed主要是用來將數(shù)據(jù)進(jìn)行選取、替換、刪除、新增的命令。
語法:
[root@localhost ~]$ sed [選項(xiàng)] ‘[動(dòng)作]’ 文件名
選項(xiàng):
-n: 一般sed命令會(huì)把所有數(shù)據(jù)都輸出到屏幕,如果加入此選擇,則只會(huì)把經(jīng)過sed命令處理的行輸出到屏幕。
-e: 允許對(duì)輸入數(shù)據(jù)應(yīng)用多條sed命令編輯。
-f 腳本文件名: 從sed腳本中讀入sed操作。和awk命令的-f非常類似。
-r: 在sed中支持?jǐn)U展正則表達(dá)式。
-i: 用sed的修改結(jié)果直接修改讀取數(shù)據(jù)的文件,而不是由屏幕輸出
動(dòng)作:
num a : 追加,在當(dāng)前行后添加一行或多行。添加多行時(shí),除最后一行外,每行末尾需要用“”代表數(shù)據(jù)未完結(jié)。num表示第幾行
c : 行替換,用c后面的字符串替換原數(shù)據(jù)行,替換多行時(shí),除最后一行外,每行末尾需用“”代表數(shù)據(jù)未完結(jié)。
num i : 插入,在當(dāng)期行前插入一行或多行。插入多行時(shí),除最后一行外,每行末尾需要用“”代表數(shù)據(jù)未完結(jié)。num表示第幾行
d ; 刪除,刪除指定的行。
p : 打印,輸出指定的行。
s : 字串替換,用一個(gè)字符串替換另外一個(gè)字符串。格式為“行范圍s/"舊字串/新字串/g”(和vim中的替換格式類似)。
對(duì)sed命令大家要注意,sed所做的修改并不會(huì)直接改變文件的內(nèi)容(如果是用管道符接收的命令的輸出,這種情況連文件都沒有),而是把修改結(jié)果只顯示到屏幕上,除非使用“-i”選項(xiàng)才會(huì)直接修改文件。
2 字符處理命令
2.1 sort 排序命令
[root@localhost~]$ sort [選項(xiàng)] 文件名
選項(xiàng):
-f: 忽略大小寫
-b: 忽略每行前面的空白部分
-n: 以數(shù)值型進(jìn)行排序,默認(rèn)使用字符串型排序
-r: 反向排序
-u: 刪除重復(fù)行。就是uniq命令
-t: 指定分隔符,默認(rèn)是分隔符是制表符
-k n[,m]: ―按照指定的字段范圍排序。從第n字段開始,m字段結(jié)束(默認(rèn)到行尾)
案例:
sort命令默認(rèn)是用每行開頭第一個(gè)字符來進(jìn)行排序的,比如:
[root@localhost~]$ sort /etc/passwd
#排序用戶信息文件
如果想要反向排序,請(qǐng)使用“-r”選項(xiàng):
[root@localhost~]$ sort -r/etc/passwd
#反向排序
如果想要指定排序的字段,需要使用“-t”選項(xiàng)指定分隔符,并使用“-k”選項(xiàng)指定字段號(hào)。加入我想要按照UID字段排序/etc/passwd文件:
[root@localhost~]$ sort -t ":" -k 3,3 /etc/passwd
#指定分隔符是“:”,用第三字段開頭,第三字段結(jié)尾排序,就是只用第三字段排序
因?yàn)閟ort默認(rèn)是按照字符排序,前面用戶的UID的第一個(gè)字符都是1,所以這么排序。要想按照數(shù)字排序,請(qǐng)使用“-n”選項(xiàng):
[root@localhost~]$ sort -n -t ":" -k 3,3 /etc/passwd
當(dāng)然“-k”選項(xiàng)可以直接使用“-k 3”,代表從第三字段到行尾都排序(第一個(gè)字符先排序,如果一致,第二個(gè)字符再排序,知道行尾)。
2.2 uniq 取消重復(fù)行
uniq [-c/d/D/u/i] [-f Fields] [-s N] [-w N] [InFile] [OutFile]
參數(shù)解釋:
-c: 在每列旁邊顯示該行重復(fù)出現(xiàn)的次數(shù)。
-d: 僅顯示重復(fù)出現(xiàn)的行列,顯示一行。
-D: 顯示所有重復(fù)出現(xiàn)的行列,有幾行顯示幾行。
-u: 僅顯示出一次的行列
-i: 忽略大小寫字符的不同
-f Fields: 忽略比較指定的列數(shù)。
-s N: 忽略比較前面的N個(gè)字符。
-w N: 對(duì)每行第N個(gè)字符以后的內(nèi)容不作比較。
# uniq.txt
My name is Delav
My name is Delav
My name is Delav
I'm learning JAVA
I'm learning Java
I'm learning Java
who am i
Who am i
Python/ target=_blank class=infotextkey>Python is so simple
My name is Delav
That's good
That's good
And studying Golang
直接去重
uniq uniq.txt
結(jié)果為:
My name is Delav
I'm learning Java
who am i
Who am i
Python is so simple
My name is Delav
That's good
And studying Golang
顯示重復(fù)出現(xiàn)的次數(shù)
uniq -c uniq.txt 、
結(jié)果為:
3 My name is Delav
3 I'm learning Java
1 who am i
1 Who am i
1 Python is so simple
1 My name is Delav
2 That's good
1 And studying Golang
你會(huì)發(fā)現(xiàn),上面有兩行 ”My name is Delav ” 是相同的。也就是說,當(dāng)重復(fù)的行不相鄰時(shí),uniq 命令是不起作用的。所以,經(jīng)常需要跟
sort 命令一起使用。
sort uniq.txt | uniq -c
結(jié)果為:
1 And studying Golang
3 I'm learning Java
4 My name is Delav
1 Python is so simple
2 That's good
1 who am i
1 Who am i
只顯示重復(fù)的行,并顯示重復(fù)次數(shù)
uniq -cd uniq.txt
結(jié)果為:
3 My name is Delav
3 I'm learning Java
2 That's good
顯示所有重復(fù)的行,不能與 -c 一起使用
uniq -D uniq.txt
My name is Delav
My name is Delav
My name is Delav
I'm learning Java
I'm learning Java
I'm learning Java
That's good
That's good
忽略第幾列字符
下面這里 -f 1 忽略了第一列字符,所以"who am i" 和 "Who am i" 判定為重復(fù)
uniq -c -f 1 uniq.txt
結(jié)果為:
3 My name is Delav
3 I'm learning Java
2 who am i
1 Python is so simple
1 My name is Delav
2 That's good
1 And studying Golang
忽略大小寫
下面這里 -i 忽略了大小寫,所以"who am i" 和 "Who am i" 判定為重復(fù)
uniq -c -i uniq.txt
結(jié)果為:
3 My name is Delav
3 I'm learning Java
2 who am i
1 Python is so simple
1 My name is Delav
2 That's good
1 And studying Golang
忽略前面N個(gè)字符
下面這里 -s 4 表示忽略前面四個(gè)字符,所以"who am i" 和 "Who am i" 判定為重復(fù)
uniq -c -s 4 uniq.txt
結(jié)果為:
3 My name is Delav
3 I'm learning Java
2 who am i
1 Python is so simple
1 My name is Delav
2 That's good
1 And studying Golang
忽略第N個(gè)字符后的內(nèi)容
uniq -c -w 2 uniq.txt
2.3 wc 統(tǒng)計(jì)命令
[root@localhost~]$ wc [選項(xiàng)] 文件名
選項(xiàng):
-l:只統(tǒng)計(jì)行數(shù)
-w:只統(tǒng)計(jì)單詞數(shù)
-m:只統(tǒng)計(jì)字符數(shù)