Shell腳本基礎入門
一、shell腳本介紹
1.1 開頭(環境使用shebang機制)
#!/bin/bash 必須寫在文件首行
符號#!用來告訴系統它後面的參數是用來執行該文件的程序。
當編輯好腳本時,如果要執行該腳本,還必須使其可執行。
要使腳本可執行:
編譯 chmod +x filename 這樣才能用./filename或source filename 來運行。
1.2 註釋
在進行shell編程時,以#開頭的句子表示註釋,直到這一行的結束。我建議在程序中使用註釋。如果使用了註釋,那麽即使相當長的時間內沒有使用該腳本,我們也能在很短的時間內明白該腳本的作用及工作原理。
1.3格式
腳本中如果有語法錯誤可以用來: bash -n /path/to/some_script
調試跟蹤: bash -x /path/to/some_script
第一行的#!/bin/bash是聲明這個腳本使用的shell格式,因為我們使用的是bash,所以必須要以“#!/bin/bash”來告訴別人這個腳本裏的語法使用的是bash語法,這樣它在執行的時候,就能加載bash相關配置文件,使我們的命令能夠更好的執行下去。(如果不加,有可能腳本無法執行,無法判斷腳本是使用的什麽shell。當然如果默認選擇bash格式,可以不寫,但最好還是腳本第一行寫上shebang機制,還有就是創建文本的時候,後綴加上.sh為結尾。
二、變量
2.1 變量定義
所有的變量都由字符串組成,用一個字符串,替代更多更復雜的內容 ,並且您不需要對變量進行聲明。在變量使用命令時需要加` ` 或者$( ).
賦值給一個變量,並打印出內容如下:
有時候變量名很容易與其他文字混淆,比如:
num=2
echo “this is the $numnd”
這並不會打印出“this is the 2nd”,而僅僅打印”this is the “,因為shell會去搜索變量numnd的值,但是這個變量時沒有值的。可以使用花括號來告訴shell我們要打印的是num變量:echo “this is the ${num}nd” 這將打印:this is the 2nd
。註意:變量名外面的花括號是可選的,加不加都行,加花括號是為了幫助解釋器識別變量的邊界
2.2 變量格式
1、必須以字母開頭後面可以使用下劃線,數字
2、中間不能有空格,不能使用標點符號,不能使用程序中的保留字如:if,for。
3、駝峰語法:首個字母小寫,其余開頭字母大寫如 mageJiaoYu
2.3變量分類
本地變量,環境變量,位置變量,特殊變量
本地變量:生效範圍為當前shell進程;對當前shell之外的其它shell進程,包括當前shell的子shell進程均無效。
變量賦值:name=‘value’
可以使用引用value:
(1)可以是直接字串; name=“root”
(2)變量引用:name=”$USER”
(3)命令引用:name=`COMMAND`, name=$(COMMAND)
變量引用:${name}, $name
" ":弱引用,其中的變量引用會被替換為變量值”;
例:[[email protected] ~]# var1=1
[[email protected] ~]# echo "$var1 * ‘‘ \$a\\
1 * $a\
(雙引號比起單引號比較有人情味,也比較聰明,它能識別裏面的變量,不會屏蔽\和$這兩個字符的含義,如果需要屏蔽這些字符含義,除了用單引號外,還可以在前面加個\符號。)
‘ ‘:強引用,其中的變量引用不會被替換為變量值,而保持原字符串;
例:[[email protected] ~]# var1=2
[[email protected] ~]# echo ‘$var1 * \$a\\‘
$var1 * \$a\\
(單引號可以屏蔽全部字符的特殊意義,但是不能表示一個字符:(單引號‘)。由於她沒有轉義字符,所以不能表示她本身。強引用)
顯示已定義的所有變量:set
刪除變量:unset name 腳本運行完畢要釋放變量。
環境變量:生效範圍為當前shell進程及其子進程
變量聲明、賦值:
export name=VALUE
declare -x name=VALUE
變量引用:$name, ${name}
刪除變量:unset name
顯示所有環境變量(環境變量的查詢):
env
printenv
export
declare -x
#env列出環境下所有環境變量與其內容
#set可查看所有的變量(含環境變量與本地變量)
bash內建的環境變量:PATH SHELL UID HOME PWD LANG MAIL HOSTNAME HISTSIZE PS1等
例:[[email protected]centos7 ~ ]# echo $HOME
[root@centos7 ~]# /root
局部變量:生效範圍為當前shell進程中某代碼片斷(通常指函數)
位置變量:$1, $2, …來表示,用於讓腳本在腳本代碼中調用通過命令行傳遞給它的參數。
$1, $2, …:對應第1、第2等參數,shift [n]換位置。當位置參數遇到10及10以上的要用{ };例:echo 10st 結果為arg is {10}。
$0:命令本身,是用於取腳本本身的名字
[[email protected]centos7 tmp]# vim file1
#!/bin/bash
dirname $0
basename $0
[[email protected]centos7 tmp]# bash ./file1
.
file1
[[email protected]centos7 tmp]# bash /tmp/file1
/tmp
file1
$#:傳遞給腳本的參數的個數,一般用於控制參數個數。
[[email protected]centos7 tmp]#vim file2
#!/bin/bash
echo $#
[[email protected]centos7 tmp]# bash file2 123
3
[[email protected]centos7 tmp]# bash file2 123456
6
$?:判斷上個命令腳本,函數是否執行成功,命令執行的返回值,0表示沒有錯誤,其他表示有錯誤,代表上個命令執行失敗
[[email protected]centos7 tmp] # ls
file1 file2
[[email protected]centos7 tmp] # echo $?
0
[[email protected]centos7 ~] # lls
bash:lls:command not found...
[[email protected]centos7 ~] # echo $?
1
$*:傳遞給腳本的所有參數,全部參數合為一個字符串
[email protected]:傳遞給腳本的所有參數,每個參數為獨立字符串
[email protected] $*只在被雙引號包起來的時候才會有差異
set —清空所有位置變量
小總結:變量內容若有空格符可使用雙引號" "或者單引號‘ ‘將變量內容結合起來:
a.雙引號內的特殊字符如$等,可以保持原本的特性,如:
b.單引號內的特殊字符則僅為一般字符(純文本),如:
c.可用轉義字符“\”將特殊符號(如$、\、!)變成一般字符。如
d.在一串命令中,還需要通過其他的命令提供的信息,可以使用反單引號“`命令`”或者“$(命令)”,如:
e.若該變量為了增加變量內容時,則可用“$變量名稱”或${變量}累加內容,如:給變量PATH增加內容,不能直接用”PATH=內容“,這樣會覆蓋掉原本的變量值,應該用“PATH=$PATH:內容”.
f.若該變量需要在其他子進程(子進程:在目前這個shell的情況下,去打開另一個新的shell,新的shell就是子進程)執行,則需要以export來使變量變成環境變量。如:
g.取消變量的方法為使用“unset變量名稱”,如:
三、bash的配置文件
3.1按生效範圍劃分,存在兩類:
全局配置:/etc/profile /etc/profile.d/*.sh /etc/bashrc
個人配置:~/.bash_profile ~/.bashrc
3.2按功能劃分,存在兩類:
profile類和bashrc類
profile類:為交互式登錄的shell提供配置
全局:/etc/profile, /etc/profile.d/*.sh
個人:~/.bash_profile
功用:(1)用於定義環境變量 (2)運行命令或腳本
bashrc類:為非交互式和交互式登錄的shell提供配置
全局:/etc/bashrc
個人:~/.bashrc
功用:(1)定義命令別名和函數 (2)定義本地變量
3.3shell登錄兩種方式
交互式登錄:(不能繼承上一個shell)
(1)直接通過終端輸入賬號和密碼登錄
(2)使用“su -l UserName 或su - UserName”切換的用戶
執行順序:/etc/profile –> /etc/profile.d/*.sh –> ~/.bash_profile–> ~/.bashrc–> /etc/bashrc
非交互式登錄:(繼承上一個shell)
(1)su UserName
(2)圖形界面下打開的終端,執行腳本
執行順序:~/.bashrc–> /etc/bashrc–> /etc/profile.d/*.sh
例子:在這幾個文件分別設置環境變量A,B,C,D,E
沒有重新登陸時,這幾個變量就不會生效,echo $A $B $C $D $E,什麽都不顯示。重新登陸後,這幾個變量就會生效。
用非交互式登陸guanyu用戶,就會繼承上一個shell,所以echo 5個變量都會顯示。而用交互式登陸guanyu用戶,不能繼承上一個shell,就只能讀取/etc/profile,/etc/profile.d/*.sh,~/.bash_profile,~/.bashrc,/etc/bashrc這幾個文件,而變量C,D都是在用戶root的家目錄設置的,只能讀取到變量A,B,E,所以echo這幾個變量只顯示變量A,B,E的值。
在root用戶下,將變量A的值改為A6,變量B的值改為B7,變量D的值改為D9,變量E的值改為E10
然後不退出重新登陸,直接echo這幾個變量,當然不變,因為這幾個變量沒有生效,此時用非交互式登陸切到guanyu用戶下,變量A的值不變,盡管,變量A的值變為A6,但是用非交互式登陸,繼承了上一個shell變量A的值A1,但是不讀取/etc/profile這個文件,所以不更新變量A的值,變量B的值變為B7,是因為繼承了上一個shell變量B的值然後讀取/etc/profile.d/mage.sh這個文件,更新變量B的值。變量C3的值不變,變量D的值為D4,是因為繼承了上一個shell的變量D的值D4,又因為變量D在root用戶的家目錄裏,所以不讀取,不更新變量D的值。變量E5的值為E10,是因為繼承了上一個shell的變量D的值,又讀取/etc/bashrc所以更新變量E的值,所以變量E的值為E10.
在用交互式登陸guanyu用戶,echo這幾個變量,不繼承上一個shell,只讀取文件,因為是交互式登陸,所以/etc/profile,/etc/profile.d/mage.sh,/etc/bashrc這幾個文件都能讀取,所以變量A,B,E的值為A6,B7,E10,又因為變量C,D都在root用戶的家目錄裏,所以不能讀取,變量C,D的值為空。
3.4 read
變量的定義—read讀入 默認存儲位置$REPLY
如果傳的參數多於賦值,最後一個值會全部接收
–p增加提示
[[email protected] ~]# read -p "輸入密碼" e (必須有空格)
輸入密碼123456
[[email protected] ~]# echo $e
123456
–s 隱藏輸入
[[email protected] ~]# read -s -p "輸入密碼" e (將密碼隱藏)
輸入密碼
[[email protected] ~]# echo $e
123456
-t 設置超時時間
[[email protected] ~]# read -t5 -p "輸入密碼" e (表示5秒後直接退出當前操作)
輸入密碼[[email protected]~]#
四、邏輯判斷語句
4.1條件判斷
[[email protected] ~]# [ -f /etc/hosts ] 判斷是否是文件
[[email protected]~]# echo $?
0
4.2 && ||
&&表示前面執行成功,執行後面.
||表示前面執行成功,不執行後面,前面執行失敗,執行後面
[[email protected] ~]# [ -f /etc/hosts ]&& echo "文件存在" || echo "文件不存在" 相當於返回值0和1
文件存在
[[email protected] ~]# [ -f /etc/host ]&& echo "文件存在" || echo "文件不存在"
文件不存在
4.3 測試文件符號(test)
4.4 測試字符串
特別註意:字符串必須要用雙引號引起來。字符串比較,比較符號兩端必須有空格。
4.5測試大小
[[email protected] ~]# [ 1 -eq 1 ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ 1 -eq 2 ] && echo "表達式成立" || echo "表達式不成立"
表達式不成立
[[email protected] ~]# [ 1 -ne 2 ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ 1 -gt 2 ] && echo "表達式成立" || echo "表達式不成立"
表達式不成立
[[email protected] ~]# [ 1 -gt 0 ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ 1 -gt 1 ] && echo "表達式成立" || echo "表達式不成立"
表達式不成立
[[email protected] ~]# [ 1 -ge 1 ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ 1 -le 1 ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ 1 -lt 1 ] && echo "表達式成立" || echo "表達式不成立"
表達式不成立
[[email protected] ~]# [ 1 -lt 2 ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ 1 = 1 ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ 1 = 2 ] && echo "表達式成立" || echo "表達式不成立"
表達式不成立
[[email protected] ~]# [ "1" = "2" ] && echo "表達式成立" || echo "表達式不成立"
表達式不成立
[[email protected] ~]# [ "1" != "2" ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ "1" > "2" ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ "1" \> "2" ] && echo "表達式成立" || echo "表達式不成立"
表達式不成立
[[email protected] ~]# [ "1" \< "2" ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ "1" \<= "2" ] && echo "表達式成立" || echo "表達式不成立"
-bash: [: <=: binary operator expected
表達式不成立
[[email protected] ~]# [ "1" \<\= "2" ] && echo "表達式成立" || echo "表達式不成立"
-bash: [: <=: binary operator expected
表達式不成立
4.6 與或非 -a -o !
-a 前一個條件成立,後一個條件成立,整個表達式才成立,有一個是錯的都不會成立
-o 只要任意一個條件成立,表達式就成立,只有倆個都不成立,才不成立,則表達式不成立
[[email protected] ~]# [ "1" = "1" -a "2" = "2" ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ "1" = "1" -a "2" = "3" ] && echo "表達式成立" || echo "表達式不成立"
表達式不成立
[[email protected] ~]# [ "1" = "1" -o "2" = "3" ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
[[email protected] ~]# [ "1" = "2" -o "2" = "3" ] && echo "表達式成立" || echo "表達式不成立"
表達式不成立
[[email protected] ~]# [ "1" = "1" -a !"2" = "3" ] && echo "表達式成立" || echo "表達式不成立"
-bash: !"2": event not found
[[email protected] ~]# [ "1" = "2" -o ! "2" = "3" ] && echo "表達式成立" || echo "表達式不成立"
表達式成立
五、循環語句
if 語句 單分支語句
語法格式:
如果if [條件語句]
然後 then
執行命令
fi
實例:編寫一個腳本/root/bin/createuser.sh,腳本的執行語法必須是:createuser.sh -u username -m password,選項與參數間可支持多空格,但不能順序顛倒。
當未指定正確的選項或參數時,以錯誤輸出方式提示“createuser.sh -u username -m password ”後退出腳本。
用戶名必須以字母開頭,可包括數字和_。否則不合法。以錯誤輸出提示用戶"用戶名僅包含字母數據和下劃線"
當用戶名檢測合法後,判斷用戶名是否已存在,若存在,再判斷用戶是否已設置過密碼,若設置過密碼,直接退出,未設置,則將密碼設置為所指定的密碼後以正確輸出方式顯示“username 密碼已更新後退出”
當用戶名不存在,則創建用戶,並為該用戶設置所指定的密碼後以正確輸出方式顯示“用戶username已創建並更新密碼”
要求腳本執行過程中不能有非要求的其他輸出結果出現。腳本在非正確方式退出時應反回給?參數非0值。
#判斷用戶名是否規範,並且給出返回值
decide=`(echo $2|grep "^[[:alpha:]]\([[:alpha:]]\|[[:digit:]]\|_\)*$" &>/dev/null;echo $?)`
#判斷用戶是否存在,並給出返回值
id=`id $2 &>/dev/null;echo $?`
#截取用戶密碼位
mima=`getent shadow $2 |cut -d: -f 2`
#判斷第一個參數
if [ "$1" != "-u" ];then
echo "createuser.sh -u username -m password"
exit 1
fi
#判斷第三個參數
if [ "$3" != "-m" ];then
echo "createuser.sh -u username -m password"
exit 1
fi
#判斷參數個數
if [ $# -ne 4 ];then
echo " createuser.sh -u username -m password"
exit 1
fi
#判斷第二個參數
if [ $decide -ne 0 ];then
echo "用戶僅包含字母數字和下劃線"
exit 1
else
if [ $id -eq 0 ];then
if [ "$mima" == "!!" -o "$mima" == "" ];then
`echo "$4" |passwd --stdin $2 &>/dev/null`
echo "$2密碼已更新"
exit 1
else
exit 1
fi
else
`useradd $2`
`echo $4 |passwd --stdin $2 &>/dev/null`
echo "用戶$2已創建並更新密碼"
fi
fi
#刪除變量
unset decide
unset id
unset mima
解析:先定義變量decide,id,mima,再對變量進行測試。
練習:
1.寫一個腳本名為jiaozuoyexx.sh 當執行該腳本時如jiaozuoyeXX.sh testXX.sh,就會自動將該testXX.sh傳給教師機,
路徑是scp testXX.sh [email protected]:~/scripts 密碼為mage26
1、vim jiaozuoye.sh
#!/bin/bash
--------------------------
# Filename:
# Revision:
# Date:
# Author:
# Email:
# Website:
# Description:
# ------------------------------------------
scp $1 [email protected]:~/scripts
2、chmod +x createshXX.sh 添加權限
3、./jiaozuoyeXX.sh jiaozuoyeXX.sh 查看結果
思路:這裏的$1實際是個參數,代表腳本名稱。
2.寫一個能夠創建新腳本的Shell script,如名為createshXX.sh 當執行時createsh /root/bin/test1.sh
則會自動創建並打開/root/bin/test1.sh,且其中包含以下內容。
1、vim createshXX.sh
2、#!/bin/bash
# ------------------------------------------
# Filename:
# Revision:
# Date:
# Author:
# Email:
# Website:
# Description:
# ------------------------------------------
echo "#!/bin/bash
# ------------------------------------------
# Filename:
# Revision:
# Date:
# Author:
# Email:
# Website:
# Description:
# ------------------------------------------
" >>$1
chmod +x createshXX.sh
vim $1
3、修改腳本要進入vim createshXX.sh
4、./jiaozuoyeXX.sh createshXX.sh
此處>>表示追加
3、編寫腳本/root/bin/sumid.sh,計算/etc/passwd文件中的第10個用戶和第20用戶的ID之和
1、./createsh36.sh(相當於一個命令) /root/bin/sumid.sh 進入編輯界面
2、#!/bin/bash
#--------------------------------------------------
# Filename:/root/bin/sumid.sh
# Version:1.0
# Date:‘date "+%F %T"‘
# Author:Lemon.
# Description:ID之和
#--------------------------------------------------
echo "$[$(cat /etc/passwd|head -10|tail -1|cut -d: -f3)+$(cat /etc/passwd|head -20|tail -1|cut -d: -f3)]"
chmod +x /root/bin/sumid.sh
4、cd /root/bin/ 需要進入腳本目錄才能查看結果
5、./sumid.sh 查看文件結果
或uid=10 uid=20
#!/bin/bash
Uid1=`head -$1 /etc/passwd|tail -1|cut -d: -f3`
Uid2=`head -$2 /etc/passwd|tail -1|cut -d: -f3`
Sumid=$[Uid1+Uid2]
echo "Sumid is $Sumid"
unset Uid1 Uid2 Sumid
分析:分別表示出第10個用戶和第20個用戶
4、編寫腳本/root/bin/sumfile.sh,統計/etc, /var, /usr目錄中共有多少個一級子目錄和文件
1、./createsh36.sh sumfile.sh
2、#!/bin/bash
file1=`ls -A /etc|wc -l`
file2=`ls -A /var|wc -l`
file3=`ls -A /usr|wc -l`
let sum=$file1+$file2+$file3
echo "$sum"
unset file1 file2 file3 sum
chmod +x sumfile.sh
3、 ./sumspace.sh /etc /var /usr
註意:要想引用一串命令需要用` `和$( )引用。
5、編寫腳本/root/bin/checkdisk.sh,檢查磁盤分區空間和inode使用率,如果超過80%,就發廣播警告空間將滿
diskused_max=`df | grep "/dev/sd"|sort -nr -k5|head -1|tr -s ‘ ‘ %|cut -d% -f5`
inodeused_max=`df -i| grep "/dev/sd"|sort -nr -k5|head -1|tr -s ‘ ‘ %|cut -d% -f5`
[ "$diskused_max" -gt "80" ] && wall "空間即將滿"||echo "空間使用率不超過80%"
[ "$inodeused_max" -gt "80" ] && wall "inode即將滿"||echo "inode使用率不超過80%"
unset diskused_max inodeused_max
解釋:搜索到磁盤分區中的分區/dev/sd,sort -nr -k5表示取第5列然後按數字從大到小排列,tr -s ‘ ‘ %將空格替換成%,再剪切,隨後和80比較。
-----------------------答案2---------------------------------------------------
dev=`df|grep "/dev/sd"|egrep -o "[0-9]{1,3}%"|sort -n|tail -n 1|cut -d% -f1`
ino=`df -i|egrep -o "[0-9]{1,3}%" |sort -n|tail -n 1|cut -d% -f1`
[[ "$dev" -gt 80 ]] || [[ "$ino" -gt 80 ]] && echo $(wall 磁盤已滿)||echo 還有很多利用空
間呦
unset dev
unset ino
解釋:egrep -o 只顯示被模式匹配的字串,[0-9]{1,3}%表示在空間使用率這裏前面的字符匹配至少1次,最多3次,隨後剪切,排序。這裏的比較用或表示,只要有一個滿了,另一個不用比較,就能發出警報。
Shell腳本基礎入門