shell腳本基礎
shell腳本是包含一些命令或聲明,并符合一定格式的文本文件
一般格式要求:
1.首行shebang機制
#!/bin/bash
#!/bin/python
#!/usr/bin/perl
2.注釋信息
以#開頭,可寫明腳本用途,作者,版本,時間等
3.腳本正文內容,
創建shell腳本
以bash為例
-
第一行定格#!/bin/bash
-
注釋信息:
-
#用途
-
#作者
-
#版本
-
#時間
-
腳本正文內容,保存退出
-
給予腳本文件執行權限,
-
在命令上執行腳本的絕對路徑或相對路徑
-
直接運行解釋器,將腳本作為解釋器程序的參數運行
腳本范例
腳本調試
當我們寫完腳本時,可以使用指令檢測腳本中的語法錯誤
bash -n /path/to/some_script ==>檢測錯誤
bash -x /path/to/some_script ==>調試執行
變量 :命令的內存空間
變量命名法則:
-
不能使用程序中的保留字:例如if,for ==>不能使用系統自帶的變量名
-
只能使用數字,字母及下劃線,且不能以數字開頭
-
見名知意
-
統一命名規則:駝峰命名法
變量種類
本地變量:生效范圍為當前shell進程;對當前shell之外的其他shell進程,包括當前shell的子shell進程均無效
說明:腳本file1.sh定義變量num1,且腳本file2.sh是其子進程,腳本file2.sh中定義變量num2
運行file2.sh,變量num2有效顯示,變量num1未顯示,運行file1.sh,
變量num1有效顯示,變量num2未顯示,子進程file2.sh運行,變量num2有效顯示,變量num1未顯示
由以上可知,以上變量只在當前進程有效
環境變量:生效范圍為當前shell進程及其子進程
說明:在上面本地變量的基礎上將變量類型改變成環境變量,執行效果也有差異
file2.sh是file.1sh的子進程,file1.sh中定義的變量file2.sh在作為其子進程運行時,
是有效果的,但是在file2.sh不作為file1.sh的子進程運行時,是沒效果的,看單獨運行
file2.sh中只有變量num2,而在運行file1.sh時,num2沒有繼承其子進程的變量
所以:生效范圍為當前shell進程及其子進程,其父進程也是不生效的
局部變量:生效范圍為當前shell進程中某代碼片段(通常指函數)
位置變量:$1,$2,…..來表示,用于讓腳本在腳本代碼中調用通過命令行傳遞給它的參數
特殊變量:$?,$0,$*,$@,$#
本地變量:
變量賦值:name=‘value’
-
可以直接是字符串;name=“root”
-
變量引用:name="$USER"
-
命令引用:name=`command` ,name=$(command)
變量引用:${name} ,$name
" " :弱引用,其中的變量引用會被替換為變量值
‘ ’:強引用,其中的變量引用不會被替換為變量值,而保持原字符
顯示已定義的所有變量:set
刪除變量: unset name
環境變量
變量賦值:
export name=VALUE
declare -x name=VALUE
變量引用:${name} ,$name
顯示所有環境變量:
export
env
printenv
刪除變量: unset name
bash內建的環境變量:PATH,SHELL,USER,UID,HISTSIZE,HOME,PWD,OLPWD,PS1,HISTFILE
只讀變量:只能用,不能修改和刪除
readonly name
declare -r name
可用來定義一些固定的值,直接引用,例如π
位置變量:在腳本代碼中調用通過命令行傳遞給腳本的參數
$1,$2,…:對應第1,2,….等參數
$0 :命令本身
$* :傳遞給腳本的所有參數,全部參數何為一個字符串
$@ :傳遞給腳本的所有參數,每個參數為獨立字符串
$# :傳遞給腳本的參數的個數
備注:$@ $* 只在被雙引號包起來的時候才會有差異(后面會專門驗證)
算數運算:
bash的算術運算: +,-,*,/,%取余,**(乘方)
實現算術運算:
-
let var=算術表達式
-
var=$[算術運算]
-
var=$((算術運算))
-
var=$(expr arg1 arg2 arg3 ….)
-
declare -i var = 數值
-
echo `算術表達式` |bc
乘法符號游戲場景中需要轉義:\*
bash 有內建的隨機數生成器:$RANDOM (1-32767) )
echo $[$RANDOM%50] :0-49 之間隨機數
賦值
增強型賦值:
變量做某種算數運算后會存至此變量中
i=$[$i+1] ==>每運行一次此命令,變量i的值會增加1
let i+=1 同上
let i+=2每次增加2
let(+=,-=,*=,/=,%=)
自增:
VAR=$[$VAR+1]
let VAR+=1
let VAR++
自減:
VAR=$[$VAR-1]
let VAR-=1
let VAR–
邏輯運算
true :1 false:0
與:
1與1=1
1與0=0
0與1=0
0與0=0
或:
1或1=1
1或0=1
0或1=1
0或0=0
非:!
!1=0
!0=1
短路運算:
短路與:
第一個為0,結果必定為0
第一個為1,第二個必須要參與運算
短路或:
第一個為1,結果必定為1
第一個為0,第二個必須要參與運算
異或:^
異或的兩個值相同為假,不同為真
聚集命令
復合式:date;who| wc -l
命令會一個接一個的運行
子shell:(date;who|wc -l)>>/tmp/trace
所有的輸出都被發送給單個的STDOUT和STDERR
退出狀態
進程使用退出狀態來報告成功或失敗
0:成功
1-255:失敗
$? :變量保存最近的命令退出狀態
退出狀態碼
bash自定義退出狀態碼
exit [n] :自定義退出狀態碼
ps:腳本中一旦遇到exit命令,腳本會立即終止;終止狀態取決于exit命令后面的數字
如果未給腳本指定退出狀態碼,整個腳本的推出狀態碼取決于腳本中執行的最后一條命令的狀態碼
條件測試
判斷某需求是否滿足,需由測試機制來實現
如何編寫測試表達式以實現所需的測試:
1.執行命令,并利用命令的狀態返回值來判斷
0:成功
1-255:失敗
2.測試表達式
test expression 比較大小
[ expression ]
[[ expression ]]
注意:expression兩端必須有空白字符,否則為語法錯誤
根據退出狀態而定,命令可以有條件的運行
&& 代表條件性的AND THEN
|| 代表條件性的OR ELSE
短路與: command1&&command2
如果command1成功,執行command2
如果command1失敗,不執行command2
短路或:command1||command2
如果command1成功,跳過command2
如果command1失敗,將執行command2
test
長格式
test "$A" == "$B"&& echo "xiangdeng"
test "$A" -eq "$B" && echo "budengde"
簡寫格式
[ "$A" == "$B" ]&& echo "xiangdeng"
[ "$A" -eq "$B ]&& echo "budengde"
bash 測試類型
數值測試:
-gt:是否大于
-ge:是否大于等于
-eq:是否等于
-ne:是否不等于
-lt:是否小于
-le:是否小于等于
字符串測試
== :是否等于
> :ascii碼是否大于ascii碼
< :ascii碼是否小于ascii碼
!=:ascii碼是否不等于ascii碼
=~ :左側字符串是否能夠被右側pattern所匹配
注意:此表達式一般用于[[ ]]中
-z “string”:字符串是否為空,空為真,不空為假
-n “string”:字符串是否不空,不空為真,空為假
注意:用于字符串比較時的用到的操作數都應該使用引號
[root@localhost ~]# A=bbb [root@localhost ~]# B=aaa [root@localhost ~]# C=aaa [root@localhost ~]# [[ "$C" == "$B" ]]&& echo right right [root@localhost ~]# [[ "$C" == "$A" ]]&& echo right [root@localhost ~]# [[ "$B" == "$A" ]]&& echo right [root@localhost ~]# [[ "$B" == "$A" ]]&& echo right||echo wrong wrong [root@localhost ~]# [[ "$B" == "$c" ]]&& echo right||echo wrong wrong [root@localhost ~]# [[ "$B" == "$C" ]]&& echo right||echo wrong right [root@localhost ~]# [[ "$A" == "$C" ]]&& echo right||echo wrong wrong
文件測試
存在性測試
-e file:文件存在性測試,存在為真,否則為假
-a file:同-e
存在性及類別測試
-b file :是否存在且為塊設備文件
-c file :是否存在且為字符設備文件
-d file :是否存在且為目錄文件
-f file :是否存在且為普通文件
-h file 或者-L file:存在且為符號鏈接文件
-p file :是否存在且為命名管道文件
-S file : 是否存在且為套接字文件
文件權限測試:
-r file:是否存在且為可讀
-w file: 是否存在且為可寫
-x file: 是否存在且為可執行
文件特殊權限測試
-g file :是否存在且擁有sgid權限
-u file :是否存在且擁有suid權限
-k file :是否存在且擁有sticky權限
文件大小測試
-s file :是否存在且為非空
文件是否打開
-t fd :fd表示文件描述是否已經打開且與某終端相關
-N file:文件自上一次被讀取之后是否被修改過
-O file:當前有效用戶是否為文件屬組
-G file: 當前有效用戶是否為文件屬組
雙目測試
file1 -ef file2 :file1與file2是否指向同一個設備上的相同inode
file1 -nt file2: file1 是否新于file;
file1 -ot file2: file1 是否舊于file; ;
組合測試條件
第一種:
command1$$command2 并且
command1||command2 或者
!command 非
例如[[ -e file ]] &&[[ -r file ]]
第二種:
expression1 -a expression2 并且
expression1 -o expression2 或者
!expression
必須使用測試命令進行
關于位置變量總結
1.位置變量中$@和$*的區別
當$@和$*不使用“”包起來時效果一樣
當$@和$*用“”包起來時,執行$@正常運行
但是執行$*時報錯顯示的是沒有“a b”這個文件后目錄,
說明使用“”時,$*是把所有參數當作一個字符串的
$@則是每個參數都是分開的
$@和$*換個順序看看有影響沒
結果一樣,所以上面結論成立
上課的時候老師好像不是這樣驗證的,我嘗試下老師的方法
首先我創建了三個腳本,內容如下分別是t2.sh,t3.sh,t1.sh
其中t1.sh是t2.sh,t3.sh子腳本(后面簡稱腳本1.2.3)
我們先看下運行三個腳本結果如何
運行腳本1時,1.2.4命令正常執行,說明命令和參數都沒有問題,從其結果看也是正確的
命令3執行時顯示的的是“a b c”這個文件,這是把a b c當作的是一個參數,和上面的結論一樣的
這樣說明了$*是把多個參數當作一個字符串的
運行腳本3時,命令1.2正常執行,且結果正確,然后執行子腳本t1.sh參數是$*
但是t1第一行顯示的a b c,t2第二行命令沒顯示,說明第二行沒參數,所以沒顯示
t1第三四行顯示正常結果,再次證明t1.sh運行時只有一個參數“a b c”
在運行腳本2
所有參數都是正常執行,
在強調一下結論:
在被雙引號包起來時 $*傳遞給腳本的所有參數,全部參數合為一個字符串,變成一個參數
$@不受影響,每個參數依然是獨立的
$#:傳遞到腳本的參數個數
$0:命令本身
$$:腳本運行的當前進程ID號
$!:后臺運行的最后一個進程的進程ID號
$?:顯示最后命令的退出狀態結果
0:成功
1-255:失敗
練習
1、編寫腳本/root/bin/systeminfo.sh,顯示當前主機系統信息,包括主機名,IPv4地址,操作系統版本,內核版本,CPU型號,內存大小,硬盤大小。
腳本內容如下:
1 #!/bin/bash 2 #The current host system information 3 echo -e "hello,everyone, \nThe current host system information is as follows" 4 echo -e " " 5 echo "hostname :`hostname`" 6 echo "" 7 echo "ip:`ifconfig |head -n2|tail -n1|tr " " ":"|tr -s ":"|cut -d: -f3`" 8 echo "" 9 echo -e "system version:`cat /proc/version`" 10 echo "" 11 echo "kenel version:`uname -r`" 12 echo "" 13 echo -e "CPU Model:`lscpu |grep "Model name"|cut -d: -f2|sed 's@^[[:space:]]\+@@'`" 14 echo "" 15 echo -e "the memory capacity :`free -h|tr " " ":"|tr -s ":"|head -2|tail -1|cut -d: -f2`" 16 echo "" 17 echo -e "hard drive size:`fdisk -l |head -2|tail -1|cut -d: -f2|cut -d, -f1`"
運行結果如下:
2、編寫腳本/root/bin/backup.sh,可實現每日將/etc/目錄備份到/root/etcYYYY-mm-dd中
腳本內容如下
1 #!/bin/bash 2 #discription:wen jian bei fen 3 cp -a /etc/ /root/etc`date +%Y-%m-%d`
因為暫時還沒學到每日實現,只能做到手動備份,結果如下
3、編寫腳本/root/bin/disk.sh,顯示當前硬盤分區中空間利用率最大的值
指令如下
1 #!/bin/bash 2 #ci pan li yong lv 3 df|grep "sda"|tr " " ":"|tr -s ":"|cut -d: -f5|sort -rn|head -n1|tr -d "%"
結果:
4、編寫腳本/root/bin/links.sh,顯示正連接本主機的每個遠程主機的IPv4地址和連接數,并按連接數從大到小排序
命令如下
1 #!/bin/bash 2 netstat -nt|grep tcp|tr " " ":"|tr -s ":"|cut -d: -f6|sort |uniq -c|sort -rn
5、寫一個腳本/root/bin/sumid.sh,計算/etc/passwd文件中的第10個用戶和第20用戶的ID之和
命令如下:
1 #!/bin/bash 2 user1_id=`cat /etc/passwd |head -10|tail -1|cut -d: -f3` 3 echo "the 10 user_id: $user1_id" 4 user2_id=`cat /etc/passwd |head -20|tail -1|cut -d: -f3` 5 echo "the 20 user_id: $user2_id" 6 sumid=$[$user1_id+$user2_id] 7 echo "the sumid:$sumid"
結果:
6、寫一個腳本/root/bin/sumspace.sh,傳遞兩個文件路徑作為參數給腳本,計算這兩個文件中所有空白行之和
空行和空白行表示的應該是不一樣的
空行:^$
空白行:^[[:space:]]*$space
指令1如下
1 #!/bin/bash 2 num1=`cat /etc/rc.d/init.d/functions |grep "^[[:space:]]*$"|wc -l` 3 echo "the 1st is: $num1" 4 num2=`cat /etc/fstab |grep "^[[:space:]]*$"|wc -l` 5 echo "the 2st is: $num2" 6 sumspace=$[$num1+$num2] 7 echo "the sum is: $sumspace"
指令2如下
1 #!/bin/bash 2 num1=`cat /etc/rc.d/init.d/functions |grep "^$"|wc -l` 3 echo "the 1st is: $num1" 4 num2=`cat /etc/fstab |grep "^$"|wc -l` 5 echo "the 2st is: $num2" 6 sumspace=$[$num1+$num2] 7 echo "the sum is: $sumspace"
7、寫一個腳本/root/bin/sumfile.sh,統計/etc, /var, /usr目錄中共有多少個一級子目錄和文件
命令:
1 #!/bin/bash 2 num1=`ls -A /etc|wc -l` 3 echo "etc: $num1" 4 num2=`ls -A /var|wc -l` 5 echo "var: $num2" 6 num3=`ls -A /usr|wc -l` 7 echo "usr :$num3" 8 sumfile=$[$num1+$num2+$num3] 9 echo "the sumfile: $sumfile"
8、寫一個腳本/root/bin/argsnum.sh,接受一個文件路徑作為參數;如果參數個數小于1,則提示用戶“至少應該給一個參數”,并立即退出;如果參數個數不小于1,則顯示第一個參數所指向的文件中的空白行數
命令:
1 #!/bin/bash 2 num1=1 3 [[ $# -lt $num1 ]] &&( echo "zhishaoyinggai geiyige canshu" ;exit)|| grep "^$" $1|wc -l
9、chmod -rw /tmp/file1,編寫腳本/root/bin/per.sh,判斷當前用戶對/tmp/fiile1文件是否不可讀且不可寫
命令
1 #!/bin/bash 2 [ -r /tmp/file1 -o -w /tmp/file1 ]&&echo 讀寫至少一個 ||echo 不可讀且不可寫
10、編寫腳本/root/bin/nologin.sh和login.sh,實現禁止和充許普通用戶登錄系統。
說明下,當/etc/nologin文件存在時,用戶是禁止登錄的,知道了這個上面的就簡單了
禁止登錄
1 #!/bin/bash 2 [ -f /etc/nologin ] && echo "user cannot login" || (touch /etc/nologin; echo "user cannot login")
允許登錄
1 #!/bin/bash 2 [ -f /etc/nologin ]&& (rm -rf /etc/nologin ; echo "user can login") ||echo "user already login"
11、寫一個腳本/root/bin/hostping.sh,接受一個主機的IPv4地址做為參數,先判斷是否合格IP,否,提示IP格式不合法并退出,是,測試是否可連通。如果能ping通,則提示用戶“該IP地址可訪問”;如果不可ping通,則提示用戶“該IP地址不可訪問”
命令:
1 #!/bin/bash 2 ping -c1 -w1 $1 &>/dev/null &&echo "the ip can access" ||echo "the ip cannot access"
12、計算1+2+3+…+100的值
1 #!/bin/bash 2 echo the sumnumis: `echo {1..100}|tr " " "+"|bc`
13、計算從腳本第一參數A開始,到第二個參數B的所有數字的總和,判斷B是否大于A,否提示錯誤并退出,是則計算之
1 #!/bin/bash 2 [[ $1 -lt $2 ]] && seq -s+ $1 $2|bc || (echo "error" ;exit)
原創文章,作者:qiuwei,如若轉載,請注明出處:http://www.www58058.com/33529
總結的很好很詳細,態度認真,內容充實,排版精美,再接再厲。