一、shell script概念
可以將shell終端解釋器作為人與計算機硬件之間的“翻譯官”,作為用戶與Linux系統內部的通信媒介。
shell腳本命令的工作方式:
1.交互式(Interactive):用戶每輸入一條命令就立刻執行。
2.批處理(Batch):由用戶事先編寫好一個完整的shell腳本,腳本會一次性執行完所有的命令。
在shell script撰寫中的注意事項:
1.命令的執行是從上而下、從左而右進行的。
2.命令、選項與參數間的多個空格都會被忽略掉。
3.空白行也將被忽略掉,並且按“Tab”鍵所生成的空白同樣被視為空格鍵。
4.如果讀取到一個Enter符號(CR),就嘗試開始運行該行(或該串)命令。
5.如果一行的內容太多,則可以使用“[Enter]”來延伸至下一行。
6.“#”可作為注解。任何加在 # 后面的數據將全部被視為注解文字而被忽略
配置vscode shell編程環境詳細方法:
https://blog.csdn.net/zz153417230/article/details/103176747
查看當前系統支持哪些版本的shell
[root@localhost ~]# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
Linux默認的shell是GNU bash(Bourne Again shell).
默認的Bash提示符為美元符號$。
二、基本語法
1、首行宣告語法
第一行要使用 #!/bin/bash
宣告這個shell腳本使用的shell名稱。
宣告后,當這個程序被運行時,就能加載 bash 相關環境配置文件。
如果沒有配置,該程序可能因為系統無法判斷該程序需要什么shell而會無法運行。
2、注釋語法
整個script語句中,除了第一行 #!
是用來聲明shell之外,其他的 #
都是注釋。
注意:一定要養成注釋說明腳本內容、功能、作者、聯系方式等的習慣。這樣有助於未來程序的改寫和調試。
3、變量輸入語法
可以使用read命令撰寫腳本,由執行用戶輸入對應信息。
#!/bin/bash
# Program:
# User inputs his first name and last name.
# Program shows his full name.
# History:
# 2021/11/17 hqs First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input your first name:" firstname # 提示輸入用戶名
read -p "Please input your last name:" lastname
echo -e "\nYour full name is : $firstname $lastname"
4、利用date進行文件創建
基於date實現將每天的數據都備份成不同的文件名,這樣可以讓舊的數據也被保存下來而不被覆蓋。
#!/bin/bash
# 讓使用者輸入文件名稱,並獲取用戶名變量
echo -e "I will use 'touch' command to create 3 files. "
read -p "please input your filename: " fileuser
echo -e "當前輸入的用戶名:$fileuser"
# 為了避免用戶隨意按enter,利用變量功能分析文件名是否設置
filename=${fileuser:-"filename"}
echo -e "當前已接受的文件名:$filename"
# 開始利用date命令來取得所需要的文件名
date1=$(date --date='2 days ago' +%Y%m%d-%H:%M:%S) # 前兩天的日期,注意+號前有空格
date2=$(date --date='1 days ago' +%Y%m%d-%H:%M:%S) # 前一天的日期,注意+號前有空格
date3=$(date +%Y%m%d-%H:%M:%S) # 今天的日期
# 配置文件名
file1=${filename}${date1}
file2=${filename}${date2}
file3=${filename}${date3}
# 創建文件
touch "$file1"
echo -e "已經創建 $file1"
touch "$file2"
echo -e "已經創建 $file2"
touch "$file3"
echo -e "已經創建 $file3"
5、數值運算
可以使用declare來定義變量的類型。
利用 $(計算式)
來進行數值運算。
注意:bash shell 系統默認只支持到整數。
#!/bin/bash
# Program:
# User inputs 2 integer numbers ;program will cross these two numbers.
# History:
# 2021/11/24 hqs First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "You should input 2 numbers,i will cross them! \n"
read -p "please input first number: " firstnu
read -p "please input second number: " secondnu
result1=$(($firstnu*$secondnu))
result2=$(($firstnu/$secondnu))
result3=$(($firstnu+$secondnu))
result4=$(($firstnu-$secondnu))
result5=$(($firstnu%$secondnu))
# 乘
echo -e "\nThe result of $firstnu * $secondnu is ==> $result1"
# 除
echo -e "\nThe result of $firstnu / $secondnu is ==> $result2"
# 加
echo -e "\nThe result of $firstnu + $secondnu is ==> $result3"
# 減
echo -e "\nThe result of $firstnu - $secondnu is ==> $result4"
# 取余
echo -e "\nThe result of $firstnu % $secondnu is ==> $result5"
注意:不支持小數,10/100=0。
在數值運算上,還可以使用 declare -i total=$firstnu*$secondnu
,也可以使用$((計算式))
,更建議使用后者。
6、腳本運行對bash環境影響
(1)直接運行的方式運行腳本
直接命令或bash或sh來執行腳本時,腳本會使用一個新的bash環境來運行腳本內的命令。
即:腳本是在子程序的bash內運行的,並當子程序完成后,在子程序內的各項變量或動作將結束而不會傳回父程序。
[root@localhost ~]# sh sh04.sh
You should input 2 numbers,i will cross them!
please input first number: 100
please input second number: 10
The result of 100 * 10 is ==> 1000
The result of 100 / 10 is ==> 10
The result of 100 + 10 is ==> 110
The result of 100 - 10 is ==> 90
The result of 100 % 10 is ==> 0
[root@localhost ~]# echo $firstnu
可以看到腳本運行完畢時,子程序bash內的所有數據便被移除。
(2)利用source運行腳本
腳本會在父程序運行,各項操作都會在原來的bash中生效。
[root@localhost ~]# source sh04.sh
You should input 2 numbers,i will cross them!
please input first number: 100
please input second number: 10
The result of 100 * 10 is ==> 1000
The result of 100 / 10 is ==> 10
The result of 100 + 10 is ==> 110
The result of 100 - 10 is ==> 90
The result of 100 % 10 is ==> 0
[root@localhost ~]# echo $firstnu
100
[root@localhost ~]# echo $result1
1000
[root@localhost ~]# echo $result2
10
[root@localhost ~]# echo $result3
110
[root@localhost ~]# echo $result4
90
三、判斷式——test命令和判斷符號[]
運行結果並不會顯示任何信息,但最后我們可以通過 $? 或 && 及|| 來顯示整個結果
1、關於某文件名的“文件類型判斷”
-e
該文件名是否存在
# 用$?來檢查結果
[root@localhost ~]# test -e /root
[root@localhost ~]# echo $?
0
[root@localhost ~]# test -e /dmsai
[root@localhost ~]# echo $?
1
# &&和||來顯示整個結果
[root@localhost /]# test -e /media && echo yes || echo no
yes
[root@localhost /]# test -e /media111 && echo yes || echo no
no
-f
該文件名是否存在且為文件
[root@localhost /]# test -f /media && echo yes || echo no
no
[root@localhost /]# test -f /root/sh03.sh && echo yes || echo no
yes
-d
該文件名是否存在且為目錄
[root@localhost /]# test -d /root/sh03.sh && echo yes || echo no
no
[root@localhost /]# test -d /media && echo yes || echo no
yes
-b
該文件名是否存在且為block device 設備
-c
該文件名是否存在且為character device設備
-S
該文件名是否存在且為socket文件
-p
該文件名是否存在且為FIFO文件
-L
該文件名是否存在且為一個連接文檔
2、關於某文件的權限檢測
-rwx
檢測文件名是否存在和具有的權限
注意:root權限常有例外
[hqs@localhost ~]$ chmod 777 example.sh
[hqs@localhost ~]$ chmod 100 example1.sh
[hqs@localhost ~]$ chmod 200 example2.sh
[hqs@localhost ~]$ chmod 400 example3.sh
[hqs@localhost ~]$ ll
total 16
---x------ 1 hqs hqs 71 Nov 30 02:22 example1.sh
--w------- 1 hqs hqs 71 Nov 30 02:22 example2.sh
-r-------- 1 hqs hqs 71 Nov 30 02:23 example3.sh
-rwxrwxrwx 1 hqs hqs 71 Nov 17 23:58 example.sh
# 檢查是否具有可讀權限
[hqs@localhost ~]$ test -r example.sh && echo yes || echo no
yes
[hqs@localhost ~]$ test -r example1.sh && echo yes || echo no
no
[hqs@localhost ~]$ test -r example2.sh && echo yes || echo no
no
[hqs@localhost ~]$ test -r example3.sh && echo yes || echo no
yes
# 檢查是否具有可寫權限
[hqs@localhost ~]$ test -w example.sh && echo yes || echo no
yes
[hqs@localhost ~]$ test -w example1.sh && echo yes || echo no
no
[hqs@localhost ~]$ test -w example2.sh && echo yes || echo no
yes
[hqs@localhost ~]$ test -w example3.sh && echo yes || echo no
no
# 檢查是否具有可執行權限
[hqs@localhost ~]$ test -x example.sh && echo yes || echo no
yes
[hqs@localhost ~]$ test -x example1.sh && echo yes || echo no
yes
[hqs@localhost ~]$ test -x example2.sh && echo yes || echo no
no
[hqs@localhost ~]$ test -x example3.sh && echo yes || echo no
no
-s
檢查文件名是否存在且為非空白文件
-ugk
檢查屬性
# 檢查文件名是否存在且具有SUID屬性
# 檢查文件名是否存在且具有SGID屬性
# 檢查文件名是否存在且具有Sticky bit屬性
3、兩個整數間的判定
-eq
兩個數值相等
#!/bin/bash
echo -e "輸入兩個數值進行判定!"
read -p "first number n1:" n1
read -p "second number n2:" n2
test $n1 -eq $n2 && echo "true,$n1和$n2相等" || echo "false,$n1和$n2不相等"
-ne
兩個數值不相等
#!/bin/bash
echo -e "輸入兩個數值進行判定!"
read -p "first number n1:" n1
read -p "second number n2:" n2
test $n1 -ne $n2 && echo "true,$n1和$n2不相等" || echo "false,$n1和$n2相等"
-gt
大於
#!/bin/bash
echo -e "輸入兩個數值進行判定!"
read -p "first number n1:" n1
read -p "second number n2:" n2
test $n1 -gt $n2 && echo "true,$n1大於$n2" || echo "false,$n1不大於$n2"
-lt
小於
#!/bin/bash
echo -e "輸入兩個數值進行判定!"
read -p "first number n1:" n1
read -p "second number n2:" n2
test $n1 -lt $n2 && echo "true,$n1小於$n2" || echo "false,$n1不小於$n2"
-ge
大於等於
#!/bin/bash
echo -e "輸入兩個數值進行判定!"
read -p "first number n1:" n1
read -p "second number n2:" n2
test $n1 -ge $n2 && echo "true,$n1大於等於$n2" || echo "false,$n1小於$n2"
-le
小於等於
#!/bin/bash
echo -e "輸入兩個數值進行判定!"
read -p "first number n1:" n1
read -p "second number n2:" n2
test $n1 -le $n2 && echo "true,$n1小於等於<=$n2" || echo "false,$n1大於$n2"
4、字符串數據判定
-z
判定字符串是否為空
若字符串為空字符串,則為true。
[root@localhost ~]# test -z 'asdasdasd' && echo "true,為空字符串" || echo "false,非空字符串"
false,非空字符串
[root@localhost ~]# test -z '' && echo "true,為空字符串" || echo "false,非空字符串"
true,為空字符串
-n
判定字符串是否不為空
若字符串為非空字符串,則為true.若字符串為空字符串,則為false。
[root@localhost ~]# test -n '' && echo "true,為非空字符串" || echo "false,空字符串"
false,空字符串
[root@localhost ~]# test -n 'kobe' && echo "true,為非空字符串" || echo "false,空字符串"
true,為非空字符串
=
判定兩個字符串是否相等
若相等,則回傳true.
注意:等號兩邊要有空格。
[root@localhost ~]# test 'kobe'='james' && echo "true,相等字符串" || echo "false,不相等字符串"
true,相等字符串
[root@localhost ~]# test 'kobe' = 'james' && echo "true,相等字符串" || echo "false,不相等字符串"
false,不相等字符串
[root@localhost ~]# test 'kobe' = 'kobe' && echo "true,相等字符串" || echo "false,不相等字符串"
true,相等字符串
!=
判定兩個字符串是否不相等
若相等,則回傳false.
[root@localhost ~]# test 'kobe' != 'kobe' && echo "true,不相等字符串" || echo "false,相等字符串"
false,相等字符串
[root@localhost ~]# test 'kobe' != 'curry' && echo "true,不相等字符串" || echo "false,相等字符串"
true,不相等字符串
5、判斷符號[]
除了使用test之外,其實,我們還可以利用判斷符號“[]”(就是中括號)來進行數據的判斷。
書寫要點:
在中括號 [] 內的每個組件都需要有空格鍵來分隔。
在中括號內的變量,最好都以雙引號括起來。
在中括號內的常數,最好都以單或雙引號括起來。
[root@localhost ~]# test -z $HOME && echo yes || echo no
no
[root@localhost ~]# [ -z "$HOME" ]; echo $?
1
[root@localhost ~]# [ "$HOME" == "$MAIL" ]; echo $?
1
[root@RHEL7-2 scripts]# vim sh06.sh
#!/bin/bash
# Program:
# This program shows the user's choice
# History:
# 2018/08/25 Bobby First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input (Y/N): " yn
[ "$yn" == "Y" -o "$yn" == "y" ] && echo "OK, continue" && exit 0
[ "$yn" == "N" -o "$yn" == "n" ] && echo "Oh, interrupt!" && exit 0
echo "I don't know what your choice is" && exit 0
三、條件判斷式
簡單的條件判斷可以使用 &&和||實現。要實現更多功能需要使用if...then。
1、if簡單條件判斷
# 語法格式:
if [條件判斷式]; then
條件成立,命令內容
fi
# 案例:
#!/bin/bash
if [ -n "$HOME" ]; then
echo -e '當前的home目錄:' $HOME
fi
2、if...else多重判斷
# 語法格式:
if [條件判斷式]; then
條件成立,命令內容
else
條件不成立,命令內容
fi
# 案例:
#!/bin/bash
read -p "please input home path:" my_home
if [ "$my_home" = "$HOME" ]; then
echo -e 'home path input success:' $my_home
else
echo -e 'home path input error' $my_home
fi
3、if...elif...else語法格式
# 語法格式:
if [條件判斷式一]; then
條件一成立,命令內容
elif [條件判斷式二]; then
條件二成立,命令內容
else
條件一、二均不成立,命令內容
fi
# 案例
#!/bin/bash
read -p "Please input (Y/N): " yn
if [ "$yn" == "Y" -o "$yn" == "y" ]; then
echo "OK, continue" && exit 0
elif [ "$yn" == "N" -o "$yn" == "n" ]; then
echo "NO, interrupt!" && exit 0
else
echo "I don't know what your choice is" && exit 0
fi
4、case...in...esac判斷
case ... esac 為多選擇語句,與其他語言中的 switch ... case 語句類似,是一種多分枝選擇結構。
- 每個
case
分支用右圓括號開始; - 用兩個分號
;;
表示 break,即執行結束,跳出整個 case ... esac 語句 esac
(就是 case 反過來)作為結束標記。- 可以用 case 語句匹配一個值與一個模式,如果匹配成功,執行相匹配的命令。
語法格式:
case $變量名稱in # 關鍵字為case,變量前有 $ 符
"第一個變量內容") # 每個變量內容建議用雙引號括起來,關鍵字則為小括號 )
程序段
;; # 每個類別結尾使用兩個連續的分號來處理
"第二個變量內容")
程序段
;;
*) # 最后一個變量內容都會用 * 來代表所有其他值
不包含第一個變量內容與第二個變量內容的其他程序運行段
exit 1
;;
esac # 最終的case結尾!case反過來寫
案例:
#!/bin/bash
echo '輸入 1 到 4 之間的數字:'
echo '你輸入的數字為:'
read aNum
case $aNum in
1) echo '你選擇了 1'
;;
2) echo '你選擇了 2'
;;
3) echo '你選擇了 3'
;;
4) echo '你選擇了 4'
;;
*) echo '你沒有輸入 1 到 4 之間的數字'
;;
esac
四、條件循環語句
1、for條件循環語句
for 循環語句允許腳本一次性讀取多個信息,然后逐一對信息進行操作處理,當要處理的數據有范圍時,使用 for 循環語句最為合適。
for循環語句語法格式:
for 變量名 in 取值列表
do
命令序列
done
# bash shell支持C式for循環
for (( 初始值; 限制值; 執行步長 ))
do
程序段
done
# 這種語法適合於數值方式的運算,在for后面括號內的參數的意義如下。
# 初始值:某個變量在循環當中的起始值,直接以類似i=1設置好。
# 限制值:當變量的值在這個限制值的范圍內,就繼續進行循環,例如i<=100。
# 執行步長:每作一次循環時,變量的變化量,例如i=i+1,步長為1。
(1)普通練習
# 練習1:編寫腳本清空所有arp緩存記錄
#!/bin/bash
for i in $(arp | tail -n +2|tr -s ' ' |cut -d' ' -f1)
do
arp -d $i
done
# 練習2:產生十個隨機數
for i in {0..9};do echo $RANDOM;done
# 練習3:倒數五秒
#!/bin/bash
echo "准備倒數5秒:"
for i in $(seq 5 -1 1)
do
echo -en "$i";sleep 1
done
echo -e "開始"
# 練習4:三種動物
#!/bin/bash
for animal in dog cat elephant
do
echo "There are ${animal}s.... "
done
# 練習5:查看/etc/passwd中信息
# 先安裝finger軟件
yum install finger -y
# 腳本如下:
#!/bin/bash
users=$(cut -d ':' -f1 /etc/passwd) # 獲取賬號名稱
for username in $users # 開始循環
do
id $username
finger $username
done
# 練習6:偵察網絡狀態
#!/bin/bash
network="192.168.10" # 先定義一個網絡號(網絡ID)
for sitenu in $(seq 1 100) # seq為sequence(連續) 的縮寫之意
do
# 下面的語句取得ping的回傳值是正確的還是失敗的
ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1
# 開始顯示結果是正確的啟動(UP)還是錯誤的沒有連通(DOWN)
if [ "$result" == 0 ]; then
echo "Server ${network}.${sitenu} is UP."
else
echo "Server ${network}.${sitenu} is DOWN."
fi
done
# 練習7:查看某目錄下文件權限
#!/bin/bash
# 先看看這個目錄是否存在啊
read -p "Please input a directory: " dir
if [ "$dir" == "" -o ! -d "$dir" ]; then
echo "The $dir is NOT exist in your system."
exit 1
fi
# 開始測試文件
filelist=$(ls $dir) # 列出所有在該目錄下的文件名稱
for filename in $filelist
do
perm=""
test -r "$dir/$filename" && perm="$perm readable"
test -w "$dir/$filename" && perm="$perm writable"
test -x "$dir/$filename" && perm="$perm executable"
echo "The file $dir/$filename's permission is $perm "
done
(2)高級案例
案例1:准備用戶名稱列表users.txt,編寫腳本使用read命令讀取用戶輸入得密碼,賦值給passwd變量。
[root@linuxprobe ~]# vim users.txt
andy
barry
carl
duke
eric
george
[root@linuxprobe ~]# vim Example.sh
#!/bin/bash
read -p "Enter The Users Password : " PASSWD
for UNAME in `cat users.txt`
do
id $UNAME &> /dev/null
if [ $? -eq 0 ];then
echo "Already exists"
else
useradd $UNAME &> /dev/null
echo "$PASSWD" | passwd --stdin $UNAME &> /dev/null # 多余信息重定向到/dev/null黑洞中(無回收能力垃圾箱)
if [ $? -eq 0 ];then
echo "$UNAME , Create success"
else
echo "$UNAME , Create failure"
fi
fi
done
案例2:批量測試主機主機是否在線。
讓腳本從主機列表文件 ipadds.txt中自動讀取 IP 地址(用來表示主機)並將其賦值給 HLIST 變量,從而通過判斷 ping 命令執行后的返回值來逐個測試主機是否在線。
[root@linuxprobe ~]# vim ipadds.txt
192.168.10.10
192.168.10.11
192.168.10.12
[root@linuxprobe ~]# vim CheckHosts.sh
#!/bin/bash
HLIST=$(cat ~/ipadds.txt)
for IP in $HLIST
do
ping -c 3 -i 0.2 -W 3 $IP &> /dev/null
if [ $? -eq 0 ] ; then
echo "Host $IP is On-line."
else
echo "Host $IP is Off-line."
fi
done
[root@linuxprobe ~]# ./CheckHosts.sh
Host 192.168.10.10 is On-line.
Host 192.168.10.11 is Off-line.
Host 192.168.10.12 is Off-line.
案例3:從1累加到用戶輸入數值