更多內容請點擊:shell
Linux學習從入門到打死也不放棄,徹底筆記整理(持續更新,求收藏,求點贊~~~~)
編程
https://blog.51cto.com/13683480/2095439centos
第14章,Shell腳本編程進階數組
本章內容:bash
條件判斷服務器
循環ssh
信號捕捉編程語言
函數ide
數組模塊化
高級字符串操做
高級變量
Expect
過程式編程語言執行方式:
順序執行,選擇執行,循環執行
條件選擇-----------------------------------------------------------------------
if語句:
結構: 可嵌套
單分支:
if 判斷條件;then
條件判斷爲真的執行代碼
fi
雙分支:
if 判斷條件;then
條件判斷爲真的執行代碼
(此段若想爲空,使用":")
else
條件判斷爲假的執行代碼
fi
多分支:
if 條件判斷1;then
cmd..
elif 條件判斷2;then
cmd..
elif 條件判斷3;then
cmd..
else
以上條件全爲假的分支代碼
fi
case語句:
格式:
case 變量引用 in
pat1)
分支1
;;
pat2)
分支2
;;
...
*)
默認分支
;;
esac
注意: case支持glob風格的通配符
* 任意長度任意字符
? 任意單個字符
[abc] 指定範圍內的任意單個字符
a|b a或b
循環執行------------------------------------------------------------------------
將某代碼段重複屢次運行
重複運行多少次
循環次數事先已知
循環次數事先未知
有進入條件和退出條件
for,while,until
for循環
格式:
for 變量名 in 列表;do
循環體代碼
done
機制:
依次將列表中的元素賦值給"變量名"
每次賦值後即執行一次循環體,直到列表中的元素耗盡,循環結束
列表生成方式:
1 直接給出列表
2 整數列表
{1..10..2}
$(seq 1 2 10)
3 返回列表的命令
$(cmd)
4 使用glob,如:*.sh
5 變量引用
$@,$*
while循環
格式:
while CONDITION;do
循環體
done
CONDITION :
循環控制條件
進入循環以前,先作一次判斷
每一次循環以後會再次作判斷
條件爲true,則執行一次循環,直到條件測試狀態爲false 終止循環
所以: CONDITION通常應該有循環控制變量,而此變量的值會在循環體不斷的被修正
進入條件:CONDITION爲true
退出條件:CONDITION爲false
until循環
格式:
until CONDITION;do
循環體
done
進入條件:CONDITION 爲 false
退出條件:CONDITION 爲 true
循環控制語句:
用於循環體中
continue [N] 默認爲1
用於循環體中,提早結束第N層的本輪循環,而直接進入下一輪判斷
所在層爲第1層
break [N] 默認爲1
用於循環體中,提早結束第N層循環,所在層爲第1層
shift [N] 默認爲1
用於將參數列表list 左移指定的次數,默認1次
參數列表list 一旦被移動,最左端的那個參數就從列表中刪除。
while循環遍歷位置參數列表時,經常使用到shift
建立無限循環:
1 while true;do
循環體代碼
done
2 until false;do
循環體
done
特殊用法:
while 循環的特殊用法(遍歷文件的每一行)
while read 變量名;do
循環體代碼
done < /file
一次讀取file 文件中的每一行,且將行賦值給變量
PS: 也可使用 cmd |while read;do
循環體
done
可是循環中的數組值貌似不能輸出,即如在done以後echo 數組中的值爲空 ,須要注意
雙小括號方法,即((....))格式,也能夠用於算術運算
也能夠是bash實現C語言風格的變量操做
i=10
((i++))
for 循環 的特殊格式:
for((控制變量初始化;條件判斷表達式;控制標量的修正表達式));do
循環體代碼
done
如: for ((i=1;i<=10;i++));do
echo $i
done
控制變量初始化:
僅在運行到循環代碼段時執行一次
控制變量的修正表達式:
條件判斷表達式:進入循環先作一次條件判斷,true則進入循環
每輪循環結束會先進行控制變量的修正運算,然後在作條件判斷
select循環與菜單:
格式:
select 變量 in 列表;do
循環體命令
done
select 循環主要用於建立菜單,按數字順序排列的菜單項將顯示在標準錯誤上,並顯示
PS3提示符,等待用戶輸入
用戶輸入菜單列表上的某個數字,執行相應的命令
用戶輸入被保存在內置變量REPLY中
PS2: 多行重定向的提示符
PS3: select的提示符
REPLY 保存select的用戶輸入
select與case
select是個無限訓話,所以要記住用break命令退出循環,或用exit命令終止腳本。
也能夠按Ctrl+c退出循環
select 常常和case聯合使用
與for循環相似,能夠省略in list ,此時使用位置參量
trap 信號捕捉:
trap ‘觸發指令’信號
自定義進程收到系統發出的指定信號後,將執行觸發指令,而不會執行原操做
trap ''信號
忽略信號的操做
trap '-' 信號
恢復原信號的操做
trap -p
列出自定義信號操做
注意事項:
信號: 格式能夠是 int INT sigint SIGINT 2
信號捕捉以後,雖然不會馬上結束腳本,可是腳本當前運行的命令卻會被終止
如: trap 'echo trap a sig2' 2
sleep 10000
ping 192.168.0.1
若是捕捉到2信號,不會退出腳本,可是sleep會被打斷,而繼續執行ping命令
函數:------------------------------------------------------------------------------
函數介紹:
函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程
它與shell程序形式上是類似的,不一樣的是它不是一個單獨的進程,不能獨立運行,
而是shell程序的一部分
函數和shell程序區別在於:
shell程序在子shell中運行
而shell函數在當前shell中運行。所以在當前shell中,函數能夠對shell變量
進行修改
定義函數:
函數由兩部分組成:函數名和函數體
help function
語法1:
f_name(){
函數體
}
語法2:
function f_name()
{
函數體
}
語法3:
function f_name{
函數體
}
能夠交互式環境下定義函數,如:
[root@centos6 ~/bin]$func_name1(){
> echo nihao
> echo wohenhao
> ping -c1 -w1 192.168.0.1
> }
[root@centos6 ~/bin]$func_name1
nihao
wohenhao
connect: Network is unreachable
可將函數放在腳本文件中做爲它的一部分
可放在只包含函數的單獨文件中
函數調用:
函數只有被調用纔會執行
函數名出現的地方,會被自動替換爲函數代碼
函數的生命週期:
被調用時建立,返回時終止
在腳本中用戶前必須定義,所以應該將函數定義放在腳本開始部分,直至shell首次
發現它以後才能使用
調用函數僅使用其函數名便可
函數返回值:
函數由兩種返回值:
函數的執行結果返回值:
1 使用echo等命令進行輸出
2 函數體中調用命令的輸出結果
函數的退出狀態碼:
1 默認取決於函數中執行的最後一條命令的退出狀態碼
2 自定義退出狀態碼,return命令
return : 從函數中返回,用最後狀態命令決定返回值
return 0 無錯誤返回
return 1-255 有錯誤返回
刪除函數:
unset func_name1
函數文件:
建立函數文件:
#!/bin/bash
# function.main
f_hello()
{
echo helio
}
f_func2(){
...
}
...
系統自帶函數文件:/etc/rc.d/init.d/functions
使用函數文件:
能夠將進程使用的函數存入函數文件,而後將函數文件載入shell
文件名可任意選取,但最好與相關任務有某種聯繫,如 function.main
通常函數文件載入shell,就能夠在命令行或腳本中調用函數,
可使用set命令查看全部定義的函數,其輸出列表包括已經載入shell的全部函數
若要改動函數,首先用unset命令從shell中刪除函數,改動完畢後,再從新載入此文件
載入函數:
要想使用已建立好的函數文件,要將他載入shell
使用
. /path/filename
source /path/filename
若是使用source 載入函數以後,對函數文件的某個函數作了修改,須要unset函數以後從新載入
unset func_name1
或者exit 從新登陸而後再次source
默認本shell進程有效,如需函數子進程有效,需聲明
export -f func_name
declare -xf
如需查看當前全部的全局函數
export -f 或 declare -xf
例如:
[root@centos6 ~/bin]$export -f func_release
[root@centos6 ~/bin]$export -f
func_release ()
{
declare rel=$(cat /etc/centos-release|tr -dc [0-9]|cut -c1);
echo $rel
}
declare -fx func_release
[root@centos6 ~/bin]$
函數參數:
函數能夠接受參數:
調用函數時,在函數名後面以空白分隔給定參數列表便可;
例如 func_name arg1 arg2 ...
在函數體當中,可使用$1,$2..調用這些參數;還可使用$@,$*,$# 等特殊變量
注意區分腳本的位置參數和傳遞給函數的位置參數
函數變量:
變量做用域:
環境變量: 當前shell和子shell有效
本地變量: 只在當前shell進程有效,爲執行腳本會啓動專用shell進程;
所以,本地變量的做用範圍是當前shell腳本程序文件,包括腳本中的函數
局部變量: 函數的生命週期;函數結束時變量會被自動銷燬
注意: 如函數中的變量名同本地變量,使用局部變量
函數中定義局部變量:
local name=
declare name= declare自帶局部變量屬性
declare -ig 在函數中定義普通變量,centos6不支持
函數遞歸:
函數直接或間接調用自身
注意遞歸層數
函數遞歸示例:
階乘是基斯頓·卡曼於 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
func_factorial()
{
if [ $1 = 0 ];then
echo 1
else
echo $[$1*`func_factorial $[$1-1]`]
fi
}
func_factorial $1
fork×××:
fork×××是一種惡意程序,它的內部是一個不斷fork進程的無限循環,實質是一個簡單
的遞歸程序。因爲程序是遞歸的,若是沒有任何顯示,這會致使整個簡單的程序迅速
耗盡系統全部資源
函數實現:
:(){ :|:&};:
bomb(){ bomb|bomb&};bomb
腳本實現:
cat bomb.sh
#!/bin/bash
./$0|./$0&
多種語言版本
數組---------------------------------------------------------------------------------
變量: 存儲單個元素的內存空間
數組: 存儲多個元素的連續的內存空間,至關於多個變量的集合
數組名和索引:
索引:編號從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]=varl [3]=val2...)
4 交互式數組值對賦值
read -a array_name1
如:
[root@centos7 ~]$read -a array_name1
monday tusday wensday thursday
[root@centos7 ~]$echo ${array_name1[@]}
monday tusday wensday thursday
注意:
若是先賦值單個元素array[0]=a,
再使用賦值所有 array=(b c d) 或者特定賦值 array=([1]=e [2]=f [3]=g)
會使以前單個元素array[0]被覆蓋消失
索引數組能夠無需聲明直接賦值使用
關聯數組必須先聲明以後才能賦值使用
如:[root@centos7 ~]$array3[0]=mage
[root@centos7 ~]$array3[1]=zhangsir
[root@centos7 ~]$echo ${array3[*]}
mage zhangsir
[root@centos7 ~]$echo ${array3[1]}
zhangsir
[root@centos7 ~]$echo ${array3[0]}
mage
[root@centos7 ~]$array4[ceo]=mage
[root@centos7 ~]$array4[cto]=zhangsir
[root@centos7 ~]$echo ${array4[*]}
zhangsir
直接賦值使用關聯數組會賦值失敗,只顯示最後一個值
數組引用:
引用數組元素:
${array_name[index]}
注意:省略[index表示引用下標爲0的元素]
引用數組全部元素
${array_name[@]}
${array_name[*]}
數組的長度(數組中元素的個數)
${#array_name[*]}
${#array_name[@]}
刪除數組中的某元素:致使稀疏格式
unset array[index]
刪除整個數組
unset array
數組數據處理
引用數組中的元素:
數組切片:${array[@]:offset:number}
offset: 要跳過的元素個數
number:要取出的元素個數
${array[0]:offset} 取偏移量以後的全部元素
${array[0]: -n} 取最後n個元素
${array[0]::2} 取開頭2個元素
${array[0]: -m:-n} 跳過最後第n+1個到第m個元素間的全部元素
向數組中追加元素:
array[${#array[*]}]=
關聯數組:
declare -A array_name
array_name=([idx_name1]=val1 [idx_name2]=val2 ...)
字符串處理-------------------------------------------------------------------------
字符串切片:
${#var}: 返回字符串變量var的長度
${var:offset}: 返回字符串變量var中從第off個字符後(不包括第offset個字符)的字符
開始,到最後的部分,offer的取值在0到${#var}-1之間(bash4.2以後容許爲負值)
${var:offset:number}: 返回字符串變量var中第off個以後的num個字符(不包含off)
${var: -n}: 取字符串最右側那個字符(冒號後需加一個空格)
${var: -n:-m}: 取倒數第m+1個字符 到 倒數第n個字符
基於模式取子串:
${var#*word} 其中var爲變量名,不須要加$引用,word能夠是指定的任意字符串
功能:自左而右,查找var變量所存儲的字符串中,第一次出現的word,刪除字符串開頭
至第一次出現word字符之間的全部字符
${var##*word}
貪婪模式,刪除字符串開頭到最後一次」word「指定的字符之間的全部內容
${var%word*} 其中word能夠是指定的任意字符串
功能: 自右邊而左,查找var變量所存儲的字符串中,第一次出現word,刪除字符串最後一個字符
向左至第一次出現word字符之間的全部字符
${var%%word*}
自右而左,刪除至最後一個word所指定的字符串
查找替換:
${var/pattern/substr}:
查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替換之
${var//pattern/substr}:
替換全部能被pattern所匹配到的字符串,以substr替換
${var/#pattern/substr}
行首被pattern匹配,並替換
${var/%pattern/substr}:
行尾被pattern匹配,並替換
查找刪除:
${var/pattern}: 刪除第一次被pattern匹配到的字符串
${var//pattern}: 刪除全部被pattern匹配到的字符串
${var/#pattern}: 刪除pattern爲行首所匹配到的字符串
${var/%pattern}: 刪除pattern爲行尾所匹配到的字符串
字符串大小寫轉換:
${var^^} 把var中全部的小寫字母轉換爲大寫
${var,,} 把var中全部的大寫字母轉換爲小寫
高級變量用法:-------------------------------------------------------------------
變量賦值:
var=${str-expr} str爲變量,expr爲字符串
若是str沒有沒配置,var=expr
若是str配置且爲空,var=
若是str配置且非空,var=$str
var=${str:-expr}
若是str沒有配置或者爲空,var=expr
若是str已配置且非空: var=$str
其餘諸多用法,此處不一一列舉,若有須要查看相關表格查詢
高級變量用法:有類型變量
shell變量通常是無類型的,可是bash shell提供了declare和一樣typeset兩個命令
用於指定變量的類型,兩個命令是等價的
declare:
declare [option] [var]
-r 聲明或顯示只讀變量
-i 整型數
-a 數組
-A 關聯數組
-f 顯示已定義的全部函數名及其內容
-F 僅顯示已定義的全部函數名
-x 聲明或顯示環境變量和函數
-xf 全局函數
-l 聲明變量爲小寫字母
-u 聲明變量爲大寫字母
declare -ig 在函數中定義普通變量,centos6不支持
eval:
eval命令將會首先掃描命令進行全部的置換,而後再執行該命令。該命令適用於那些一次
掃描沒法實現其功能的變量:該命令對變量進行兩次掃描
示例: eval echo {1..$i}
間接變量引用:
若是第一個變量的值是第二個變量的名字,從第一個變量引用第二個變量的值
就稱爲間接變量引用
如:
var1=var2
var2=nnn
bash 提供了兩種格式實現間接變量引用
eval var=\$$var1
var3=${!var1}
如:
var1=var2
var2=nnn
var3=${!var1} 或者 eval var3=\$$var1
echo $var3
nnn
建立臨時文件:---------------------------------------------------------------------
mktemp :
建立並顯示臨時文件,可避免衝突
mktemp [option]..[template]
template: filenameXXX
-d 建立臨時目錄
-p DIR 或 --tmpdir=DIR 指明臨時文件所存放目錄位置
示例:
tmpfile1=`mktemp httptmp-XXX`
tmpdir=`mktemp -d /tmp/roottmp-XXXX`
tmpdir=`mktemp --tmpdir=/tmp roottmp-XXXX`
安裝複製文件:
install 命令:
install [options] source dest 單文件
install [] source dir 單文件
install [] -t dir source 單文件
install [] -d dir 建立空目錄
選項:
-m mode 默認755
-o owner
-g group
示例:
expect介紹-----------------------------------------------------------------------
expect 是由Don Libes 基於Tcl(Tool Command Language)語言開發的
主要應用於自動化交互式操做的場景,藉助expect處理交互的命令,能夠將交互過程
如:ssh登陸,ftp登陸等寫在一個腳本上,使之自動化完成。尤爲適用於須要對多臺
服務器執行相同操做的環境中,能夠大大提供系統管理人員的工做效率
expect命令:
expect [選項] [-c cmds] [[ -f[f|b]] cmdfile ] [args]
選項:
-c 從命令行執行expect腳本,默認expect是交互地執行的
示例:expect -c 'expect "hello" { send "you said hello\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"}
}
expect 示例:
ssh自動鍵入密碼
#!/usr/bin/expect
spawn ssh 192.168.65.132
expect {
"yes/no" { send "yes\n";exp_contunue }
"password" { send "112233\n"; }
}
interact
#expect eof
scp自動鍵入密碼
#!/usr/bin/expect
spawn scp /etc/passwd 192.168.65.132:/data
expect {
"yes/no" { send "yes\n";exp_continue }
"password" {send "112233\n" }
}
expect eof
自動從文件獲取ip地址,且登陸ip地址機器的root帳號,並建立帳號
也能夠不用條用,直接在bash腳本中引用expect代碼
cat ssh.exp
#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd user1\n" }
expect "]#" { send "echo nagedu |passwd --stdin user1\n"}
send "exit\n"
#interact
expect eof
cat sshauto.sh
#!/bin/bash
while read ip;do
user=root
password=112233
ssh.exp $ip $user $password
done < /root/bin/ip.txt
自動從文件獲取ip地址,並scp同一文件到全部主機的root目錄.txt
#!/bin/bash
declare password=112233
while read ip;do
expect <<EOF
spawn scp /root/bin/scpauto.sh $ip:/root
expect {
"password" { send "112233\n" }
}
expect eof
EOF
done < /root/bin/ip.txt
筆記整理完成時間:2018年5月16日20:21:12