一、流程控制
過程式編程語言:順序執行、選擇執行、循環執行
1.1 條件語句
1.1.1 if條件選擇語句
選擇執行: (注意: if 語句可嵌套)
v 單分支:if 判斷條件;then
條件為真的分支代碼
fi
v 雙分支:if 判斷條件; then
條件為真的分支代碼
else
條件為假的分支代碼
fi
v 多分支: if 判斷 條件 1 ; then
if-true
elif 判斷 條件 2 ; then
if-ture
elif 判斷 條件 3 ; then
if-ture
…
else
all-false
fi
v 逐條 件進行判斷,第一次遇為“真”條件時,執行其分支,而后結束整個if 語句
1.1.2 if 實例
v 根據命令的退出狀態來執行命令
if ping -c1 -W2 station1 &> /dev/null; then
echo 'Station1 is UP'
elif grep "station1" ~/maintenance.txt &> /dev/null;then
echo 'Station1 is undergoing maintenance’
else
echo 'Station1 is unexpectedly DOWN!'
exit 1
fi
1.1.3 case條件判斷
適合于離散值的匹配;如:value 1,3,5 cmd1;value 2,4,6 cmd2… read p “please input a number ” value
case 變量引用($) in 如:case $value in
PAT1) 1|3|5)
分支1 echo a
;; ;;
PAT2) 2|4|6)
分支2 echo b
;; ;;
…
*) *)
默認分支 echo d
;; ;;
esac esac
case 支持glob 風格的通配符:
*: 任意長度任意字符 ; ?: 任意單個字符 ; [] :指定范圍內的任意單個字符 ; a|b: a 或b
1.1.4 練習
1 、編寫腳本/root/bin/createuser.sh ,實現如下功能:使用一個用戶名做為參數,如果指定參數的用戶存在,就顯示其存在,否則添加之;顯示添加的用戶的id 號等信息
read -p "please give me a user:" u
useradd $u &> /dev/null
if [ $? -eq 0 ];then
echo "user $u is already added sucessfully" ; id $u
else
echo "user is exit"
fi
2、編寫腳本/root/bin/yesorno.sh ,提示用戶輸入yes 或no,并判斷用戶輸入的是yes 還是no, 或是其它信息
read -p "please input yes or no:" i
case $i in
[yY]|[yY][eE][sS])
echo yes
;;
[nN]|[nN][oO])
echo no
;;
*)
echo your answer is wrong
;;
esac
3 、編寫腳本/root/bin/filetype.sh, 判斷用戶輸入文件路徑,顯示其文件類型(普通,目錄,鏈接,其它文件類型)
for file in $D
do
if [ -L $file ];then
echo -e "\033[32m$file\033[0m is a link file"
elif [ ! -L $file -a -d $file ];then
echo -e "\033[34m$file\033[0m is a directory"
elif [ ! -L $file -a -f $file ];then
echo -e "\033[37m$file\033[0m is a normal file"
elif [ ! -L $file -a -c $file ];then
echo -e "\033[47;33m$file\033[0m is a character device file"
elif [ ! -L $file -a -b $file ];then
echo -e "\033[33m$file\033[0m is a block device file"
elif [ ! -L $file -a -p $file ];then
echo -e "\033[35m$file\033[0m is a pipe file"
elif [ ! -L $file -a -s $file ];then
echo -e "\033[31m$file\033[0m is a socket file"
else
echo -e "\033[47;30mIt's not a directory\033[0m"
fi
done
4 、編寫腳本/root/bin/checkint.sh, 判斷用戶輸入的參數是否為正整數。
read -p "please input a number:" n
[ -z $n ] && echo "please input a int" && exit 123
num=`echo $n | egrep -o "\-?[[:digit:]]+"` #\-? 判斷–是否出現\為轉義符號
if [ "$num" == "$n" ];then
if [ "$num" -le 0 ];then
echo your input is a negative integer #負整數
elif [ "$num" == 0 ];then
echo your input is zero #0
else
echo your input is positive integer #正整數
fi
else
echo your input is not a integer #不是一個整數
fi
1.2 循環語句
循環執行:將某代碼段重復運行多次
重復運行多少次:循環次數事先已知;循環次數事先未知
有進入條件和退出條件:for, while, until
1.2.1 for循環
for 變量名 in 列表;do
循環體
done
執行機制 :
依次將列表中的元素賦值給“變量名”; 每次賦值后即執行一次循環體; 直到列表中的元素耗盡,循環結束
列表生成方式:
(1) 直接給出列表
(2) 整數列表:(a) {start..end} ; (b) $(seq [start [step]] end) 如:seq 1 10 表示1-10列表豎行顯示
(適用于數值運算) seq 0 2 10 表示顯示0 2 4 6 8 10,2為步長
(3) 返回列表的命令:$(COMMAND)
(4) 使用glob, 如:*.sh
(5) 變量引用;$@, $*
1.2.2 for練習
v 1、判斷/var/ 目錄下所有文件的類型
v 2、 添加10個用戶user1-user10 ,密碼為8 位隨機字符
read -p "how many users do you want to add: " N
#從1-$N中所有數據添加
for j in $(seq 1 $N)
do
#添加用戶,但不顯示過程
useradd user$j &> /dev/null
#取隨機8位數,作為用戶的密碼
cat /dev/urandom |tr -dc "[^[:alnum:]]"|head -c 8|passwd user$j &> /dev/null
done
v 3、/etc/rc.d/rc3.d 目錄下分別有多個以K 開頭和以S 開頭的文件 ;分別 讀取每個文件,以K 開頭的文件輸出為文件加stop,以S 開頭的文件輸出為文件名加start
“K34filename stop” “S66filename start”
for j in $(ls -1 /etc/rc.d/rc3.d/);do
if [[ $j =~ ^S.* ]];then FileC1=`echo $j | cut -c1`
echo -e "$j\tstart" case $FileC1 in
elif [[ $j =~ ^K.* ]];then K)
echo -e "$j\tstop" echo -e “$j\tstop”
else ;;
echo "it's not service file"
fi
done
v 4、編寫腳本 ,提示輸入正整數n 的值,計算1+2+…+n 的總和
read -p "give me a number:" n
sum=0
for((i=0;i<=$n;i++))
do
sum=$[$sum+$i]
done
echo $sum
v 5、編寫腳本 ,提示請輸入網絡地址,如192.168.0.0 ,判斷輸入的網段中主機在線狀態
read -p "Please give a network:" ip
pingip=`echo "$ip"| cut -d. -f1-3`.
if echo "$ip" | egrep'\<(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}
([0-9]|[1-9 ][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>' &>/dev/null;then
#test only 15 host
for i in `seq 240 255`
do
if ping -c1 -W1 $pingip$i &> /dev/null;then
echo "$pingip$i is on work"
else
echo "$pingip$i is out of work"
fi
done
else
echo "$ip is useless"
fi
v 6、 打印 九九 乘法表
#方法1:for循環 if語句 #方法2:for循環 for語句
for i in {1..9} for i in {0..9}
do do
for j in {1..9} for j in $(seq $i)
do do
if [ $j -le $i ];then echo -ne “$i*$j=$[$i*$j]\t”
echo -ne "$i*$j=$[i*j]\t" #不換行打印i*j=*TAB done
else echo
break #結束這一層的循環;一般在if中使用 done
fi
done
echo #顯示打印內容
done
v 7、在/testdir 目錄下創建10個html文件, 文件名格式為數字N(從1 到10 )加隨機8個字母,如1AbCdeFgH.html
if [ -a /testdir ];then
echo "/testdir is alreadly existing"
else
mkdir /testdir
fi
for i in $(seq 10);do
for j in `cat /dev/urandom|tr -dc "a-zA-Z"|head -c 8`
do
cd /testdir
touch $i$j.html
done
echo "$i$j.html has been created"
done
1.2.3 while 循環
while CONDITION; do
循環體
done
CONDITION :循環控制條件;進入循環之前,先做一次判斷;每一次循環之后會再次做判斷;條件為“true” ,則執行一次循環;直到條件測試狀態為“false” 終止循環。
v 因此:CONDTION 一般應該有循環控制變量;而此變量的值會在循環體不斷地被修正
v 進入條件:CONDITION 為true
v 退出條件:CONDITION 為false
1.2.4 while練習
1、編寫腳本,求100 以內 所有正奇數之和.
sum=0;i=1
while ((i<$n))
do
sum=$[$sum+$i]
let i=$[$i+2]
done
echo "sum=$sum"
v 2、編寫腳本, 提示請輸入網絡地址,如192.168.0.0 ,判斷輸入的網段中主機在線狀態 ,并統計在線主機和離線主機各多少。
read -p "Please give a network:" ip
pingip=`echo "$ip"| cut -d. -f1-3`.
if echo "$ip" | egrep '\<(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9
][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>' &>/dev/null;then
#test only 15 host
for i in `seq 240 255`
do
if ping -c1 -W1 $pingip$i &> /dev/null;then
echo "$pingip$i is on work"
else
echo "$pingip$i is out of work"
fi
done
else
echo "$ip is useless"
fi unset ip pingip i
v 3、編寫腳本,打印 九九乘法表
#方法3:while循環 #方法3:until循環
i=1 i=1
while [ $i -le 9 ];do #當$i的值小于9,就執行以下循環 until ((i>9))
j=1 do
while [ $j -le $i ];do #當$j的值小于$i,就執行以下循環 let j=1
echo -en "$i*$j=$[i*j]\t" until ((j>i))
let j++ #$j=$j+1 do
done echo -en “$i*$j=$[$i*$j]\t”
let i++ #$i=$i+1,重新開始循環 let j++
let j=1 #j的值從1開始,重新開始循環 echo
echo done
done
v 4、編寫腳本,利用變量RANDOM 生成10 個隨機數字,輸出這個10 數字,并顯示其中的最大值和最小值
max=$[$RANDOM] #變量賦值也可以用let max=$RANDOM
min=$[$RANDOM] #變量賦值也可以用$(( ));
#[ $max -lt $min ]&&let mid=$min&&let min=$max&&let max=$mid #舉一個例子
#echo "random number 1 is : $max"
#echo "random number 2 is : $min"
for ((i=1;i<=10;i++));do
ran=$[$RANDOM] #與隨機生成的中間參數做比較
echo "random number $i is : $ran"
[ $max -lt $ran ]&&let max=$ran
[ $min -gt $ran ]&&let min=$ran
done
echo "======================================="
echo "max number is $max , min number is $min"
v 5、編寫腳本,實現打印國際象棋棋盤
read -p "How many lines will you want to make for chess:" line
for i in $(seq $line);do #控制行數
for j in $(seq $line);do #控制列數
let sum=$[i+j] #把不同的兩行合并成一個單元
if [[ $sum%2 -eq 0 ]];then #取余,當為單數的話打印一種顏色,為雙數打印另一種
echo -en "\e[47m \e[0m" #打印空格顏色為黑色
else
echo -en "\e[40m \e[0m" #打印空格顏色為白色
fi z=$[$sum%2]
done [ $z -eq 0 ] && echo -ne "\033[47;1m \033[0m"||echo -ne "\033[40;1m \033[0m"
echo
done
unset i j sum
v 6、后續六個字符串:154773ae5d 、bc3f3efe68、ada7aa2054 、4ee771de1f 、2ebd3caa45 、3417171ac1是通過對RANDOM 隨機數變量執行命令:md5sum|cut -c1-10后 的結果,請破解這些字符串對應的的RANDOM值。
a=(
154773ae5d
bc3f3efe68
ada7aa2054
4ee771de1f
2ebd3caa45
3417171ac1
) #a是一個數組
for n in {0..65535};do
md5=`echo $n | md5sum |cut -c1-10`
for m in ${a[@]};do #${a[@]} 表示數組中的子集
if [[ "$md5" == "$m" ]];then
echo -e $md5 "–>" $n
fi
done
done
unset n m md5
1.2.5 until 循環
v until CONDITION; do
循環體
v done
v 進入條件: CONDITION 為false
v 退出條件: CONDITION 為true
1.3 循環控制語句
1.3.1 continue循環控制語句
v continue [N] :提前結束第N 層的本輪循環,而直接進入下一輪循環判斷;最內層為第1層;用于循環體中
while CONDTIITON1; do
CMD1
…
if CONDITION2; then
continue
fi
CMDn
…
done
1.3.2 break循環控制語句
v break [N] :提前結束第N 層循環,最內層為第1層 ;用于循環體中,退出整個循環,繼續執行循環后的命令
while CONDTIITON1; do
CMD1
…
if CONDITION2; then
break
fi
CMDn
…
done
1.3.3 shift循環控制命令
v shift [n]
v 用于將參量列表 list 左移指定次數,缺省為左移一次。
v 參量列表 list 一旦被移動,最左端的那個參數就從列表中刪除。while 到循環遍歷位置參量列表時,常用到 shift
v ./doit.sh a b c d e f g h
v ./shfit.sh a b c d e f g h
示例:doit.sh
#!/bin/bash
# Name: doit.sh
# Purpose: shift through command line arguments
# Usage: doit.sh [args]
while (( $# > 0 )) # or [ $# -gt 0 ];do
echo $*
shift
done
示例:shift.sh
#!/bin/bash
# Using 'shift' to step through all thepositional parameters.
until [ -z "$1" ];do
echo "$1"
shift
done
echo
exit 0
1.3.4創建無限循環
while true; do
循環體
done
until false; do
循環體
done
1.3.5 練習
1、每隔3秒鐘到系統上獲取已經登錄的用戶的信息;如果發現用戶hacker 登錄,則將登錄時間和主機記錄日志/var/log/login.log 中, 并退出腳本。 until false;do
while true ;do until who | grep “^hacker\>” &> /dev/null;do
if who | grep “^hacker\>” &> /dev/null;then sleep 3
break done
fi echo “$(date +: %F &T”)hacker logged on” >> /var/log/login.log
sleep 3
done
echo “$(date +: %F &T”)hacker logged on” >> /var/log/login.log
v 2、隨機生成10 以內的數字,實現猜字游戲,提示比較大或小,相等則退出。
#!/bin/bash
suiji=$[$RANDOM%10+1] #隨機生成10以內的數字
read -p "我猜:" shuru
until [[ $suiji -eq $shuru ]]; do
[ $shuru -lt $suiji ] && echo "小了"
[ $shuru -gt $suiji ] && echo "大了"
read -p "我猜:" shuru
done
echo "猜中了,退出"
v 3、用文件名做為參數,統計所有參數文件的總行數特殊用法。
1.3.6 循環的特殊用法
while特殊用法
遍歷文件的每一行:while read line; do
循環體
done < /PATH/FROM/SOMEFILE
依次讀取/PATH/FROM/SOMEFILE 文件中的每一行,且將行賦值給變量line。
練習
掃描/etc/passwd 文件每一行,如發現GECOS 字段為空,則填充用戶名和單位電話為62985600 ,并提示該用戶的GECOS信息修改成功。
while read line;do
if [ -z “`awk -F: ‘{print $5}’ $line`” ] ; then if [-z “`echo $line|cut -d: -f5`”] ;then
chfn -p 62985600 -f 62985600 `awk -F: ‘{print $1}’ $line` &> /dev/null
echo “The user GECOS have been changed successful”
fi
done < etc/passwd
unset line
for特殊用法
v 雙小括號方法,即((…)) 格式,也可以用于算術運算
v 雙小括號方法也可以使bash Shell 實現C 語言風格的變量操作
#I=10 ; #((I++))
for 循環的特殊格式:
for (( 控制變量初始化; 條件判斷表達式; 控制變量的修正表達式))
do
循環體
done
v 控制變量初始化:僅在運行到循環代碼段時執行一次
v 控制變量的修正表達式:每輪循環結束會先進行控制變量修正運算,而后再做條件判斷
1.3.7 select 循環與菜單
select variable(變量) in list
do
循環體命令
done
v select循環主要用于創建菜單,按數字順序排列的示 菜單項將顯示在標準錯誤上,并顯示 PS3提示符,等待用戶輸入。
用戶輸入菜單列表中的某個數字,執行相應的命令
v 用戶輸入被保存在內置變量 REPLY 中。
select 與case
vselect 是個無限循環,因此要記住用 break 命令退出循環,或用exit按命令終止腳本。也可以按 ctrl+c退出循環。
vselect 和經常和 case 聯合使用
v與 for循環類似,可以省略 in list ,此時使用位置參量
1.3.8 trap信號捕捉
trap ' 觸發指令' 信號
自定義進程收到系統發出的指定信號后,將執行觸發指令,而不會執行原操作
trap ' ' 信號:忽略信號的操作
trap '-' 信號:恢復還信號的操作
trap -p:列出自定義信號操作
1.3.9 trap 示例
#!/bin/bash
trap 'echo “signal:SIGINT"' int
trap -p
for((i=0;i<=10;i++))
do
sleep 1
echo $i
done
trap '' int
trap -p
for((i=11;i<=20;i++))
do
sleep 1
echo $i
done
trap '-' int
trap -p
for((i=21;i<=30;i++))
do
sleep 1
echo $i
done
二、函數介紹
v 函數function 是由若干條shell 命令組成的語句塊,實現代碼重用和模塊化編程。
v它與shell 程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell 程序的一部分。只能被調用。
v 函數和shell 程序比較相似,區別在于:
? Shell 程序在子Shell 中運行。
? 而Shell 函數在當前Shell 中運行。因此在當前Shell 中,函數可以對shell 中變量進行修改。
匿名函數:{cmd1;cmd2;cmd3},不定義在set的函數變量中。
2.1定義函數
v 函數由兩部分組成:函數名f_name和函數體。
v 語法一:
function f_name {
… 函數體…
}
v 語法二:
function f_name () {
… 函數體…
}
v最常用語法三:
f_name (){
… 函數體…
}
2.2函數使用
v 函數的定義和使用:
1.可在交互式環境下定義函數 ; 2.可將函數放在腳本文件中作為它的一部分 ; 3.可放在只包含函數的單獨文件中。
v 調用:函數只有被調用才會執行
函數的調用:給定函數名;函數名出現的地方,會被自動替換為函數代碼。
函數的生命周期:被調用時創建,返回時終止,只能在當前shell中進行。
2.2.1函數返回值
函數有兩種返回值:
v 函數的執行結果返回值:
(1) 使用echo等命令進行輸出 ; (2) 函數體中調用命令的輸出結果。
v 函數的退出狀態碼:
(1) 默認取決于函數中執行的最后一條命令的退出狀態碼 ;
(2) 自定義退出狀態碼, 其格式為:在函數中 return #從函數中返回,用最后狀態命令決定返回值。
return 0 無錯誤返回。退出函數,但是繼續執行函數后面的命令;return 1-255 有錯誤返回。不執行后面任務。
(echo $? 的值為return 返回的值)
function函數存放目錄:/etc/init.d/functions;在自己腳本中調用的話,用source /root/bin/f_name 就可執行函數
交互式環境下定義和使用函數(不常用)
v 示例:
$dir() {
> ls -l
> }
v 定義該函數后,若在$ 后面鍵入dir ,其顯示結果同ls -l的作用相同。
$dir 函數:
v 該dir 函數將一直保留到用戶從系統退出(僅對當前終端有效),或執行了如下所示的unset 命令:$ unset dir
unset f_name :刪除函數 ;set f_name :查看定義的函數
在腳本中定義及使用函數
v 函數在使用前必須定義,因此應將函數定義放在腳本開始部分,直至shell 首次發現它后才能使用
v 調用函數僅使用其函數名即可。
v 示例:
$cat func1
#!/bin/bash
# func1
hello()
{
echo "Hello there today's date is `date +%F`"
}
echo "now going to the function hello"
hello
echo "back from the function"
2.2.2使用函數文件
v 可以將經常使用的函數存入函數文件,然后將函數文件載入shell 。
v 文件名可任意選取,但最好與相關任務有某種聯系。例如:functions.main
v 一旦函數文件載入shell ,就可以在命令行或腳本中調用函數??梢允褂?/span>set 命令查看所有定義的函數,其輸出列表包括已經載入shell 的所有函數。
v 若要改動函數,首先用unset 命令從shell 中刪除函數。改動完畢后,再重新載入此文件。
創建函數文件
v 函數文件示例:
$cat functions.main
#!/bin/bash
#functions.main
findit()
{
if [ $# -lt 1 ] ; then
echo "Usage:findit file"
return 1
fi
find / -name $1 –print
}
載入函數
v 函數文件已創建好后,要將它載入shell
v 定位函數文件并載入shell 的格式:. filename 或 source filename
隨后,函數就被載入到set 所定義的函數里了,就可以在腳本里直接使用函數名了
v 注意:此即< 點> < 空格> < 文件名>
這里的文件名要帶正確路徑
v 示例:上例中的函數,可使用如下命令: . functions.main
檢查載入函數
v 使用set 命令檢查函數是否已載入。set 命令將在shell 中顯示所有的載入函數。
v 示例:
$set
findit=( )
{
if [ $# -lt 1 ]; then
echo "usage :findit file";
return 1
fi
find / -name $1 -print
}
…
2.2.3執行shell 函數
v 要執行函數,簡單地鍵入函數名即可:
$findit groups
/usr/bin/groups
/usr/local/backups/groups.bak
刪除shell 函數
v 現在對函數做一些改動。首先刪除函數,使其對shell 不可用。使用unset 命令完成此功能.
v 命令格式為:unset function_name
v 示例 :$unset findit ; 再鍵入set 命令,函數將不再顯示
2.3函數參數
v 函數可以接受參數:
傳遞參數給函數:調用函數時,在函數名后面以空白分隔給定參數列表即可;例如“testfunc arg1 arg2 …”
在函數體中當中,可使用$1, $2, … 調用這些參數;還可以使用$@, $*, $# 等特殊變量
2.3.1函數變量
變量作用域:
環境變量:當前shell 和子shell 有效
本地變量:只在當前shell 進程有效,為執行腳本會啟動專用子shell 進程;因此,本地變量的作用范圍是當前shell 腳本程序文件,包括腳本中的函數。
局部變量:函數的生命周期;函數結束時變量被自動銷毀
v 注意:如果函數中有局部變量,如果其名稱同本地變量,使用局部變量。
v 在函數中定義局部變量的方法:local NAME=VALUE
2.3.2函數遞歸
函數直接或間接調用自身;注意遞歸層數
v 遞歸實例:階乘是基斯頓·于卡曼于1808年發明的運算符號,是數學術語一個正整數的階乘(factorial )是所有小于及等于該數的正整數的積,并且有0 的階乘為1 。自然數n 的階乘寫作n!。
n!=1 ×2 ×3 ×… ×n。
階乘亦可以遞歸方式定義:0!=1 ,n!=(n-1)! ×n。
n!=n(n-1)(n-2)…1 ; n(n-1)! = n(n-1)(n-2)!
函數遞歸示例: fact.sh
#!/bin/bash
#
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}
fact $1
注意:引入變量先要用read
2.3.3練習
1 、編寫服務 腳本/root/bin/testsrv.sh ,完成如下要求:
(1) 腳本可接受參數:start, stop, restart, status
(2) 如果參數非此四者之一,提示使用格式后報錯退出
(3) 如是start:則創建/var/lock/subsys/ SCRIPT_NAME , 并顯示“啟動成功”
考慮:如果事先已經啟動過一次,該如何處理?
(4) 如是stop:則刪除/var/lock/subsys/ SCRIPT_NAME , 并顯示“停止完成”
考慮:如果事先已然停止過了,該如何處理?
(5) 如是restart ,則先stop, 再start
考慮:如果本來沒有start ,如何處理?
(6) 如是status, 則如果/var/lock/subsys/ SCRIPT_NAME 文件存在,則顯示“ SCRIPT_NAME is running…”
如果/var/lock/subsys/ SCRIPT_NAME 文件不存在,則顯示“ SCRIPT_NAME is stopped…”
其中: SCRIPT_NAME 為當前腳本名
(7) 在所有模式下禁止啟動該服務,可用chkconfig 和 service 命令管理
lockfile=(/var/log/subsys/SCRIPT_NAME)
usagte(){
echo "Usage: $prog {start|stop|restart|status}"
}
if [ $# -lt 1 ]; then
usage
exit 1
fi
start(){
if [ -e $lockfile ]; then
echo "$prog is already running."
return 0
else
touch $lockfile
[ $? -eq 0 ] && echo "Starting $prog finished."
fi
}
stop(){
if [ -e $lockfile ]; then
rm -f $lockfile && echo "Stop $prog ok."
else
echo "$prog is stopped yet."
fi
}
status(){
if [ -e $lockfile ]; then
echo "$prog is running."
else
echo "$prog is stopped."
fi
}
case $1 in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
status
;;
*)
usage
esac
2 、編寫腳本/root/bin/copycmd.sh
(1) 提示用戶輸入一個可執行 命令名稱
(2) 獲取此命令所依賴到的所有庫文件列表
(3) 復制命令至某目標目錄( 例如/mnt/sysroot) 下的對應路徑下;
如:/bin/bash ==> /mnt/sysroot/bin/bash
/usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd
(4) 復制此命令依賴到的所有庫文件至目標目錄下的對應路徑下:
如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2
(5) 每次復制完成一個命令后,不要退出,而是提示用戶鍵入新的要復制的命令,并重復完成上述功能;直到用戶輸入quit.
#!/bin/bash
ch_root="/mnt/sysroot"
[ ! -d $ch_root ] && mkdir $ch_root
bincopy() {
if which $1 $>/dev/null; then
local cmd_path="which –skip-alias $1"
local bin_dir="dirname $cmd_path"
[ -d ${ch_root}${bin_dir} ] || mkdir -p ${ch_root}${bin_dir}
[ -f ${ch_root}${cmd_path}] || cp $cmd_path ${ch_root}${bin_dir}
return 0
else
echo "Command not found."
return 1
fi
}
libcopy() {
local lib_list=$(ldd `which –skip-alias $1` | grep -Eo '/[^[:space:]]+')
for loop in $lib_list;do
local lib_dir=`dirname $loop`
[ -d $ {ch_root}${lib_dir} ] || mkdir -p ${ch_root}${lib_dir}
[ -f $ {ch_root}${loop} ] || cp $loop ${ch_root}${lib_dir}
done
}
read -p "Please input a command:" command
while [ "$command" != "quit" ]; do
if bincopy $command ; then
libcopy $command
fi
read -p "Please input a command or quit: "command
done
3 、編寫函數實現兩個數字做為參數,返回最大值.
#!/bin/bash
source funs.sh
funs.sh
#!/bin/bash
echo "please enter two number"
read a
read b
if test $a -eq $b;then
echo "two same: "$a
elif test $a -gt $b;then
echo "big is: "$a
else
echo "big is: "$b
fi
4 、編寫函數實現數字的加減乘除運算,例如輸入 1 + 2 ,將得出正確結果。
#!/bin/bash
source funs.sh
jia $1 $2
jian $1 $2
cheng $1 $2
chu $1 $2
funs.sh
#!bin/bash
echo "1st arg is $1"
echo "2nd arg is $2"
jia (){
local a=$[$1+$2]
echo $a
}
jian (){
local a=$[$2-$1]
echo $a
}
cheng (){
local a=$[$1*$2]
echo $a
}
chu (){
local a=$[$1/$2]
echo $a
}
5 、斐波那契數列又稱黃金分割數列,因數學家列昂納多· 斐波那契以兔子繁殖為例子而引入,故又稱為“兔子數列”,指的是這樣一個數列:0 、1 、1 、2 、3 、5 、8 、13 、21 、34 、…… ,斐波納契數列以如下被以遞歸的方法定義:F (0 )=0 ,F (1 )=1 ,F (n )=F(n-1)+F(n-2) (n≥2)利用函數 ,求n 階斐波那契數列。
fact() {
if [ $1 -eq 0 ]
then
echo 0
elif [ $1 -eq 1 ]
then
echo 1
else
echo $[$(fact $[$1-2])+$(fact $[$1-1])]
fi
}
6 、漢諾塔(又稱河內塔)問題是源于印度一個古老傳說。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞著64 片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。并且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。
利用函數,實現N 片盤的漢諾塔的移動步驟。
#!/bin/bash
step=0
hanoi(){
[[ ! $1 = ~ ^[1-9][0-9]*$ ]]&&echo "error! please input a positive interger" && exit
if [ $1 -eq 1 ]; then
let step++
echo "$step: move plate $1 $2 —–> $4"
else
hanoi "$[$1-1]" $2 $4 $3
let step++
echo "$step: move plate $1 $2 —–> $4"
hanoi "$[$1-1]" $3 $2 $4
fi
}
read -p "please input the number of plates: "number
hanoi $number A B C
三、數組
v 變量:存儲單個元素的內存空間
v 數組:存儲多個元素的連續的內存空間,相當于多個變量的集合。
數組名和索引
索引:編號從0 開始,屬于數值索引
注意:索引可支持使用自定義的格式,而不僅是數值格式,即為關聯索引 ,bash4.0 版本之后開始支持。
bash 的數組支持稀疏格式(索引之間不連續)
聲明數組:
declare -a ARRAY_NAME 可以不聲明,但是建議聲明
declare -A ARRAY_NAME: 關聯數組 ;必須要先聲明才能使用
3.1數組賦值
v 數組元素的賦值:
(1) 一次只賦值一個元素;
ARRAY_NAME[INDEX]下標=VALUE
weekdays[0]="Sunday" ; weekdays[4]="Thursday" 稀疏格式
(2) 一次賦值全部元素:ARRAY_NAME=("VAL1" "VAL2" "VAL3" …)
生成列表可以用ARRAY_NAME=;ARRAY_NAME=({1..10});ARRAY_NAME=(*)
(3) 只賦值特定元素:指定下標,直接賦值
ARRAY_NAME=([0]="VAL1" [3]="VAL2" …)
(4) 交互式數組值對賦值:read -a ARRAY
3.2引用數組
v 引用數組元素:${ARRAY_NAME[INDEX]} 注意:省略[INDEX] 表示引用下標為0 的元素
數組的長度( 數組中元素的個數): 當不知道數組中存在多少元素
${ARRAY_NAME[*]}、${ARRAY_NAME[@]}:查看顯示數組中所有元素
${#ARRAY_NAME[*]}、${#ARRAY_NAME[@]}:查看顯示數組的元素個數 ,新加的元素的下標就等于元素的個數
如果數組要新添加一個元素 name[${ARRAY_NAME[@]}]=”var”
v 示例:生成10 個隨機數保存于數組中,并找出其最大值和最小值
#!/bin/bash
declare -a rand
declare -i max=0
declare -i min=32767
for i in {0..9}; do
rand[$i]=$RANDOM
echo ${rand[$i]}
[ ${rand[$i]} -gt $max ] && max=${rand[$i]}
[ ${rand[$i]} -lt $min ] && min=${rand[$i]}
done
echo "Max: $max Min:$min"
v 編寫腳本 ,定義一個數組,數組中的元素是/var/log 目錄下所有以.log 結尾的文件;要統計其下標為偶數的文件中的行數之和。
#!/bin/bash
#
declare -a files
files=(/var/log/*.log)
declare -i lines=0
for i in $(seq 0 $[${#files[*]}-1]); do fileline=`wc -l ${file[$i]} |cut -d " " -f1`
if [ $[$i%2] -eq 0 ];then ——》for i in $(seq 0 2 $[${#file[*]}-1];do
let lines+=$(wc -l ${files[$i]} | cut -d' ' -f1) [ $[$i%2] -eq 0 ] && let lines+=$fileline
fi
done
echo "Lines: $lines."
3.3數組數據處理
引用數組中的元素:
所有元素:${ARRAY[@]} ; ${ARRAY[*]}
數組切片:${ARRAY[@]:offset:number} 跳offset個元素;取隨后的number個元素
offset: 要跳過的元素個數 ; number: 要取出的元素個數
取偏移量之后的所有元素:${ARRAY[@]:offset}
向數組中追加元素:ARRAY[${#ARRAY[*]}]=“var”
刪除數組中的某元素:導致稀疏格式:unset ARRAY[INDEX]
關聯數組:declare -A ARRAY_NAME 注意:必須先聲明,再調用
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2’…)
現有的數組只能先unset ,再重新開始設置
練習1 、輸入若干個數值存入數組中,采用 冒泡算法進行升序或降序排序
#!/bin/sh
echo "please input a number list:"
read -a arr
for (( i=0 ; i<${#arr[@]} ; i++ ))
do
for (( j=${#arr[@]}- 1 ; j>i ; j– ))
do
#echo $j
if [[ ${arr[j]} -lt ${arr[j-1]} ]]
then
t=${arr[j]}
arr[j]=${arr[j-1]}
arr[j-1]=$t
fi
done
done
echo "after ascending sorting:"
echo ${arr[@]}
=================================================
for (( i=0 ; i<${#arr[@]} ; i++ ))
do
for (( j=${#arr[@]}- 1 ; j>i ; j– ))
do
#echo $j
if [[ ${arr[j]} -gt ${arr[j-1]} ]]
then
t=${arr[j]}
arr[j]=${arr[j-1]}
arr[j-1]=$t
fi
done
done
echo "after descending sorting:"
echo ${arr[@]}
3.4字符串處理
bash的字符串處理工具
3.4.1字符串切片
${#var}: 返回字符串變量var 的長度
${var:offset}: 返回字符串變量var 中從第offset 個字符后(不包括第offset 個字符)的字符開始,到最后的部分,offset的取值在0 到 ${#var}-1 之間(bash4.2 后,允許為負值)
${var:offset:number} :返回 字符串變量var 中從第offset 個字符后(不包括第offset 個字符)的字符開始長度為number 的部分
${var: -lengh} :取字符串的最右側幾個字符
注意:冒號后必須有一空白字符
${var:offset: -lengh} :從最左側跳過offset 字符,一直取到字符串的最右側lengh個字符之前
3.4.2基于模式取子串
${var#*word} :其中word 可以是指定的任意字符
功能:自左而右,查找var 變量所存儲的字符串中,第一次出現的word, 刪除字符串開頭至第一次出現word 字符之間的所有字符。
${var##*word} :同上,不同的是,刪除的是字符串開頭至最后一次由word 指定的字符之間的所有內容
示例:file="var/log/messages”
echo ${file#*/}: log/messages ; echo ${file##*/}: messages
${var%word*} :其中word 可以是指定的任意字符;
功能:自右而左,查找var 變量所存儲的字符串中,第一次出現的word, 刪除字符串最后一個字符向左至第一次出現word 字符之間的所有字符;
${var%%word*} :同上,只不過刪除字符串最右側的字符向左至最后一次出現word 字符之間的所有字符;
file="/var/log/messages"
echo ${file%/*}: /var/log ; echo ${file%%/*}: 空
示例:url=http://www.magedu.com:80
echo ${url##*:} 80 ; echo ${url%%:*} http
3.4.3查找替換
${var/pattern/substi} :查找var 所表示的字符串中,第一次被pattern 所匹配到的字符串,以substi 替換之
${var//pattern/substi}: 查找var 所表示的字符串中,所有能被pattern 所匹配到的字符串,以substi 替換之
${var/#pattern/substi} :查找var 所表示的字符串中,行首被pattern 所匹配到的字符串,以substi 替換之
${var/%pattern/substi} :查找var 所表示的字符串中,行尾被pattern 所匹配到的字符串,以substi 替換之
3.4.4查找并刪除
${var/pattern} :查找var 所表示的字符串中,刪除第一次被pattern 所匹配到的字符串
${var//pattern} :所有
${var/#pattern} :行首
${var/%pattern} :行尾
3.4.5字符大小寫轉換
${var^^} :把var 中的所有小寫字母轉換為大寫
${var,,} :把var 中的所有大寫字母轉換為小寫
3.5變量賦值
v ${var:-value} :如果var 為空或未設置,那么返回value ;否則,則返回var的值
v ${var:+value} :如果var 不空,則返回value ,否則返回空值
v ${var:=value} :如果var 為空或未設置,那么返回value ,并將value 賦值給var ;否則,則返回var的值
v ${var:?error_info} :如果var 為空或未設置 ,那么在當前終端打印error_info;否則,則返回var的值
為腳本程序使用配置文件, 實現變量賦值
(1) 定義文本文件,每行定義“name=value”
(2) 在腳本中source 此文件即可
高級變量用法– 有類型變量
v Shell 變量一般是無類型的,但是bash Shell 提供了declare和typeset 兩個命令用于指定變量的類型,兩個命令是等價的。
3.5.1 declare變量
v declare [ 選項] 變量名
-r:將變量設置為只讀屬性 ; -x:將變量聲明為環境變量 ;-f:顯示此腳本前定義過的所有函數名及其內容
-i:將變量定義為整型數 ; -a:將變量定義為數組 ;-F:僅顯示此腳本前定義過的所有函數名
-A:將變量定義為關聯數組
-l:將變量值轉為小寫字母 declare -l var=UPPER ; -u:將變量值轉為大寫字母 declare -u var=lower
3.5.2間接變量引用
v 如果第一個變量的值是第二個變量的名字,從第一個變量引用第二個變量的值就稱為間接變量引用。
v variable1=variable2 ; variable2=value
v variable1 的值是variable2 ,而variable2 又是變量名,variable2 的值為value ,間接變量引用是指通過variable1獲得變量值value 的行為
v bash Shell 提供了兩種格式實現間接變量引用
eval tempvar=\$$variable1 ; tempvar=${!variable1}
示例: [root@server ~]# N=NAME
[root@server ~]# NAME=wangxiaochun
[root@server ~]# N1=${!N}
[root@server ~]# echo $N1
wangxiaochun
[root@server ~]# eval N2=\$$N
[root@server ~]# echo $N2
wangxiaochun
3.5.3 eval 命令
v eval 命令將會首先掃描命令行進行所有的置換,然后再執行該命令。該命令適用于那些一次掃描無法實現其功能的變量. 該命令對變量進行兩次掃描(如果變量是命令,則顯示執行的結果)。
示例: [root@server ~]# CMD=whoami
[root@server ~]# echo $CMD
whoami
[root@server ~]# eval $CMD
root
3.5.4 mktemp創建臨時文件
mktemp 命令:創建的臨時文件可避免沖突
v mktemp [OPTION]… [TEMPLATE]
TEMPLATE: filename.XXX 注意:X 至少要出現三個,創建并顯示創建的臨時文件的名字
-d: 創建臨時目錄 ; -p DIR 或–tmpdir=DIR :指明臨時文件所存放目錄位置
示例 :
#mktemp /tmp/test.XXX 計劃任務crontab -e 輸出重定向到/dev/null;或者用此臨時文件愛你
#tmpdir=`mktemp –d /tmp/testdir.XXX`
#mktemp –tmpdir=/testdir test.XXXXXX
3.5.5 install安裝復制文件
v install 命令:適合安裝程序,能直接給予文件的所有者,所有組以及權限
install [OPTION]… [-T] SOURCE DEST 單文件 ; install [OPTION]… SOURCE… DIRECTORY
install [OPTION]… -t DIRECTORY SOURCE… ; install [OPTION]… -d DIRECTORY… 創建空目錄
-m MODE:指明權限 ,默認755 ; -o OWNER:指明屬主 ; -g GROUP:指明屬組
示例:
install -m 700 -o wang -g admins file1 file2 相當于cp,不過給予了權限
install –m –d /testdir/installdir 直接創建目錄
原創文章,作者:lyx,如若轉載,請注明出處:http://www.www58058.com/61073