一、函數遞歸
1.1 函數簡介
樣式1:函數間調用
?? ?- 函數體內部調用其他的函數名
樣式2:文件間調用
?? ?- 函數體內部調用另外一個文件的函數名
?? ?- 需要額外做一步文件source的加載動作
?? ?注意:我們將專門提供函數的文件稱為 -- 函數庫
樣式3:函數自調用
?? ?- 函數體內部調用自己的函數名,將復雜的邏輯簡單化
1.2 簡單實踐
需求: 實現數學階乘的實踐
?? ?示例:5的階乘
?? ??? ? 完整格式:5! = 1 * 2 * 3 * 4 * 5 = 120
?? ??? ? 簡寫格式:5! = 5 * (1 * 2 * 3 * 4) = 5 * 4!
?? ?公式: x! = x * (x-1)!
查看腳本內容
[root@localhost ~]# vim a.sh
#!/bin/bash
# 功能:函數自調用實踐# 定制功能函數框架
self_func(){# 接收一個參數num=$1if [ ${num} -eq 1 ];thenecho 1else# 定制一個臨時本地變量,獲取遞減后的值local temp=$[ ${num} - 1 ]# 使用函數自調用方式獲取內容local result=$(self_func $temp)# 格式化輸出信息echo $[ $result * ${num} ]fi
}# 檢測邏輯效果
while true
doread -p "請輸入一個您要查詢的階乘:" valueresult=$(self_func ${value})echo "${value}的階乘是: ${result}"
done
1.3 案例實踐
實踐1-遍歷制定目錄下的所有文件
[root@localhost ~]# vim a.sh
#!/bin/bash
# 功能:掃描目錄下所有文件
# 版本:v0.1
# 作者:書記
# 聯系:# 定制功能函數框架
# 定制目錄掃描功能函數
scan_dir() {# 定制臨時局部功能變量# cur_dir 當前目錄 workdir 工作目錄local cur_dir workdir# 接收要檢查的目錄,進入到目錄中workdir=$1cd ${workdir}# 對工作目錄進行簡單判斷,根目錄沒有父目錄if [ ${workdir} = "/" ]thencur_dir=""elsecur_dir=$(pwd)fi# 查看當前目錄下的文件列表for item in $(ls ${cur_dir})do# 如果文件是目錄,則繼續查看目錄下文件if test -d ${item};thencd ${item}scan_dir ${cur_dir}/${item}cd ..# 如果文件是普通文件,則輸出信息即可elseecho ${cur_dir}/${item}fidone
}# 檢測邏輯效果
while true
doread -p "請輸入一個您要查詢的目錄:" valueif [ -d ${value} ]thenscan_dir ${value}elseecho "您輸入的不是目錄,請重新輸入!"fi
done
二、腳本自動化
2.1?簡介
信號 | 值 | 描述 | 信號 | 值 | 描述 |
---|---|---|---|---|---|
1 | SIGHUP | 掛起進程 | 15 | SIGTERM | 優雅的終止進程 |
2 | SIGINT | 終止進程 | 17 | SIGSTOP | 無條件停止進程,不終止進程 |
3 | SIGQUIT | 停止進程 | 18 | SIGTSTP | 停止或暫停進程,不終止進程 |
9 | SIGKILL | 無條件終止進程 | 19 | SIGCONT | 繼續運行停止的進程 |
? 默認情況下,bash shell會忽略收到的任何SIGQUIT(3)和SIGTERM(15)信號(正因為這樣交互式shell才不會被意外終止)。但是bash shell會處理收到的SIGHUP(1)和SIGINT(2)信號。
? ? 如果bash shell收到SIGHUP信號,它會退出。但在退出之前,它會將信號傳給shell啟動的所有進程(比如shell腳本)。通過SIGINT信號,可以中斷shell,Linux內核停止將CPU的處理時間分配給shell,當這種情況發生時,shell會將SIGINT信號傳給shell啟動的所有進程。
生成信號
終止進程:ctrl+c,
暫停進程:ctrl+z,停止的進程繼續保留在內存中,并能從停止的位置繼續運行
恢復進程:jobs查看運行任務,fg num 重新執行
殺死進程:kill -9 pid
2.2?信號捕捉
簡介
?? ?shell編程提供了一種方式,讓我們可以隨意的控制腳本的運行狀態,這就需要涉及到信號的捕獲操作。在shell編程中,我們可以借助于 trap命令實現指定shell腳本要watch哪些linux信號并從shell中攔截。如果腳本收到了trap命令中列出的信號,它會阻止它被shell處理,而在本地處理。
trap命令格式
命令格式trap commands signals命令示例:# 收到指定信號后,執行自定義指令,而不會執行原操作trap '觸發指令' 信號# 忽略信號的操作trap '' 信號# 恢復原信號的操作trap '-' 信號# 列出自定義信號操作trap -p# 當腳本退出時,執行finish函數trap finish EXIT
簡單實踐
實踐1-捕獲終止信號
查看腳本內容
[root@localhost ~]# vim a.sh
#!/bin/bash
# 功能:腳本信號捕捉# 捕獲關閉信號
trap "echo '你敢關我,就不關,氣死你'" SIGINT SIGTERM
trap "echo '走了,不送'" EXIT# 檢測邏輯效果
while true
doread -p "請輸入一個數據:" valueecho "您輸入的數據是: ${value}"
done
效果:
腳本執行效果
[root@localhost ~]# /bin/bash a.sh
請輸入一個數據:4
您輸入的數據是: 4
請輸入一個數據:^Csignal_trap_test1.sh:行1: 你敢關我,就不關,氣死你: 未找到命令您輸入的數據是:
請輸入一個數據:^Z
[1]+ 已停止 /bin/bash signal_trap_test1.sh
[root@localhost ~]#
[root@localhost ~]# jobs
[1]+ 已停止 /bin/bash signal_trap_test1.sh
[root@localhost ~]# fg 1
/bin/bash signal_trap_test1.sh您輸入的數據是:
請輸入一個數據:3
您輸入的數據是: 3
另開一個終端,直接kill進程
[root@localhost ~]# ps aux | grep sign
root 39142 0.0 0.0 113288 1460 pts/0 S+ 17:43 0:00 /bin/bash signal_trap_test1.sh
[root@localhost ~]# kill -9 39142回到之前的終端查看效果
[root@localhost ~]# fg 1
/bin/bash signal_trap_test1.sh您輸入的數據是:
請輸入一個數據:3
您輸入的數據是: 3
請輸入一個數據:已殺死
實踐2-捕獲正常退出
查看腳本內容
[root@localhost ~]# vim a.sh
#!/bin/bash
# 功能:腳本信號捕捉# 捕獲關閉信號
trap "echo '走了.不送'" EXITvalue="0"
# 檢測邏輯效果
while true
doread -p "請輸入一個數據:" valueif [ ${value} == "9" ]thenexitelseecho "您輸入的數據是: ${value}"fi
done
實踐3-移除捕獲
查看腳本內容
[root@localhost ~]# vim a.sh
#!/bin/bash
# 功能:移除腳本信號捕捉# 捕獲關閉信號
trap "echo '走了.不送'" EXITi=1
# 檢測邏輯效果
while [ $i -le 3 ]
doread -p "請輸入一個數據:" valueif [ ${value} == "9" ]thenexitelseecho "您輸入的數據是: ${value}"filet i+=1
done# 移除捕獲信號
trap - EXIT
echo "移除了捕獲信號"
三、expect基礎
3.1?基礎知識
場景需求
????????在日常工作中,經常會遇到各種重復性的"手工交互"操作,雖然沒有什么技術含量,但是相當的重要。在實際的工作場景中,這種重復性的手工操作動作,非常的繁多,但是對于量大的工作來說,效率就非常低效了。所以我們就需要有一種工具,能夠簡化我們重復的手工操作。
expect簡介
????????expect是一個免費的編程工具,由DonLibes制作,作為Tcl腳本語言的一個擴展,它可以根據程序的提示,模擬標準輸入提供給程序,從而實現自動的交互式任務,而無需人為干預,可以用作Unix系統中進行應用程序的自動化控制和測試的軟件工具。
????????說白了,expect就是一套用來實現自動交互功能的軟件。它主要應用于執行命令和程序時,系統以交互形式要求輸入指定字符串,實現交互通信。在使用的過程中,主要是以腳本文件的樣式來存在
官方網站:
?? ?https://www.nist.gov/services-resources/software/expect
工具手冊:
?? ?man expect
軟件部署
安裝軟件
[root@localhost ~]# yum install expect -y進入專用的命令交互界面
[root@localhost ~]# expect
expect1.1> ls
anaconda-ks.cfg
expect1.2> exit
命令幫助-c: 執行腳本前先執行的命令,可多次使用,多個命令之間使用;隔開-d: debug模式,可以在運行時輸出一些診斷信息,與在腳本開始處使用exp_internal 1相似。-D: 啟用交換調式器,可設一整數參數。-f: 從文件讀取命令,僅用于使用#!時。如果文件名為"-",則從stdin讀取(使用"./-"從文件名為-的文件讀取)。-i: 交互式輸入命令,使用"exit"或"EOF"退出輸入狀態。--: 標示選項結束(如果你需要傳遞與expect選項相似的參數給腳本時),可放到#!行:#!/usr/bin/expect --。-v: 顯示expect版本信息
3.2 簡單實踐
?? ?在進行expect腳本編寫的時候,我們需要記住 -- expect 用的不是我們普通的shell或者python語法,它使用的是Tcl語法。
?? ?Tcl 全稱是 Tool command Language。它是一個基于字符串的命令語言,基礎結構和語法非常簡單,易于學習和掌握。Tcl 語言是一個解釋性語言,所謂解釋性是指不象其他高級語言需要通過編譯和聯結,它象其他 shell 語言一樣,直接對每條語句順次解釋執行。
?? ?Tcl 數據類型簡單。對 Tcl 來說,它要處理的數據只有一種——字符串。Tcl 將變量值以字符串的形式進行存儲,不關心它的實際使用類型。
輸出語法
輸出:tcl使用”puts"關鍵字來作為輸出語句
樣式:puts -nonewline string
屬性解析:
?? ?如果string中間有特殊字符,可以使用 {} 或者 "" 將其作為一個小組,共同輸出
?? ?-nonewline 代表輸出結果的時候,不輸出換行符
?? ?put 和 puts 都可以在命令行使用,但是腳本中,最好用puts
腳本基礎
1 文件名后綴 ??
?? ?.expect 作為標識符
2 文件首行,要指定命令的執行解釋器?
?? ?#!/usr/bin/expect
3 腳本文件的執行
?? ?expect 腳本名
腳本內容示例
[root@localhost ~]# vim a.expect
#!/usr/bin/expect
# 設定一個環境變量
set var nihao
# 輸出環境變量
puts $var腳本執行效果
[root@localhost ~]# expect expect_test.expect
nihao
3.3?語法實踐
賦值語法
????????賦值:tcl 使用“set”關鍵字來定義參數,不必指定變量值的類型,因為變量值的類型僅一種——字符串
樣式:set varName [value]?
注意:
?? ?變量名是由 數字、下劃線、字符組成,數字不能開頭,大小寫敏感。
expect1.7> set a Hello # 設定一個變量名a
Hello
expect1.8> put $a # 使用$ 符號獲取變量名的存儲值
Hello
expect1.9> put "$a" # 使用 "" 方式打印變量的值
Hello
expect1.10> put {$a} # {} 有別于"" 的特點在于原字符輸出
$a
expect1.11> set b $a # 變量的傳遞
Hello
expect1.12> puts $b
Hello
替換語法
替換(解析):- $符號可以實現引用替換,用于引用變量名代替的參數值,但是TCL對嵌套的”$”不于理睬- [] 方括號“[]”完成命令替換。用“[]”將一條命令括起來,命令執行完成后,返回結果
expect1.20> set b [set a 5] # 相當于 set b $a,傳遞賦值
5
expect1.21> puts $b
5
expect1.22> set c [expr 5 * 10 ] # expr是執行系統命令,將計算結果交給c
50
expect1.24> puts $c
50
注意事項
變量的設定
expect1.13> set var value # 設定一個普通變量名
value
expect1.14> puts $var # 獲取變量名的值
value不支持嵌套$
expect1.15> set var1 $$value # TCL不支持嵌套的$
can't read "value": no such variablewhile executing
"set var1 $$value"
expect1.16> set var1 $$var # 由于$var 已經是變量,所以前面的$就無效了
$value
expect1.17> puts $var1
$value原字符輸出
expect1.18> set var2 {$var1} # {} 代表原字符輸出
$var1
expect1.19> puts $var2
$var1
腳本實踐
內置變量
對于tcl來說,它內部包含了大量的內置變量,可以讓我們實現快速的功能操作。
常見的內置變量有:
?? ?argc?? ?指命令行參數的個數。
?? ?argv?? ?指包含命令行參數的列表。
?? ?argv0?? ?是指被解釋的文件或由調用腳本的名稱的文件名。
?? ?env?? ??? ?用于表示是系統環境變量的內容,普通變量我們還是使用$即可
?? ?tcl_version?? ?返回Tcl解釋器的最新版本,注意不是expect的版本號
內置參數實踐
[root@localhost ~]# cat expect_test1.expect
#!/usr/bin/expect
# 查看當前文件傳遞的參數數量
puts "當前文件傳遞的參數數量: $argc"# 查看當前文件傳遞的參數
puts "當前文件傳遞的參數: $argv"# 查看當前文件名稱
puts "當前文件名稱: $argv0"# 獲取變量值
puts "當前系統變量PATH的值是: $env(PATH)"
set key value
puts "普通變量 key 的值是: $key"# 查看版本信息
puts "當前tcl版本信息: $tcl_version"
腳本執行效果
[root@localhost ~]# expect expect_test1.expect
當前文件傳遞的參數數量: 0
當前文件傳遞的參數:
當前文件名稱: expect_test1.expect
當前系統變量PATH的值是: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
普通變量 key 的值是: value
當前tcl版本信息: 8.5
3.4?交互基礎
腳本基礎
命令解釋器
命令解釋器
#!/usr/bin/expect
#!/usr/bin/expect -f 從文件中讀取自動化命令
#!/usr/bin/expect - 如果文件名為 - ,那么從終端輸入中讀取
#!/usr/bin/expect -i 交互式輸入命令
#!/usr/bin/expect -- 腳本傳遞的選項參數和expect選項相似的參數給腳本
注意:#!是對腳本的解釋器程序路徑,腳本的內容是由解釋器解釋的注釋信息
# 被注釋的信息
常見符號
{ }:作用1:保留所有字符原有的意思,而不做解釋,類似于shell中的單引號樣式:set var {"nihao hehehe"}作用2:代碼塊兒,但是兩個 {} 邊界必須在一起。正確樣式:if {代碼塊1 } {代碼塊2}錯誤示例:if {$count < 0}{break;}注意:無論什么時候,{}邊界符號與其他內容都最好有空格隔開,尤其是邊界外的內容
[]:作用:執行命令,類似shell中的 ``(反引號)或者 $()樣式:set count [expr $count - 1 ]注意:在expect 中,沒有小括號的概念和應用
常用命令
set 設定環境變量格式:set 變量名 變量值樣式:set host "192.168.8.12"spawn 啟動新的進程,模擬手工在命令行啟動服務格式:spawn 手工執行命令樣式:spawn ssh python@$hostexpect 接收一個新進程的反饋信息,我們根據進程的反饋,再發送對應的交互命令格式:expect "交互界面用戶輸入處的關鍵字"樣式:expect "*password*"send 接收一個字符串參數,并將該參數發送到新進程。格式:send "用戶輸入的信息" 樣式:send "$password\r"interact 退出自動化交互界面,進入用戶交互狀態,如果需要用戶交互的話,這條命令必須在最后一行格式:interact樣式:interact其他命令exit 退出expect腳本expect eof expect執行內容的結束標識符,退出當前腳本,與interact只能存在一個puts 輸出變量的信息,相當于linux命令中的echowait 退出程序后,等待時間,回收僵尸進程disconnect 斷開一個進程連接,但讓它在后臺繼續運行。exp_continue expect獲取期望后,還會有另外的期望,那么我們就把多個期望連續執行
簡單實踐
實踐1-簡單的登錄交互腳本
查看腳本內容
[root@localhost ~]# cat login_test.expect
#!/usr/bin/expect# 1 設定環境變量
set username python# 2 發起遠程登錄請求
spawn ssh $username@192.168.66.146# 3 識別用戶輸入的位置關鍵字
expect "yes/no"# 4 發送正確的信息
send "yes\r"# 5 識別密碼關鍵字,并傳遞密碼信息
send "\r"
expect "password:"
send "123456\r"# 6 切換回用戶交互界面
interact注意:由于password前面會涉及到一次Enter操作,所以在password匹配前,輸入一次 \r
清理歷史記錄
[root@localhost ~]# rm -f ~/.ssh/known_hosts執行腳本內容
[root@localhost ~]# expect login_test.expect
spawn ssh python@10.0.0.12
The authenticity of host '10.0.0.12 (10.0.0.12)' can't be established.
ECDSA key fingerprint is SHA256:XUJsgk4cTORxdcswxIKBGFgrrqFQzpHmKnRRV6ABMk4.
ECDSA key fingerprint is MD5:71:74:46:50:3f:40:4e:af:ad:d3:0c:de:2c:fc:30:c0.
Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added '10.0.0.12' (ECDSA) to the list of known hosts.
python@10.0.0.12's password:
[python@localhost ~]$ id
uid=1000(python) gid=1000(python) 組=1000(python)
實踐2-腳本結合
expect 除了使用專用的expect腳本來實現特定功能之外,它還可以與其他腳本嵌套在一起進行使用。最常用的結合方式就是 shell結合。
在于shell結合使用的時候,無非就是將expect的執行命令使用 <<-EOF ?。。。 EOF 包裝在一起即可。
樣式:
/usr/bin/expect<<-EOF
spawn ...
...
expect eof?
EOF注意:
?? ?由于expect在shell中是作為一個子部分而存在的,所以,一般情況下,expect結束的時候,使用eof命令表示expect的內容到此結束
查看腳本內容
[root@localhost ~]# cat expect_auto_login.sh
#!/bin/bash
# 功能:shell自動登錄測試
# 版本:v0.1
# 作者:書記
# 聯系:# 定制普通變量
host="$1"
username="$2"
password="$3"/usr/bin/expect <<-EOF
# 發出連接進程
spawn ssh ${username}@${host}# - 正常登陸
expect {"yes/no*" { send "yes\n"; exp_continue }"password:" {send "${password}\n";}
}
puts "測試完畢!!!"
expect eof
EOF
腳本測試效果
[root@localhost ~]# /bin/bash expect_auto_login.sh 10.0.0.12 python 123456
spawn ssh python@10.0.0.12
python@10.0.0.12's password: 測試完畢!!![python@localhost ~]$ exit
[root@localhost ~]#