腳本進階
循環
信号捕捉
函數
數組
進階字元串操作
進階變量
expect
程式設計中的邏輯處理:
- 順序執行
- 選擇執行
- 循環執行
循環
-
循環執行
将某代碼段重複運作多次
重複運作多少次
循環次數事先已知
循環次數事先未知
有進入條件和退出條件
- for, while, until
for循環
-
for 變量名 in 清單;do
循環體
done
-
執行機制:
依次将清單中的元素指派給“變量名”; 每次指派後即執行一次循環體; 直到清單中的元素耗盡,循環結束
for NAME [in WORDS ... ] ; do COMMANDS; done
其中 NAME 表示直接是變量名
case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac
case中的WORD則表示變量值 case $var in ....
表示時case與其他表示有差别~~~
清單生成方式:
- (1) 直接給出清單
-
(2) 整數清單:
{start…end}
$(seq [start [step]] end)
-
(3) 傳回清單的指令
$(COMMAND)
- (4) 使用glob,如:*.sh
-
(5) 變量引用
$@, $*
for特殊格式(類似c語言風格的循環)
- 雙小括号方法,即((…))格式,也可以用于算術運算
- 雙小括号方法也可以使bash Shell實作C語言風格的變量操作
-
for循環的特殊格式:
for ((控制變量初始化;條件判斷表達式;控制變量的修正表達式));do
循環體
done
- 控制變量初始化:僅在運作到循環代碼段時執行一次
- 控制變量的修正表達式:每輪循環結束會先進行控制變量修正運算,而後再做條件判斷
help for:
其中for (( exp1; exp2; exp3 )); do COMMANDS; done
或者for (( exp1; exp2; exp3 )); do
COMMANDS
done
①注意:連着寫CMD末尾有 ; 分号 (循環的語句跟{}類似,指令行直接寫循環時注意CMD;)
②其中for (( exp1; exp2; exp3 )); do COMMANDS; done ==
(( EXP1 ))
while (( EXP2 )); do
COMMANDS
(( EXP3 ))
done
兩種形式等價:是以在for (( exp1; exp2; exp3 ))中
若exp1有多個變量初始化時,之間使用 , 隔開
eg:for((sum=0,i=1;i<=100;i++));do let sum+=i;done; echo $sum
for循環練習
- 1、判斷/var/目錄下所有檔案的類型
- 2、添加10個使用者user1-user10,密碼為8位随機字元
- 3、/etc/rc.d/rc3.d目錄下分别有多個以K開頭和以S開頭的檔案;分别讀取每個檔案,以K開頭的輸出為檔案加stop,以S開頭的輸出為檔案名加start,如K34filename stop S66filename start
- 4、編寫腳本,提示輸入正整數n的值,計算1+2+…+n的總和
- 5、計算100以内所有能被3整除的整數之和
- 6、編寫腳本,提示請輸入網絡位址,如192.168.0.0,判斷輸入的網段中主機線上狀态
- 7、列印九九乘法表
- 8、在/testdir目錄下建立10個html檔案,檔案名格式為數字N(從1到10)加随機8個字母, 如:1AbCdeFgH.html
- 9、列印等腰三角形
while循環
-
while CONDITION; do
循環體
done
- CONDITION:循環控制條件;進入循環之前,先做一次判斷;每一次循環之後 會再次做判斷;條件為“true”,則執行一次循環;直到條件測試狀态為“false” 終止循環
- 是以:CONDTION一般應該有循環控制變量;而此變量的值會在循環體不斷地被修正
- 進入條件:CONDITION為true
- 退出條件:CONDITION為false
特殊用法
-
while循環的特殊用法(周遊檔案的每一行)
while read line; do
循環體
done < /PATH/FROM/SOMEFILE
- 依次讀取/PATH/FROM/SOMEFILE檔案中的每一行,且将行指派給變量line
若使用逐行處理時使用while read line這樣用法
實際例子:檢視網絡連接配接數ss -tn 時常将ip篩選出來,然後将對應次數統計
逐行處理時可以使用while read line
總涉及ip通路統計ip 和 次數 然後對于不正常的通路利用防火牆隔絕,
然後涉及兩個變量 ip 和 對應ip次數 可以分别放入兩個變量中,然後逐行處理
ss -nt|sed -nr '/^ESTAB/s#.* (.*):.*#\1#p'|sort|uniq -c|sort -nr|while read num ip ;do echo "$num:$ip"; done
<可以看到連接配接數賦給了num ipv4位址賦給變量ip>
當然中間執行的操作可以設定為:如果1min内,連接配接數大于100,就拒絕通路(結合計劃任務)
[ $num -eq 100 ] && iptables -A INPUT -s $ip -j REJECT
while read line;do CMDS;done 逐行處理示例:
1)标準輸入接收
~]# seq 5 | while read line;do echo line:$line;done
line:1
line:2
line:3
line:4
line:5
2)标準輸入重定向
]# while read line;do echo line:$line;done < /etc/issue
line:S
line:Kernel r on an m
3)實作對兩個變量同時指派:
~]# echo -e "a b\nc d" | while read x y;do echo $x $y;done
a b <預設以空格為分割符> 空格前值賦給x
c d <逐行處理><與read指派差不多>
~]# echo a b | { read x y;echo $x $y; }
a b
<管道右側指令在子shell中執行,是以檢視管道右側變量的指派時應該在一個shell中檢視>
-
練習
掃描/etc/passwd檔案每一行,如發現GECOS字段為空,則将使用者名和機關電
話為62985600填充至GECOS字段,并提示該使用者的GECOS資訊修改成功
練習總結:while read line;do CMDS;done 逐行處理,
上面的練習有種在通路的檔案然後在修改,好像會生個finenamer的檔案,
以下是我實作練習功能的腳本:<沒有直接修改/etc/passwd檔案,将檔案拷貝至/data/test目錄修改>
#!/bin/bash
file=/data/test/passwd
#判斷檔案是否存在
[ -f "$file" ] || { echo "$file is not existence."; exit 1; }
phone=62985600
# 主要想調用action函數
. /etc/rc.d/init.d/functions
while read line ;do
# 如果GECOS字段不為空;continue,為空再執行下面的指令
echo $line | grep -q '::' || continue
user=`echo $line | cut -d":" -f1`
#使用的sed指令修改的原檔案,但是sed指令一般習慣使用' ' (強引用),是以之間的變量要生效可以使用'''$VAR'''
echo $line | sed -ir '/'''$user'''/s#::#:'''$user''' '''$phone''':#' $file
[ $? -eq 0 ] && action "$user GECOS modified"
done < $file
# 删除腳本執行生成的finenamer檔案
rm -f ${file}r
while練習
- 1、編寫腳本,求100以内所有正奇數之和
- 2、編寫腳本,提示請輸入網絡位址,如192.168.0.0,判斷輸入的網段中主機線上狀态,并統計線上和離線主機各多少
- 3、編寫腳本,列印九九乘法表
- 4、編寫腳本,利用變量RANDOM生成10個随機數字,輸出這個10數字,并顯示其中的最大值和最小值
- 5、編寫腳本,實作列印國際象棋棋盤
- 6、後續六個字元串:efbaf275cd、4be9c40b8b、44b2395c46、 f8c8873ce0、b902c16c8b、ad865d2f63是通過對随機數變量RANDOM随機 執行指令: echo $RANDOM|md5sum|cut –c1-10 後的結果,請破解這些 字元串對應的RANDOM值
until循環(與while邏輯相反)
-
until CONDITION; do
循環體
done
- 進入條件: CONDITION 為false
- 退出條件: CONDITION 為true
循環控制語句continue
- 用于循環體中
-
continue [N]:提前結束第N層的本輪循環,而直接進入下一輪判斷;最内層為第1層
while CONDTIITON1; do
CMD1
…
if CONDITION2; then
continue
fi
CMDn
…
done
循環控制語句break
- 用于循環體中
-
break [N]:提前結束第N層循環,最内層為第1層
while CONDTIITON1; do
CMD1
…
if CONDITION2; then
break
fi
CMDn
…
done
循環控制shift指令
- shift [n]
- 用于将參量清單 list 左移指定次數,預設為左移一次。
- 參量清單 list 一旦被移動,最左端的那個參數就從清單中删除。while 循環周遊位置參量清單時,常用到 shift
- ./doit.sh a b c d e f g h
- ./shfit.sh a b c d e f g h
- 可始終針對具體的位置參數做處理
一般的判斷邏輯為:始終處理$1,循環次數$#
while [ "$1" ];do 或者 while [ $# -gt 0 ] # or (( $# > 0 ))
CMD $1
shift
done
< 切記不能使用for ((i=0;i<$#;i++)) 這種邏輯判斷!! 因為$#在遞減 >
select循環與菜單
-
select variable in list;do
循環體指令
done
- select 循環主要用于建立菜單,按數字順序排列的菜單項将顯示在标準錯誤上,并顯示 PS3 提示符,等待使用者輸入
- 使用者輸入菜單清單中的某個數字,執行相應的指令
- 使用者輸入被儲存在内置變量 REPLY 中
- select 是個無限循環,是以要記住用 break 指令退出循環,或用 exit 指令終止 腳本。也可以按 ctrl+c 退出循環
- select 經常和 case 聯合使用
- 與 for 循環類似,可以省略 in list,此時使用位置參量
- PS2是輸入重定向的提示符
其中可以實作一個目錄中全部存放可執行腳本:
然後配合select制作成菜單選項,選擇什麼就執行相應的腳本
執行該目錄下所有可執行程式:run-parts 指令(腳本需要執行權限) run-parts /dir
大概實作:
#!/bin/bash
PS3="Choose what you need to do: "
select opt in `ls /data/test/` ;do
echo $opt
echo $REPLY
# case $opt in
# ...
# esac
done
[[email protected] test]# bash select.sh <腳本效果,選擇對應選項執行對應腳本>
1) backup.sh 3) check_dos.sh 5) select.sh
2) check_disk.sh 4) scan_ip.sh
Choose what you need to do:
函數介紹
- 函數function是由若幹條shell指令組成的語句塊,實作代碼重用和子產品化程式設計
- 它與shell程式形式上是相似的,不同的是它不是一個單獨的程序,不能獨立運作,而是shell程式的一部分
- 函數和shell程式比較相似,差別在于
Shell程式在子Shell中運作
而Shell函數在目前Shell中運作。是以在目前Shell中,函數可以對shell中變 量進行修改
定義函數
- 函數由兩部分組成:函數名和函數體
- help function
-
文法一:(一般采用文法一簡潔)
f_name (){
…函數體…
}
-
文法二:
function f_name {
…函數體…
}
-
文法三:
function f_name () {
…函數體…
}
函數使用
-
函數的定義和使用:
①可在互動式環境下定義函數
②可将函數放在腳本檔案中作為它的一部分
③可放在隻包含函數的單獨檔案中
- 調用:函數隻有被調用才會執行 調用:給定函數名 函數名出現的地方,會被自動替為函數代碼
- 函數的生命周期:被調用時建立,傳回時終止
函數傳回值
- 函數有兩種傳回值:
-
函數的執行結果傳回值:
(1) 使用echo等指令進行輸出
(2) 函數體中調用指令的輸出結果
-
函數的退出狀态碼:
(1) 預設取決于函數中執行的最後一條指令的退出狀态碼
(2) 自定義退出狀态碼,其格式為:
return 從函數中傳回,用最後狀态指令決定傳回值
return 0 無錯誤傳回
return 1-255 有錯誤傳回
互動式環境下定義和使用函數
-
示例:
dir() {
> ls -l
> }
-
定義該函數後,若在$後面鍵入dir,其顯示結果同ls -l的作用相同
dir
-
該dir函數将一直保留到使用者從系統退出,或執行了如下所示的unset指令
unset dir
在腳本中定義及使用函數
- 函數在使用前必須定義,是以應将函數定義放在腳本開始部分,直至shell首次發現它後才能使用
- 調用函數僅使用其函數名即可
使用函數檔案
- 可以将經常使用的函數存入函數檔案,然後将函數檔案載入shell
- 檔案名可任意選取,但最好與相關任務有某種聯系。例如:functions.main
- 一旦函數檔案載入shell,就可以在指令行或腳本中調用函數。可以使用set指令檢視所有定義的函數,其輸出清單包括已經載入shell的所有函數
- 若要改動函數,首先用unset指令從shell中删除函數。改動完畢後,再重新載 入此檔案
函數檔案示例:
1、系統定義的函數檔案:/etc/rc.d/init.d/functions
系統定義了一個action()函數,功能如下:
[[email protected] data]# . /etc/rc.d/init.d/functions
[[email protected] data]# action "hello"
hello [ OK ]
[[email protected] data]# action "hello" false
hello [FAILED]
綠色OK,紅色FAILED
2、自己定義函數
以下常用的函數:
<輸入是否正确的函數>
<輸入的ip是否是合法ip函數>
<判斷輸入的是否是無符号整數>
cat functions.main
#functions.main
unint (){
[[ "$1" =~ ^[0-9]+$ ]] && return 0 || return 1
}
legal_ip () {
[[ "$1" =~ ^(([1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.){3}\
([1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$ ]] || return 1
}
blank (){
[ "$1" ] && return 0 || return 1
}
載入函數
- 函數檔案已建立好後,要将它載入shell
-
定位函數檔案并載入shell的格式
. filename 或 source filename
- 注意:此即<點> <空格> <檔案名> 這裡的檔案名要帶正确路徑
檢查載入函數
- 使用set指令檢查函數是否已載入。set指令将在shell中顯示所有的載入函數
執行shell函數
- 要執行函數,簡單地鍵入函數名即可
-
示例:
findit groups
/usr/bin/groups
/usr/local/backups/groups.bak
删除shell函數
-
現在對函數做一些改動後,需要先删除函數,使其對shell不可用。使用unset命
令完成删除函數
-
指令格式為:
unset function_name
-
環境函數
使子程序也可使用
聲明:export -f function_name
檢視:export -f 或 declare -xf
函數參數
-
函數可以接受參數:
傳遞參數給函數:調用函數時,在函數名後面以空白分隔給定參數清單即可;
例如“testfunc arg1 arg2 …”
-
在函數體中當中,可使用$1, 2 , . . . 調 用 這 些 參 數 ; 還 可 以 使 用 2, ...調用這些參數;還可以使用 2,...調用這些參數;還可以使用@, $*, $#
等特殊變量
函數變量
變量作用域:
- 環境變量:目前shell和子shell有效
- 本地變量:隻在目前shell程序有效,為執行腳本會啟動專用子shell程序; 是以,本地變量的作用範圍是目前shell腳本程式檔案,包括腳本中的函數
- 局部變量:函數的生命周期;函數結束時變量被自動銷毀
- 注意: 如果函數中有局部變量,如果其名稱同本地變量,使用局部變量
-
在函數中定義局部變量的方法
local NAME=VALUE
函數遞歸示例
-
函數遞歸:
函數直接或間接調用自身
注意:遞歸層數
-
遞歸執行個體:
階乘是基斯頓·卡曼于 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)!
#!/bin/bash
#
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*`fact $[$1-1]`]
fi
}
fact $1
fork炸彈
- fork炸彈是一種惡意程式,它的内部是一個不斷在fork程序的無限循環,實質是一個簡單的遞歸程式。由于程式是遞歸的,如果沒有任何限制,這會導緻這個簡單的程式迅速耗盡系統裡面的所有資源
- 函數實作
:(){ :|:& };:
bomb() { bomb | bomb & }; bomb
- 腳本實作
cat Bomb.sh
#!/bin/bash
./$0|./$0&
腳本練習
練習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…”
(7)在所有模式下禁止啟動該服務,可用chkconfig 和 service指令管理
說明:SCRIPT_NAME為目前腳本名
練習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退出
信号捕捉trap
- 當腳本在執行過程中有時不希望被kill發送的信号打斷,因為腳本執行一半有時會影響後續的執行,此時可以采用trap将信号捕獲,進行相應的處理。
-
trap ‘觸發指令’ 信号
程序收到系統發出的指定信号後,将執行自定義指令,而不會執行原操作
-
trap ‘’ 信号
忽略信号的操作
-
trap ‘-’ 信号
恢複原信号的操作
-
trap -p
列出自定義信号操作
-
trap finish EXIT
當腳本退出時,執行finish函數
- 注意: 9) SIGKIL 信号不能被捕獲(man kill中有說明)
#!/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
數組
- 變量:存儲單個元素的記憶體空間
- 數組:存儲多個元素的連續的記憶體空間,相當于多個變量的集合
-
數組名和索引
①索引:編号從0開始,屬于數值索引
②注意: 索引可支援使用自定義的格式,而不僅是數值格式,即為關聯索引,bash4.0版本之後開始支援
③bash的數組支援稀疏格式(索引不連續)
-
聲明數組:
declare -a ARRAY_NAME
declare -A ARRAY_NAME 關聯數組
注意: 兩者不可互相轉換;關聯數組必須先聲明後使用
- 數組的最大索引即為元素個數
數組指派
-
數組元素的指派
(1) 一次隻指派一個元素
ARRAY_NAME[INDEX]=VALUE
weekdays[0]=“Sunday” weekdays[4]=“Thursday”
(2) 一次指派全部元素
ARRAY_NAME=(“VAL1” “VAL2” “VAL3” …)
(3) 隻指派特定元素
ARRAY_NAME=([0]=“VAL1” [3]=“VAL2” …)
(4) 互動式數組值對指派 read -a ARRAY
- 顯示所有數組:declare -a
引用數組
-
引用數組元素
${ARRAY_NAME[INDEX]}
注意:省略[INDEX]表示引用下标為0的元素
-
引用數組所有元素
${ARRAY_NAME[*]}
${ARRAY_NAME[@]}
-
數組的長度(數組中元素的個數)
${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
- 删除數組中的某元素:導緻稀疏格式 unset ARRAY[INDEX]
-
删除整個數組
unset ARRAY
數組資料處理
-
引用數組中的元素:
數組切片:
${ARRAY[@]:offset:number}
offset 要跳過的元素個數
number 要取出的元素個數
取偏移量之後的所有元素
${ARRAY[@]:offset}
-
向數組中追加元素:
ARRAY[${#ARRAY[*]}]=value
-
關聯數組:
declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]=‘val1’ [idx_name2]='val2‘…)
注意:關聯數組必須先聲明再調用
練習
- 輸入若幹個數值存入數組中,采用冒泡算法進行升序或降序排序
- 将下圖所示,實作轉置矩陣matrix.sh
1 2 3 1 4 7
4 5 6 ===> 2 5 8
7 8 9 3 6 9
- 列印楊輝三角形
練習總結:
shell函數雖然不支援二維數組,
但是可以将一維數組分成行和列====>就對應成了二維數組
以矩陣的轉置進行示例:<楊輝三角我也以二維數組的方式實作的>
#!/bin/bash
declare -a matrix
declare -i line=3
# 一維數組的索引 index
declare -i index
# 一維數組指派
matrix=(1 2 3 4 5 6 7 8 9)
#for ((i=0;i<9;i++));do
# matrix[$i]=$[i+1]
#done
# 将一維數組變為二維數組j行k列
for((j=0;j<3;j++));do
for ((k=0;k<3;k++));do
let index=j*line+k
echo -e "${matrix[$index]}\t\c"
done
echo
done
echo "=========================="
# 實作轉置
for((j=0;j<3;j++));do
for ((k=0;k<3;k++));do
let index=k*line+j
echo -e "${matrix[$index]}\t\c"
done
echo
done
字元串處理
字元串切片
- ${#var}:傳回字元串變量var的長度
-
${var:offset}:傳回字元串變量var中從第offset個字元後(不包括第offset個字元)的字
符開始,到最後的部分,offset的取值在0 到 ${#var}-1 之間(bash4.2後,允許為負值)
- ${var:offset:number}:傳回字元串變量var中從第offset個字元後(不包括第offset個字元)的字元開始,長度為number的部分
-
${var: -length}:取字元串的最右側幾個字元
注意:冒号後必須有一空白字元
- ${var:offset:-length}:從最左側跳過offset字元,一直向右取到距離最右側lengh個字元之前的内容
-
${var: -length:-offset}:先從最右側向左取到length個字元開始,再向右取到距離最右側offset個字元之間的内容
注意:-length前空格
基于模式取子串
-
${var#*word}:其中word可以是指定的任意字元
功能:自左而右,查找var變量所存儲的字元串中,第一次出現的word, 删除字元串開頭至第一次出現word字元串(含)之間的所有字元
- ${var##*word}:同上,貪婪模式,不同的是,删除的是字元串開頭至最後一次由word指定的字元之間的所有内容
-
${var%word*}:其中word可以是指定的任意字元
功能:自右而左,查找var變量所存儲的字元串中,第一次出現的word, 删除字元串最後一個字元向左至第一次出現word字元串(含)之間的所有字元
- ${var%%word*}:同上,隻不過删除字元串最右側的字元向左至最後一次出現word字元之間的所有字元
+ 典型示例:
url=http://www.magedu.com:80
${url##*:} 80 取端口
${url%%:*} http 取協定
查找替換
- ${var/pattern/substr}:查找var所表示的字元串中,第一次被pattern所比對到的字元串,以substr替換之
- ${var//pattern/substr}: 查找var所表示的字元串中,所有能被pattern所比對到的字元串,以substr替換之
- ${var/#pattern/substr}:查找var所表示的字元串中,行首被pattern所比對到的字元串,以substr替換之
- ${var/%pattern/substr}:查找var所表示的字元串中,行尾被pattern所比對到的字元串,以substr替換之
查找并删除
- ${var/pattern}:删除var表示的字元串中第一次被pattern比對到的字元串
- ${var//pattern}:删除var表示的字元串中所有被pattern比對到的字元串
- ${var/#pattern}:删除var表示的字元串中所有以pattern為行首比對到的字元串
- ${var/%pattern}:删除var所表示的字元串中所有以pattern為行尾所比對到的字元串
字元大小寫轉換
- ${var^^}:把var中的所有小寫字母轉換為大寫
- ${var,,}:把var中的所有大寫字母轉換為小寫
變量指派
-
一個變量有三種情況:<eg:變量var>
①未定義此變量
②定義了此變量但未指派
③定義了此變量并賦予值
- 如果希望變量var的三種情況去影響另一個變量的值時,采用此種方式:
進階變量用法-有類型變量
- Shell變量一般是無類型的,但是bash Shell提供了declare和typeset(已經被declare取代)兩個指令用于指定變量的類型,兩個指令是等價的
-
declare [選項] 變量名
-r 聲明或顯示隻讀變量
-i 将變量定義為整型數
-a 将變量定義為數組
-A 将變量定義為關聯數組
-f 顯示已定義的所有函數名及其内容
-F 僅顯示已定義的所有函數名
-x 聲明或顯示環境變量和函數
-l 聲明變量為小寫字母 declare –l var=UPPER
-u 聲明變量為大寫字母 declare –u var=lower
eval指令
-
eval指令的作用
①首先掃描指令行進行所有的置換
②然後再執行該指令
- 該指令适用于那些一次掃描無法實作其功能的變量,該指令對變量進行兩次掃描
- 比如:引用間接變量等
示例:
[[email protected] ~]# CMD=whoami
[[email protected] ~]# echo $CMD
whoami
[[email protected] ~]# eval $CMD
root
[[email protected] ~]# n=10 [[email protected] ~]# echo {0..$n}
{0..10}
[[email protected] ~]# eval echo {0..$n} 0 1 2 3 4 5 6 7 8 9 10
間接變量引用
- 如果第一個變量的值是第二個變量的名字,從第一個變量引用第二個變量的值就稱為間接變量引用
-
variable1的值是variable2,而variable2又是變量名,variable2的值為value,間接變量引用是指通過variable1獲得變量值value的行為
variable1=variable2
variable2=value
-
bash Shell提供了兩種格式實作間接變量引用
eval tempvar=\$$variable1
tempvar=${!variable1}
[[email protected] data]# var1="i am var1"
[[email protected] data]# echo $var1
i am var1
[[email protected] data]# var2=var1
[[email protected] data]# echo $var2
var1
[[email protected] data]# echo ${!var2}
i am var1
數組指派示例:
declare -i tmp
declare -a num
因為 i 作為數組的索引,是以 i 從 0 開始指派,但是位置參數又是從 1 ($1,$2..)開始的
for ((i=0;i<$#;i++));do
# 間接變量指派方式:
eval tmp=\$$[i+1]
num[$i]=$tmp
done
echo ${num[@]}
建立臨時檔案
- mktemp指令:建立并顯示臨時檔案,可避免沖突
-
mktemp [OPTION]… [TEMPLATE]
TEMPLATE: filenameXXX
X至少要出現三個
-
OPTION:
-d: 建立臨時目錄
-p DIR或–tmpdir=DIR:指明臨時檔案所存放目錄位置
示例:建立臨時目錄5個随機數加上時間秒數,此時檔案名相同的概念應該就很小啦~
~]# mktemp -d /data/test/`date +%F_%T`_XXXXX
/data/test/2019-08-25_15:54:06_XUarv
<預設将建立的檔案名輸出至螢幕是以可儲存至變量,供提示和log使用>
]# ls
2019-08-25_15:54:06_XUarv
安裝複制檔案
-
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 srcfile desfile
install –m 770 –d /testdir/installdir
install 的功能差不多==cp+chown+chmod+mkdir+chgrp
可以修改建立目錄、複制檔案的屬組、屬主和權限
源碼編譯安裝時make install即實作相應的檔案複制,目錄建立
expect介紹
expect 是由Don Libes基于Tcl( Tool Command Language )語言開發的,主要應用于自動化互動式操作的場景,借助 expect 處理互動的指令,可以将互動過如:ssh登入,ftp登入等寫在一個腳本上,使之自動化完成。尤其适用于需要對 多台伺服器執行相同操作的環境中,可以大大提高系統管理人員的工作效率。
expect指令
- expect 文法:
- expect [選項] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
-
選項
-c:從指令行執行expect腳本,預設expect是互動地執行的
-d:可以輸出輸出調試資訊
-
expect中相關指令
spawn 啟動新的程序
send 用于向程序發送字元串
expect 從程序接收字元串
interact 允許使用者互動
exp_continue 比對多個字元串在執行動作後加此指令
expect應用
- expect最常用的文法(tcl語言:模式-動作)
-
單一分支模式文法:
expect “hi” {send “You said hi\n"}
比對到hi後,會輸出“you said hi”,并換行
-
多分支模式文法:
expect “hi” { send “You said hi\n” } \
“hehe” { send “Hehe yourself\n” } \
“bye” { send “Good bye\n” }
-
比對hi,hello,bye任意字元串時,執行相應輸出。等同如下:
expect {
“hi” { send “You said hi\n”}
“hehe” { send “Hehe yourself\n”}
“bye” { send " Good bye\n"}
}