函數介紹(function用法)
1、function用法
1、函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程。
2、它與shell程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運 行,而是shell程序的一部分,定義函數只對當前的會話窗口有效,如果再打開一個窗口再定義另外一個函數,就對另一個窗口有效,兩者互不影響。
3、函數和shell程序比較相似,區別在於以下兩種:
(1)Shell程序在子Shell中運行。
(2)而Shell函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改。
2、定義函數
函數由兩部分組成:函數名和函數體
help function 語法一: f_name (){ ...函數體... } 語法二: function f_name { ...函數體... } 語法三: function f_name () { ...函數體... }
可以使用declare -F 選項進行查看所有定義的函數,用unset 加上變量名 就可以刪除定義的變量
3、函數的定義和使用:
1、函數的定義和使用:
(1)可在交互式環境下定義函數
(2)可將函數放在腳本文件中作為它的一部分
(3)可放在只包含函數的單獨文件中
2、調用:函數只有被調用才會執行
調用:給定函數名
函數名出現的地方,會被自動替換為函數代碼
3、函數的生命周期:被調用時創建,返回時終止
4、函數返回值
函數有兩種返回值:
1、函數的執行結果返回值:
(1) 使用echo等命令進行輸出
(2) 函數體中調用命令的輸出結果
2、函數的退出狀態碼:
(1) 默認取決於函數中執行的最后一條命令的退出狀態碼
(2) 自定義退出狀態碼,其格式為:
return 從函數中返回,用最后狀態命令決定返回值:
(1)return 0 無錯誤返回。
(2)return 1-255 有錯誤返回
5、使用函數文件
1、可以將經常使用的函數存入函數文件,然后將函數文件載入shell
2、文件名可任意選取,但最好與相關任務有某種聯系。例如:functions.main
3、一旦函數文件載入shell,就可以在命令行或腳本中調用函數。可以使用set命 令查看所有定義的函數,其輸出列表包括已經載入shell的所有函數
4、若要改動函數,首先用unset命令從shell中刪除函數。改動完畢后,再重新載 入此文件
6、刪除shell函數
1、現在對函數做一些改動后,需要先刪除函數,使其對shell不可用。使用unset命 令完成刪除函數
2、命令格式為: unset function_name
示例: unset findit
再鍵入set命令,函數將不再顯示
3、環境函數
(1)使子進程也可使用
(2)聲明:export -f function_name
(3)查看:export -f 或 declare -xf
7、函數參數
函數可以接受參數: 傳遞參數給函數:調用函數時,在函數名后面以空白分隔給定參數列表即可;
例如“testfunc arg1 arg2 ...”
在函數體中當中,可使用$1, $2, ...調用這些參數;還可以使用$@, $*, $# 等特殊變量
8、函數變量
變量作用域:
環境變量:當前shell和子shell有效
本地變量:只在當前shell進程有效,為執行腳本會啟動專用子shell進程; 因此,本地變量的作用范圍是當前shell腳本程序文件,包括腳本中的函數
局部變量:函數的生命周期;函數結束時變量被自動銷毀
注意:如果函數中有局部變量,如果其名稱同本地變量,使用局部變量
在函數中定義局部變量的方法:local NAME=VALUE
實例1:
第一種寫法:如果命令過多,這行執行不太方便
#!/bin/bash func_os_version () { # 定義一個function函數名為func_os_version,然后在大括號里邊定義命令,取出操作系統的版本號,類似於定義別名一樣 sed -nr 's/.* ([0-9]+)\..*/\1/p' /etc/redhat-release } echo OS version is `func_os_version` # 直接寫上定義函數名稱,或者用echo 加上反向單引號進行輸出結果
查看輸出結果:
[root@centos-7 ~]# bash osversion.sh OS version is 7
第二種寫法:將定義的函數存放到文件中,並將要執行的腳本與定義的函數以及定義函數的文件名進行關聯
[root@centos-7 ~]# cat functions # 將定義的函數放到functions文件中 func_os_version () { sed -nr 's/.* ([0-9]+)\..*/\1/p' /etc/redhat-release } [root@centos-7 ~]# cat osversion.sh # 將要執行腳本的函數名和上面定義函數名的文件進行關聯 #!/bin/bash source functions # source functions是關聯上面的文件 func_os_version # 關聯functions里邊定義的函數名 [root@centos-7 ~]# chmod +x osversion.sh # 對腳本加上執行權限 [root@centos-7 ~]# ./osversion.sh # 查看此時的執行結果即可 7
實例2:
第一步:先定義functions函數文件
[root@centos-7 data]# cat functions # 定義functions函數文件 func_is_digit(){ if [ ! "$1" ];then # 如果輸入的信息不是空,就為真,但又不是數字 echo "Usage:func_is_digit number" # 請輸入數字 return 10 elif [[ $1 =~ ^[[:digit:]]+$ ]];then # 如果輸入是數字 return 0 # return 0 返回的是正確結果,但是不會推出腳本 else echo "Not a digit" # 如果上面都不是,就提醒不是數字 return 1 fi }
第二步:調用functions函數文件,並對不同的成績分段進行判斷
[root@centos-7 data]# cat score.sh #!/bin/bash source /data/functions # 調用指定的函數文件的絕對路徑 read -p "Input your score:" SCORE func_is_digit $SCORE # 直接調用上面的functions文件 if [ $? -ne 0 ];then #判斷上面的命令執行不等於0(不成功)就退出 exit else if [ $SCORE -lt 60 ];then # 如果成功了,對成績的三種判斷如下。 echo "You are loser" elif [ $SCORE -lt 80 ];then echo "soso" else echo "very good" fi fi
實例3
生產中function配合case語法:
#!/bin/bash #Author: liupengju #date: 2020-06-22 #TEL: xxxxxxxxxx #代碼發布與回滾 set -e set -u #adx代碼部署變量定義 ADX_DIR=/gnome/adx adx_new_version="gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar" ADX_NEW_MD5=`md5sum $adx_new_version | awk '{ print $1 }'` #此行需要修改為cf平台的md5碼 ADX_CHK_MD5="43bcfe7594f083a8653126e0896b93ac" #directAd代碼部署變量定義 direct_DIR=/gnome/directAd/ direct_version="direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar" direct_MD5=`md5sum $direct_version | awk '{ print $1 }'` #direct_old_version=$(ls -l |tail -n1 | awk '{print $9}') #此行需要修改為cf平台的md5碼 direct_CHK_MD5="03c3c2fc62b2edfc92e548351010ee9f" ##########部署directAd代碼############################# fun_copy_direct_code(){ mv $direct_DIR/$direct_version $direct_DIR/bak/${direct_version}_$(date +"%F-%T") echo "-----上一個版本已經移動到備份目錄" cp /data/$direct_version $direct_DIR && echo "-----代碼復制成功!!!" } fun_chk_direct_code(){ if [[ "$direct_MD5" == "$direct_CHK_MD5" ]];then echo "-----代碼校驗成功" && echo "代碼部署成功后MD5值為:$direct_MD5" else echo "-----代碼校驗失敗" && exit fi } fun_deploy_direct_restart(){ #$direct_DIR/restart.sh systemctl restart httpd systemctl restart nginx echo "后端服務重啟成功!!!" } fun_chk_direct_port1(){ #驗證端口存活狀態 PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'` PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'` for port in $PORT1 $PORT2;do echo "The port is:$port------監聽端口正常" done } #############回滾direct代碼################################### fun_rollback_direct_code(){ cd $direct_DIR/bak direct_old_version=$(ls -l |tail -n1 | awk '{print $9}') # 提取上一個版本的jar包 mv $direct_DIR/${direct_version} $direct_DIR/bak/${direct_version}_$(date +"%F-%T") mv $direct_DIR/bak/${direct_old_version} $direct_DIR/${direct_version} echo "------舊版本代碼移動成功" direct_old_MD5=$(md5sum $direct_DIR/${direct_version} | awk '{print $1}') echo "代碼回滾后MD5值為:$direct_old_MD5" } fun_rollback_direct_restart(){ #$direct_DIR/restart.sh systemctl restart httpd systemctl restart nginx echo "--------后端服務重啟成功" } fun_chk_direct_port2(){ #驗證端口存活狀態 PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'` PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'` for port in $PORT1 $PORT2;do echo "The port is:$port------端口監聽正常" done } #####################adx代碼部署######################################## fun_copy_adx__code(){ mv $ADX_DIR/$adx_new_version $ADX_DIR/bak/${adx_new_version}_$(date +"%F-%T") echo "-----上一個版本已經移動到備份目錄" cp /data/$adx_new_version $ADX_DIR && echo "-----代碼復制成功!!!" } fun_chk_adx_code(){ if [[ "$ADX_NEW_MD5" == "$ADX_CHK_MD5" ]];then echo "-----代碼校驗成功" && echo "代碼部署成功后MD5值為:$ADX_NEW_MD5" else echo "-----代碼校驗失敗" && exit fi } fun_deploy_adx_restart(){ #$ADX_DIR/restart.sh systemctl restart httpd systemctl restart nginx echo "后端服務已經啟動!!!" } #驗證端口存活狀態 fun_chk_adx_port1(){ PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'` PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'` for port in $PORT1 $PORT2;do echo "The port is:$port------監聽的端口正常啟動" done } ###################################adx代碼回滾########################### fun_rollback_adx_code(){ cd $ADX_DIR/bak adx_old_version=$(ls -l |tail -n1 | awk '{print $9}') mv $ADX_DIR/${adx_new_version} $ADX_DIR/bak/${adx_new_version}_$(date +"%F-%T") mv $ADX_DIR/bak/${adx_old_version} $ADX_DIR/${adx_new_version} echo "------舊版本代碼移動成功" adx_old_MD5=$(md5sum $ADX_DIR/${adx_new_version} | awk '{print $1}') echo "代碼回滾后MD5值為:$adx_old_MD5" } fun_rollback_adx_restart(){ #$ADX_DIR/restart.sh systemctl restart httpd systemctl restart nginx echo "--------后端服務已經啟動" } fun_chk_adx_port2(){ #驗證端口存活狀態 PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'` PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'` for port in $PORT1 $PORT2;do echo "The port is:$port-------端口監聽正常" done } case $1 in direct_deploy) fun_copy_direct_code fun_chk_direct_code fun_deploy_direct_restart fun_chk_direct_port1 ;; direct_rollback) fun_rollback_direct_code fun_rollback_direct_restart fun_chk_direct_port2 ;; adx_deploy) fun_copy_adx__code fun_chk_adx_code fun_deploy_adx_restart fun_chk_adx_port1 ;; adx_rollback) fun_rollback_adx_code fun_rollback_adx_restart fun_chk_adx_port2 ;; esac
實例4:
#!/bin/bash #Auth: liupengju #TEL: xxxxx ########部署完成校驗####### ####驗證adserver版本號############# fun_chk_adx_version(){ ansible adx -m shell -a 'md5sum /gnome/adx/gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar' |awk '{print $1}'|sort |head -n62 |tee version_adx |cat -n adx_version=$(ansible adx -m shell -a 'md5sum /gnome/adx/gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar' |awk '{print $1}'|sort |tail -n62 |uniq -c|awk '{print $2}') echo -e "\e[1;32m新發布的版本號為:$adx_version\e[0m" version1=$(diff metadata version_adx) if [ -z $version1 ];then echo -e "\e[1;32m代碼部署成功 \e[0m" else echo -e "\e[1;31m請檢查錯誤 \e[0m" fi } ####驗證directAd版本號############ fun_chk_direct_version(){ ansible adx -m shell -a ' md5sum /gnome/directAd/direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar'|awk '{print $1}'|sort |head -n62 |tee version_direct |cat -n direct_version=$(ansible adx -m shell -a 'md5sum /gnome/directAd/direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar'|awk '{print $1}'|sort |tail -n62 |uniq -c|awk '{print $2}') echo -e "\e[1;32m新發布的版本號為:$direct_version\e[0m" version2=$(diff metadata version_direct) if [ -z $version2 ];then echo -e "\e[1;32m代碼部署成功 \e[0m" else echo -e "\e[1;31m請檢查錯誤 \e[0m" fi } ###驗證8080端口狀態############### fun_chk_8080_port(){ chk_ip_8080=$(ansible adx -m shell -a ' netstat -ntulp |grep 8080' |awk '{print $1}' |egrep "[0-9]+\.*" |sort | tee data_8080.bak |cat -n) DIR_8080=$(diff metadata data_8080.bak) if [ -z $DIR_8080 ];then echo -e "\e[1;32m端口檢查成功,端口號:8080 \e[0m" else echo -e "\e[1;31m請檢查錯誤 \e[0m" fi } ####驗證8182端口狀態############# fun_chk_8182_port(){ chk_ip_8182=$(ansible adx -m shell -a ' netstat -ntulp |grep 8182' |awk '{print $1}' |egrep "[0-9]+\.*" |sort |tee data_8182.bak |cat -n) DIR_8182=$(diff metadata data_8182.bak) if [ -z $DIR_8182 ];then echo -e "\e[1;32m端口檢查成功,端口號:8182 \e[0m" else echo -e "\e[1;31m請檢查錯誤 \e[0m" fi } case $1 in adx) fun_chk_adx_version fun_chk_8080_port fun_chk_8182_port ;; direct) fun_chk_direct_version fun_chk_8080_port fun_chk_8182_port ;; esac
expect命令
expect 語法:
expect [選項] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
選項
-c:從命令行執行expect腳本,默認expect是交互地執行的
示例:expect -c 'expect "\n" {send "pressed enter\n"}
-d:可以輸出輸出調試信息
示例:expect -d ssh.exp
expect中相關命令 :
spawn:啟動新的進程 send:用於向進程發送字符串 expect:從進程接收字符串 interact:允許用戶交互,並停留在遠程連接的主機上 exp_continue 匹配多個字符串在執行動作后加此命令
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"} }
實例5
#!/usr/bin/expect set ip 192.168.8.100 set user root set password centos set timeout 10 spawn ssh $user@$ip expect { "yes/no" { send "yes\n";exp_continue } "password" { send "$password\n" } } interact
實例6:shell調用腳本expect
#!/bin/bash ip=$1 user=$2 password=$3 expect <<EOF # 開啟expect命令多行重定向 set timeout 20 spawn ssh $user@$ip expect { "yes/no" { send "yes\n";exp_continue } "password" { send "$password\n" } } expect "]#" { send "useradd hehe\n" } expect "]#" { send "echo centos |passwd --stdin hehe\n" } expect "]#" { send "exit\n" } expect eof # 結束語 EOF
實例7:實現批量復制文件
1、先編輯IP地址清單
[root@centos-7 opt]# cat iplist.txt 192.168.7.101 192.168.7.102 192.168.7.103
2、書寫腳本讀取IP地址清單內容
#!/bin/bash while read ip;do user=root password=centos expect <<EOF set timeout 20 spawn ssh $user@$ip expect { "yes/no" { send "yes\n";exp_continue } "password" { send "$password\n" } } expect "]#" { send "useradd hehe\n" } # 遠程ssh登錄后創建用戶名 expect "]#" { send "echo centos |passwd --stdin hehe\n" } # 設置密碼 expect "]#" { send "exit\n" } expect eof EOF done < iplist.txt
實例8:根據不同用戶名和密碼傳遞公鑰,實現免密鑰登錄
1、創建IP地址,密碼清單
[root@centos-7 opt]# cat iplist.txt 192.168.7.101 wangwang 192.168.7.102 centos 192.168.7.103 hahahaha
2、通過while實現批量讀取文件內容
#!/bin/bash ssh-keygen -t rsa -P "" -f /root/.ssh/id_rsa while read ip password;do user=root set timeout 10 expect << EOF spawn ssh-copy-id -i /root/.ssh/id_rsa.pub $user@$ip expect { "yes/no" { send "yes\n";exp_continue } "password" { send "$password\n" } } expect eof EOF done <iplist.txt