1 基礎(chǔ)
1.1 程序組成
- 程序:算法+數(shù)據(jù)結(jié)構(gòu)
- 數(shù)據(jù):是程序的核心
- 算法:處理數(shù)據(jù)的方式
- 數(shù)據(jù)結(jié)構(gòu): 數(shù)據(jù)在計(jì)算機(jī)中的類型和組織方式
1.2 程序編程風(fēng)格
面向過(guò)程語(yǔ)言
- 做一件事情,排出個(gè)步驟,第一步干什么,第二步干什么,如果出現(xiàn)情況A,做什么處理,如果出現(xiàn)了情況B,做什么處理
- 問(wèn)題規(guī)模小,可以步驟化,按部就班處理
- 以指令為中心,數(shù)據(jù)服務(wù)于指令
- C,shell
面向?qū)ο笳Z(yǔ)言
- 一種認(rèn)識(shí)世界、分析世界的方法論。將萬(wàn)事萬(wàn)物抽象為各種對(duì)象
- 類是抽象的概念,是萬(wàn)事萬(wàn)物的抽象,是一類事物的共同特征的集合
- 對(duì)象是類的具象,是一個(gè)實(shí)體
- 問(wèn)題規(guī)模大,復(fù)雜系統(tǒng)
- 以數(shù)據(jù)為中心,指令服務(wù)于數(shù)據(jù)
- java,C#,python,golang等
1.3 編程語(yǔ)言
計(jì)算機(jī):運(yùn)行二進(jìn)制指令
編程語(yǔ)言:人與計(jì)算機(jī)之間交互的語(yǔ)言。分為兩種:低級(jí)語(yǔ)言和高級(jí)語(yǔ)言
-
低級(jí)編程語(yǔ)言:
-
機(jī)器:二進(jìn)制的0和1的序列,稱為機(jī)器指令。與自然語(yǔ)言差異太大,難懂、難寫(xiě)
-
匯編:用一些助記符號(hào)替代機(jī)器指令,稱為匯編語(yǔ)言
? 如:ADDAB 將寄存器A的數(shù)與寄存器B的數(shù)相加得到的數(shù)放到寄存器A中
? 匯編語(yǔ)言寫(xiě)好的程序需要匯編程序轉(zhuǎn)換成機(jī)器指令
? 匯編語(yǔ)言稍微好理解,即機(jī)器指令對(duì)應(yīng)的助記符,助記符更接近自然語(yǔ)言
-
-
高級(jí)編程語(yǔ)言:
- 編譯:高級(jí)語(yǔ)言–>編譯器–>機(jī)器代碼文件->執(zhí)行,如: C,C++
- 解釋:高級(jí)語(yǔ)言–>執(zhí)行–>解釋器–>機(jī)器代碼,如: shell,python,php,JavaScript,perl
編譯和解釋型語(yǔ)言
1.4 編程邏輯處理方式
三種處理邏輯
- 順序執(zhí)行
- 選擇執(zhí)行
- 循環(huán)執(zhí)行
2 shell 腳本語(yǔ)言的基本結(jié)構(gòu)
2.1 shell腳本的用途
- 自動(dòng)化常用命令
- 執(zhí)行系統(tǒng)管理和故障排排除
- 創(chuàng)建簡(jiǎn)單的應(yīng)用程序
- 處理文本或文件
2.2 shell腳本基本結(jié)構(gòu)
shell腳本編程:是基于過(guò)程式、解釋執(zhí)行的語(yǔ)言
編程語(yǔ)言的基本結(jié)構(gòu):
- ? 各種系統(tǒng)命令的組合
- ? 數(shù)據(jù)存儲(chǔ):變量、數(shù)組
- ? 表達(dá)式:a+b
- ? 控制語(yǔ)句: if
shell腳本:包含一些命令或聲明,并符合一定格式的文本文件
格式要求:首行shebang機(jī)制
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
2.3 創(chuàng)建shell腳本過(guò)程
第一步:使用文本編輯器來(lái)創(chuàng)建文本文件
- 第一行必須包括shell聲明序列: #!
- 示例:
- #!/bin/bash
- 添加注釋,注釋以#開(kāi)頭
第二步:加執(zhí)行權(quán)限
- 給予執(zhí)行權(quán)限,在命令行上指定腳本的絕對(duì)或相對(duì)路徑
第三步:運(yùn)行腳本
- 直接運(yùn)行解釋器,將腳本作為解釋器程序的參數(shù)運(yùn)行
autocmd BufNewFile *.sh exec ":cal SetTitle()"
func SetTitle()
if expand("%:e") == 'sh'
call setline(1,"#!/bin/bash")
call setline(2,"#")
call setline(3,"#*****************************************************")
call setline(4,"#Author: minNexus")
call setline(5,"#QQ: 1299575088")
call setline(6,"#Date: ".strftime("%Y-%m-%d"))
call setline(7,"#FileName: .expand("%"))
call setline(8,"#URL: https://blog.csdn.net/qq_24472227?type=blog")
call setline(9,"#Description: The test script")
call setline(10,"#Copyright (C): ".strftime("%Y")," All rights reserved")
call Setline(11,"#****************************************************")
call setline(12,"")
endif
endfunc
autocmd BufNewFile * normal G
2.4 腳本注釋規(guī)范
-
第一行一般為調(diào)用使用的語(yǔ)言
-
程序名,避免更改文件名為無(wú)法找到正確的文件
-
版本號(hào)
-
更改后的時(shí)間
-
作者相關(guān)信息
-
該程序的作用,及注意事項(xiàng)
-
最后是各版本的更新簡(jiǎn)要說(shuō)明
2.5 第一個(gè)腳本
[root@CentOS8 ~]#vim hello.sh
輸入echo hello world
[root@CentOS8 ~]#./hello.sh 執(zhí)行hello.sh
hello world
2.6 腳本調(diào)試
-
語(yǔ)法錯(cuò)誤:導(dǎo)致后續(xù)命令不再執(zhí)行
-
命令錯(cuò)誤:后續(xù)命令仍然執(zhí)行
-
邏輯錯(cuò)誤
檢測(cè)腳本中的語(yǔ)法錯(cuò)誤,bash后加-n,提示出錯(cuò)行不一定準(zhǔn)確,不執(zhí)行
bash -n /path/to/some_script
一定程度上檢查邏輯出錯(cuò) 與 命令出錯(cuò),調(diào)試并執(zhí)行,加-x,逐行執(zhí)行并顯示
bash -x /path/to/some_script
注意:
- bash ScriptName 開(kāi)啟一個(gè)子進(jìn)程,在里面執(zhí)行腳本
- source ScriptName 在當(dāng)前進(jìn)程執(zhí)行腳本,不開(kāi)啟子進(jìn)程,如果在其中修改了變量,則修改了當(dāng)前shell的變量,不推薦使用,除非配置文件
2.7 變量
2.7.1 變量
變量表示命名的內(nèi)存空間,將數(shù)據(jù)放在內(nèi)存空間中,通過(guò)變量名引用,獲取數(shù)據(jù)
2.7.2 變量類型
類型:
- 內(nèi)置變量,如: PS1,PATH,UID,HOSTNAME,PPID,$ , , ,?,BASHPID,HISTSIZE(使用$查看)
- 用戶自定義變量
不同的變量存放的數(shù)據(jù)不同,決定了以下
- 數(shù)據(jù)存儲(chǔ)方式
- 參與的運(yùn)算
- 表示的數(shù)據(jù)范圍
變量數(shù)據(jù)類型:
- 字符
- 數(shù)值:整型、浮點(diǎn)型 (bash不支持浮點(diǎn)型)
2.7.3 編程語(yǔ)言分類
靜態(tài)和動(dòng)態(tài)語(yǔ)言
- 靜態(tài)編譯語(yǔ)言:使用變量前,先聲明變量類型,之后類型不能改變,在編譯時(shí)檢查,如:java,c
- 動(dòng)態(tài)編譯語(yǔ)言:不用事先聲明,可隨時(shí)改變類型,如: bash,Python
強(qiáng)類型和弱類型語(yǔ)言
- 強(qiáng)類型語(yǔ)言: 不同類型數(shù)據(jù)操作,必須經(jīng)過(guò)強(qiáng)制轉(zhuǎn)換才同一類型才能運(yùn)算,如java , c#,python
? 如:以下python代碼
print('magedu'+ 10) 提示出錯(cuò),不會(huì)自動(dòng)轉(zhuǎn)換類型
print('magedu'+str(10)) 結(jié)果為magedu10,需要顯示轉(zhuǎn)換類型
-
弱類型語(yǔ)言:語(yǔ)言的運(yùn)行時(shí)會(huì)隱式類型轉(zhuǎn)換。無(wú)須指定類型,默認(rèn)均為字符型,參與運(yùn)算會(huì)自動(dòng)進(jìn)行隱式類型轉(zhuǎn)換;變量無(wú)須事先定義可直接調(diào)用
如: bash ,php,javascript
2.7.4 shell中變量命名法則
不能使程序中的保留字:如: if,for
只能使用數(shù)字、字母及下劃線,且不能以數(shù)字開(kāi)頭,注意:不支持短橫線“.",和主機(jī)名相反
見(jiàn)名知義,用英文單詞命名,并體現(xiàn)出實(shí)際作用,不要用簡(jiǎn)寫(xiě),如: ATM
統(tǒng)一命名規(guī)則:駝峰命名法studentname,大駝峰StudentName ,小駝峰studentName
- 變量名大寫(xiě):STUDENTNAME
- 局部變量小寫(xiě)
- 函數(shù)名小寫(xiě)
2.7.5 變量定義和引用
變量的生效范圍等標(biāo)準(zhǔn)劃分變量類型
- 普通變量:生效范圍為當(dāng)前shel進(jìn)程;對(duì)當(dāng)前shell之外的其它shell進(jìn)程,包括當(dāng)前shell的子shell進(jìn)程均無(wú)效
- 環(huán)境變量:生效范圍為當(dāng)前shell進(jìn)程及其子進(jìn)程
- 本地變量:生效范圍為當(dāng)前shell進(jìn)程中某代碼片斷,通常指函數(shù)
變量賦值:
name='value'
value可以是以下多種形式
- 直接字串: name=‘root’
- 變量引用: name=“$USER”
- 命令引用: name=
COMMAND
或者 name=$(COMMAND)
注意:變量賦值臨時(shí)生效,終端退出將自動(dòng)刪除,不過(guò)為了安全起見(jiàn)最好使用完手動(dòng)刪除。腳本中的變量也隨腳本結(jié)束自動(dòng)刪除
變量引用:
$name
${name}
弱引用和強(qiáng)引用
"$name" 雙引號(hào),弱引用,其中的變量引用會(huì)被替換為變量值
'$name' 單引號(hào),強(qiáng)引用,其中的變量引用不會(huì)被替換為變量值,而保持原字符串
顯示已定義的所有變量
set
刪除變量
unset name
例:
1:
[root@CentOS8 ~]#ls
f1.txt f2.txt f3.txt issue.bak passwd scripts test.img
[root@CentOS8 ~]#FILE=* 將當(dāng)前文件夾所有文件名保存至變量FILE中
[root@CentOS8 ~]#echo $FILE
f1.txt f2.txt f3.txt issue.bak passwd scripts test.img
2:
[root@CentOS8 ~]#FILE="i am $HOSTNAME" 將hostname的變量名存入變量FILE中
[root@CentOS8 ~]#echo $FILE
i am CentOS8.Joyce.person1
3:
[root@CentOS8 ~]#seq 10
1
2
3
4
5
6
7
8
9
10
[root@CentOS8 ~]#NUM=`seq 10`
[root@CentOS8 ~]#echo $NUM
1 2 3 4 5 6 7 8 9 10
[root@CentOS8 ~]#echo "$NUM" 加雙引號(hào)保留多行格式
1
2
3
4
5
6
7
8
9
10
2.7.6 環(huán)境變量
環(huán)境變量:可以使子進(jìn)程(包括子子進(jìn)程)繼承父進(jìn)程的變量,但是無(wú)法讓父進(jìn)程使用子進(jìn)程的變量
一旦子進(jìn)程修改從父進(jìn)程繼承的變量,新的值將傳遞給子子進(jìn)程
一般只在系統(tǒng)配置文件中使用,在腳本中較少使用
聲明環(huán)境變量 并賦值
export name=VALUE
declare -x name=VALUE
或分2步
name=VALUE
export name
變量引用
$name
${name}
例:
[root@CentOS8 ~]#NAME=joyce
[root@CentOS8 ~]#AGE=21
[root@CentOS8 ~]#echo $NAME$AGE
joyce21
[root@CentOS8 ~]#echo $NAME_$AGE
21
[root@CentOS8 ~]#echo ${NAME}_$AGE
joyce_21
顯示所有環(huán)境變量
env
printenv
export
declare -x
刪除變量:
unset name
bash內(nèi)建的環(huán)境變量
PATH
SHELL
USER
UID
HOME
PWD
SHLVL 當(dāng)前所在shell的深度,使用bash進(jìn)入下一shell(開(kāi)子進(jìn)程)后,深度+1,最低是1
LANG 當(dāng)前設(shè)置語(yǔ)言與編碼格式
MAIL 郵箱位置
HOSTNAME
HISTSIZE
_ 下劃線,表示前一命令的最后一個(gè)參數(shù),前面記得加$
腳本:顯示系統(tǒng)信息
RED="\E[1;31m"
GREEN="\E[1;32m"
END="\E[0m"
echo -e "\E[1;32m--------------------------------Host systeminfo------------------------------------$END"
echo -e "HOSTNAME: $RED`hostname`$END"
echo -e "IPADDR: $RED` ifconfig ens160|grep -Eo '([0-9]{1,3}\.){3}[0-9][1,3]' |head -n1`$END"
echo -e "OSVERSION: $RED`cat /etc/redhat-release`$END"
echo -e "KERNEL: $RED`uname -r`$END"
echo -e "CPU: $RED`lscpu|grep 'Model name'|tr -s ' '|cut -d : -f2`$END"
echo -e "MEMORY: $RED`free -h|grep Mem|tr -s ' ' : |cut -d : -f2`$END"
echo -e "DISK: $RED`lsblk |grep '^sd' |tr -s ' ' |cut -d " " -f4`$END"
echo -e "\E[1;32m-----------------------------------------------------------------------------------$END"
2.7.7 只讀變量
只讀變量:只能聲明定義,但后續(xù)不能修改和刪除,即常量,退出終端自動(dòng)刪除
聲明只讀變量: 加-r
readonly name
declare -r name
查看只讀變量:
readonly [-p]
declare -r
2.7.8 位置變量
位置變量:在bash shell中內(nèi)置的變量,在腳本代碼中調(diào)用通過(guò)命令行傳遞給腳本的參數(shù)
$1,$2,... 對(duì)應(yīng)第1個(gè)、第2個(gè)等參數(shù),shift [n]換位置
$0 命令本身,包括路徑,想只顯示名字可以寫(xiě)`basename $0`
$* 傳遞給腳本的所有參數(shù),全部參數(shù)合為一個(gè)字符串
$@ 傳遞給腳本的所有參數(shù),每個(gè)參數(shù)為獨(dú)立字符串
$# 傳遞給腳本的參數(shù)的個(gè)數(shù)
注意: $@ $ 只在被雙引號(hào)包起來(lái)的時(shí)候才會(huì)有差異*
清空所有位置變量
set --
例:
[root@CentOS8 ~]#cat arg.sh 查看arg.sh文件
#!/bin/bash
#
#*****************************************************************************
#Author: minNexus
#QQ: 1299575088
#Date: 2023-07-25
#FileName: arg.sh
#URL: https://blog.csdn.net/qq_24472227?type=blog
#Description: The test script
#Copyright (C): 2023 All rights reserved
#****************************************************************************
echo "lst arg is $l"
echo "2st arg is $2"
echo "3st arg is $3"
echo "10st arg is ${l0}"
echo "1lst arg is ${11}"
echo "The number of arg is $#"
echo "All args are $*"
echo "All args are $@"
echo "The scriptname is `basename $0`"
[root@CentOS8 ~]#bash arg.sh {a..z} 運(yùn)行腳本,加上a..z參數(shù)
lst arg is
2st arg is b
3st arg is c
10st arg is
1lst arg is k
The number of arg is 26
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
All args are a b c d e f g h i j k l m n o p q r s t u v w x y z
The scriptname is arg.sh
2.7.9 退出狀態(tài)碼變量
進(jìn)程執(zhí)行后,將使用變量 ? 保存狀態(tài)碼的相關(guān)數(shù)字,不同的值反應(yīng)成功或失敗,$?取值范例 0-255
- $?的值為0 代表成功
- $?的值是1到255 代表失敗
例:
[root@CentOS8 ~]#curl www.google.com &> /dev/null
[root@CentOS8 ~]#echo $?
7
用戶可以在腳本中使用以下命令自定義退出狀態(tài)碼
exit [n]
注意:
- 腳本中一旦遇到exit命令,腳本會(huì)立即終止,終止退出狀態(tài)取決于exit命令后面的數(shù)字
- 如果未給腳本指定退出狀態(tài)碼,整個(gè)腳本的退出狀態(tài)碼取決于腳本中執(zhí)行的最后一條命令的狀態(tài)碼
2.7.10 展開(kāi)命令行
展開(kāi)命令執(zhí)行順序:
把命令行分成單個(gè)命令詞
展開(kāi)別名
展開(kāi)大括號(hào)的聲明{}
展開(kāi)波浪符聲明 ~
命令替換$()和``
再次把命令行分成命令詞
展開(kāi)文件通配* ? [abc]等等
準(zhǔn)備I/0重導(dǎo)向 <、>
運(yùn)行命令
防止擴(kuò)展:
反斜線(\)會(huì)使隨后的字符按原意解釋
加引號(hào)來(lái)防止擴(kuò)展:
單引號(hào)('')-防止所有擴(kuò)展
雙引號(hào) ("")也可防止擴(kuò)展,但是以下情況例外: $(美元符號(hào))
變量擴(kuò)展:
`` 反引號(hào),命令替換
\ 反斜線,禁止單個(gè)字符擴(kuò)展
! 嘆號(hào),歷史命令替換
2.7.11 腳本安全和 set 命令
**set命令:**可以用來(lái)定制shell環(huán)境
$- 變量
h hashal,打開(kāi)洗項(xiàng)后,Shell會(huì)將命令所在的路徑hash下來(lái),避免每次都要查詢。通過(guò)set +h將h洗項(xiàng)關(guān)閉
i interactive-comments,包含這個(gè)選項(xiàng)說(shuō)明當(dāng)前的 shel 是一個(gè)交式的 shel。所謂的交式shell,在腳本中i選項(xiàng)是關(guān)閉的
m monitor,打開(kāi)監(jiān)控模式,就可以通過(guò)Job control來(lái)控制進(jìn)程的停止、繼續(xù),后臺(tái)或者前臺(tái)執(zhí)行等
B braceexpand,大括號(hào){}擴(kuò)展,set +B關(guān)閉{}的功能
H history,H選項(xiàng)打開(kāi),可以展開(kāi)歷史列表中的命令,可以通過(guò)!感嘆號(hào)來(lái)完成,例如“!"返回上最近的一個(gè)歷史命令,“!n”返回第 n個(gè)歷史命令
set 命今實(shí)現(xiàn)腳本安全 推薦打開(kāi)-eu
-u 在擴(kuò)展一個(gè)沒(méi)有設(shè)置的變量時(shí),顯示錯(cuò)誤信息,等同set -o nounset,避免出現(xiàn)刪除整個(gè)目錄
-e 如果一個(gè)命令返回一個(gè)非0退出狀態(tài)值(失敗)就退出,等同set -o errexit
-o option 顯示,打開(kāi)或者關(guān)閉選項(xiàng)
顯示選項(xiàng) set-o
打開(kāi)選項(xiàng) set -o選項(xiàng)
關(guān)閉選項(xiàng) set +o選項(xiàng)
-x 當(dāng)執(zhí)行命令時(shí),打印命令及其參數(shù),類似bash -x
2.8 格式化輸出 printf
格式
printf "指定的格式" "文本1" "文本2"...
常用格式替換符
%s 字符串
%f 浮點(diǎn)格式
%b 相對(duì)應(yīng)的參數(shù)中包含轉(zhuǎn)義字符時(shí),可以使用此替換符進(jìn)行替換,對(duì)應(yīng)的轉(zhuǎn)義字符會(huì)被轉(zhuǎn)義
%c ASCII字符,即顯示對(duì)應(yīng)參數(shù)的第一個(gè)字符
%d,%i 十進(jìn)制整數(shù)
%o 八進(jìn)制值
%u 不帶正負(fù)號(hào)的十進(jìn)制值
%x 十六進(jìn)制值 (a-f)
%X 十六進(jìn)制值 (A-F)
%%
說(shuō)明:%s中的數(shù)字代表此替換符中的輸出字符寬度,不足補(bǔ)空格,默認(rèn)是右對(duì)齊,%-10s表示10個(gè)字符寬,- 表示左對(duì)齊
常用轉(zhuǎn)義字符:
\a 警告字符,通常為ASCII的BEL字符
\b 后退
\f 換頁(yè)
\n 換行
\r 回車
\t 水平制表符
\v 垂直制表符
\ 表示\本身
例:
[root@CentOS8 ~]#printf "(%.3f)\n" 1 2 3
(1.000)
(2.000)
(3.000)
[root@CentOS8 ~]#printf "%s\n%s" 1 2 3 4;echo
1
23
4
2.9 算術(shù)運(yùn)算
shell 支持算術(shù)運(yùn)算,但只支持整數(shù),不支持小數(shù)
bash中的算術(shù)運(yùn)算:
+
-
*
/
% 取模,即取余數(shù),%5的結(jié)果在0-4
** 乘方
乘法符號(hào)有些場(chǎng)景中需要轉(zhuǎn)義
實(shí)現(xiàn)算術(shù)運(yùn)算:
let var=算術(shù)表達(dá)式
var=$[算術(shù)表達(dá)式]
var=$((算術(shù)表達(dá)式))
var=$(expr argl arg2 arg3 ...) 注意:expr里*可能被當(dāng)作通配符,乘法要用\*
declare -i var = 數(shù)值
echo'算術(shù)表達(dá)式'|bc
例:
[root@CentOS8 ~]#echo -e "\E[1;$[RANDOM%7+31]mhello\e[0m" 打印隨機(jī)顏色的hello,顏色編號(hào)31-37
hello
[root@CentOS8 ~]#expr 99 \* 3
297
[root@CentOS8 ~]#echo "scale=3;3/7"|bc
.428
內(nèi)建的隨機(jī)數(shù)生成器變量
$RANDOM 取值范圍 0-32767
增強(qiáng)型賦值:
+= i+=10 相當(dāng)于 i=i+10
-= i-=j 相當(dāng)于 i=i-j
*=
/=
%=
++ i++,++i 相當(dāng)于 i=i+1
-- i--,--i 相當(dāng)于 i=i-1
2.10 邏輯運(yùn)算
true false
1 0
與:& 與0為0,與1保留
1與1=1
1與0=0
0與1=0
0與0=0
或:| 同0為0
1或1=1
1或0=1
0或1=1
0或0=0
非:!
!1 =0 ! true
!0 =1 ! false
異或:^ 異或的兩個(gè)值,相同為,假0;不同為真,非0。
1^1=0
1^0=1
0^1=1
0^0=0
兩個(gè)數(shù)字異或,將結(jié)果與任意一個(gè)數(shù)字異或,結(jié)果是另一數(shù)字
12^8=4
8^4=12
12^4=8
例:
[root@CentOS8 ~]#i=i=10;j=20;echo i=$[i^(i^j)] j=$[j^(i^j)] 使用異或交換i和j的值
i=20 j=10
[root@CentOS8 ~]#i=10;j=20;i=$[i^j];j=$[i^j];i=$[i^j];echo i=$i,j=$j
i=20,j=10
短路運(yùn)算
短路與
CMD1 短路與 CMD2
- 第一個(gè)CMD1結(jié)果為0(假),總的結(jié)果必定為0,因此不需要執(zhí)行CMD2
- 第一個(gè)CMD1結(jié)果為1(真),第二個(gè)CMD2必須要參與運(yùn)算,才能得到最終的結(jié)果
短路或
CMD1 短路或CMD2
- 第一個(gè)CMD1結(jié)果為1(真),總的結(jié)果必定為1,因此不需要執(zhí)行CMD2
- 第一個(gè)CMD1結(jié)果為0(假),第二個(gè)CMD2必須要參與運(yùn)算,才能得到最終的結(jié)果
2.11 條件測(cè)試命令
條件測(cè)試:判斷某需求是否滿足,需要由測(cè)試機(jī)制來(lái)實(shí)現(xiàn),專用的測(cè)試表達(dá)式需要由測(cè)試命令輔助完成測(cè)試過(guò)程,實(shí)現(xiàn)評(píng)估布爾聲明,以便用在條件性環(huán)境下進(jìn)行執(zhí)行
- 若真,則狀態(tài)碼變量 $? 返回0
- 若假,則狀態(tài)碼變量 $? 返回1
條件測(cè)試命令
test EXPRESSION 等價(jià)于 [ EXPRESSION ] 推薦使用[ ]
[[ EXPRESSION ]]
注意:EXPRESSION前后必須有空白字符
2.11.1 變量測(cè)試
-v VAR 變量VAR是否設(shè)置,即是否存在
-R VAR 變量VAR是否設(shè)置并引用
示例:判斷NAME變量是否定義
[root@CentOS8 ~]#[ -v name ] 或 test -v NAME
[root@CentOS8 ~]#echo $?
1
2.11.2 數(shù)值測(cè)試
-eq 是否等于
-ne 是否不等于
-gt 是否大于
-ge 是否大于等于
-lt 是否小于
-le 是否小于等于
例:
[root@CentOS8 ~]#x=33
[root@CentOS8 ~]#y=44
[root@CentOS8 ~]#[ $x -gt $y ] 必須假$
[root@CentOS8 ~]#echo $?
1
[root@CentOS8 ~]#[ $x -lt $y ]
[root@CentOS8 ~]#echo $?
0
2.11.3 字符串測(cè)試
-z "STRING" 字符串是否為空,未定義或空為真,不空為假
-n "STRING" 字符串是否不空,不空為真,空為假
= 是否等于
!= 是否不等于
? ascii碼是否大于ascii碼
< 是否小于
== 左側(cè)字符串是否和右側(cè)的PATTERN相同
注意:此表達(dá)式用于[[ ]]中,PATTERN為通配符
=~ 左側(cè)字符串是否能夠被右側(cè)的正則表達(dá)式的PATTERN所匹配
注意: 此表達(dá)式用于[[ ]]中;擴(kuò)展的正則表達(dá)式
- 即只在正則表達(dá)式才用雙中括號(hào)[[]],其他都用[]
- 且在[[ == ]] 右側(cè)的*,單列則表示通配符,只作為字符需要加""或\轉(zhuǎn)義
- 字符串最好兩邊加"",否則可能出現(xiàn)問(wèn)題
[root@CentOS8 ~]#name="hello babe"
[root@CentOS8 ~]#[ $name ]
-bash: [: hello: unary operator expected
[root@CentOS8 ~]#[ "$name" ]
[root@CentOS8 ~]#echo $?
0
例:
[root@CentOS8 ~]#title1=ceo
[root@CentOS8 ~]#title1=cto
[root@CentOS8 ~]#[ $title1 = $title2 ] 字符串之間必須加空格 ,不加空格則成賦值
[root@CentOS8 ~]#echo $?
1
2通配符
[root@CentOS8 ~]#FILE=test.log
[root@CentOS8 ~]#[[ "$FILE == *.log" ]]
[root@CentOS8 ~]#echo $?
0
3正則表達(dá)式
[root@CentOS8 ~]#FILE=test.log
[root@CentOS8 ~]#[[ "$FILE" =~ \.log$ ]]
[root@CentOS8 ~]#echo $?
0
[root@CentOS8 ~]#IP=1.2.3.444 判斷ip地址是否合法(在255.255.255.255之間)
[root@CentOS8 ~]#[[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]
[root@CentOS8 ~]#echo $?
1
[root@CentOS8 ~]#IP=1.2.3.4
[root@CentOS8 ~]#echo $?
0
2.11.4 文件測(cè)試
存在性測(cè)試
-a FILE 同 -e
-e FILE 文件存在性測(cè)試,存在為真,否則為假
-b FILE 是否存在且為塊設(shè)備文件
-C FILE 是否存在且為字符設(shè)備文件
-d FILE 是否存在且為目錄文件
-f FILE 是否存在且為普通文件,判斷軟鏈接則判斷軟鏈接指定的文件
-h FILE 或 -L FILE: 存在且為符號(hào)鏈接文件
-p FILE 是否存在且為命名管道文件
-S FILE 是否存在且為套接字文件
例:
[root@CentOS8 ~]#[[ -c /dev/zero ]] 判斷是否為字符文件
[root@CentOS8 ~]#echo $?
0
文件權(quán)限測(cè)試.
-r FILE 是否存在且可讀
-W FILE 是否存在且可寫(xiě)
-X FILE 是否存在且可執(zhí)行
-u FILE 是否存在且擁有suid權(quán)限
-g FILE 是否存在且擁有sgid權(quán)限
-k FILE 是否存在且擁有sticky權(quán)限
例:
[root@CentOS8 ~]# [[ -x CHOOK_RABBIT.sh ]] 判斷是否有執(zhí)行權(quán)限
[root@CentOS8 ~]#echo $?
0
[root@CentOS8 ~]#chmod 0 CHOOK_RABBIT.sh 去除所有權(quán)限
[root@CentOS8 ~]# [[ -x CHOOK_RABBIT.sh ]] 判斷是否有執(zhí)行權(quán)限
[root@CentOS8 ~]#echo $?
1
[root@CentOS8 ~]# [[ -r CHOOK_RABBIT.sh ]] 判斷是否有可讀權(quán)限,因?yàn)槲覀兪莚oot,所有仍然可以讀
[root@CentOS8 ~]#echo $?
0
[root@CentOS8 ~]# [[ -w CHOOK_RABBIT.sh ]] 判斷是否有寫(xiě)入權(quán)限,因?yàn)槲覀兪莚oot,所有仍然可以寫(xiě)
[root@CentOS8 ~]#echo $?
0
文件屬性測(cè)試
-S FILE 是否存在且非空
-t fd fd 文件描述符是否在某終端已經(jīng)打開(kāi)
-N FILE 文件自從上一次被讀取之后是否被修改過(guò)
-O FILE 當(dāng)前有效用戶是否為文件屬主
-G FILE 當(dāng)前有效用戶是否為文件屬組
FILE1 -ef FILE2 : FILE1是否是FILE2的硬鏈接
FILE1 -nt FILE2 : FILE1是否新于FILE2(mtime)
FILE1 -ot FILE2 : FILE1是否舊于FILE2
2.12 關(guān)于()和{}
(CMD1;CMD2;...) 都可以批量執(zhí)行多個(gè)命令
{ CMD1;CMD2;...; }最后加;,{ }內(nèi)前后加空格
-
()會(huì)開(kāi)啟子shell,繼承父進(jìn)程的變量。list中變量賦值及內(nèi)部命令執(zhí)行后,不影響后續(xù)的環(huán)境。幫助參看:man bash 搜索(list)
-
{;}不會(huì)啟子shell,在當(dāng)前shell中運(yùn)行,會(huì)影響當(dāng)前shell環(huán)境,幫助參看:man bash 搜索{list;}
例:
[root@CentOS8 ~]#name=babe;(echo $name;name=hello;echo $name);echo $name 使用()
babe
hello
babe
[root@CentOS8 ~]#name=babe;{ echo $name;name=hello;echo $name; };echo $name 使用{}
babe
hello
hello
2.13 組合測(cè)試條件
2.13.1 方法 1 []
EXPRESSION1 -a EXPRESSION2 并且,EXPRESSION1和EXPRESSION2都是真,結(jié)果才為真
EXPRESSION1 -o EXPRESSION2 或者,EXPRESSION1和EXPRESSION2只要有一個(gè)真,結(jié)果就為真
[ ! EXPRESSION ] 或 ![ exp ] 取反
說(shuō)明: -a和-0 需要使用測(cè)試命令進(jìn)行,[[]]不支持
2.13.2 方法 2 [[]]
COMMAND1 && COMMAND2 #并且,短路與,代表?xiàng)l件性的AND THEN
如果COMMAND1 成功,將執(zhí)行COMMAND2,否則,將不執(zhí)行COMMAND2
COMMAND1 || COMMAND2 #或者,短路或,代表?xiàng)l件性的OR ELSE
如果COMMAND1 成功,將不執(zhí)行COMMAND2否則,將執(zhí)行COMMAND2
!COMMAND 非,取反
例:
[root@CentOS8 ~]#NAME=joycee;id $NAME &> /dev/null && echo "$NAME is exist" || echo "$NAME is not exist"
joycee is not exist
[root@CentOS8 ~]#NANAME=joycee;id $NAME &> /dev/null && echo "$NAME is exist" || (useradd $NAME;echo $NAME is created)
joycee is created
注意:&& 如果和 || 混用,則&& 要放前,|| 放后
2.13 使用read命今來(lái)接受輸入
使用read來(lái)把輸入值分配給一個(gè)或多個(gè)shell變量,read從標(biāo)準(zhǔn)輸入中讀取值,給每個(gè)單詞分配一個(gè)變量,所有剩余單詞都被分配給最后一個(gè)變量
格式
read [options] [name ...]
常見(jiàn)選項(xiàng):
-p 指定要顯示的提示
-s 靜默輸入,一般用于密碼
-n N 指定輸入的字符長(zhǎng)度N
-d '字符',輸入結(jié)束符
-t N TIMEOUT為N秒
[root@CentOS8 ~]#read
joyce
[root@CentOS8 ~]#echo $REPLY
joyce
[root@CentOS8 ~]#read NAME TITLE
joyce babe
[root@CentOS8 ~]#echo $NAME $TITLE
joyce babe
[root@CentOS8 ~]#read -p "are u ok?" ANSWER
are u ok?not bad
[root@CentOS8 ~]#echo $ANSWER
not bad
[root@CentOS8 ~]#echo 1 2 > test.txt 重定向
[root@CentOS8 ~]#read a b < test.txt;echo $a; echo $b
1
2
注意:
[root@CentOS8 ~]#echo 1 2 | read a b | echo $a $b
1 2
[root@CentOS8 ~]#echo 1 2 | (read a b; echo $a $b)
1 2
[root@CentOS8 ~]#echo 1 2 | read a b; echo $a $b 管道里,如果不加括號(hào)或|,則中間和后面是兩個(gè)獨(dú)立的shell,因此二者的ab不等
1 2
3 bash的配置文件
bash shell的配置文件很多,可以分成下面類別
3.1 按生效范圍劃分兩類
全局配置
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
個(gè)人配置:
~/.bash_profile
~/.bashrc
3.2 shell登錄兩種方式分類
3.2.1 交互式登錄
- 直接通過(guò)終端輸入賬號(hào)密碼登錄
- 使用
su - UserName
切換用戶
配置文件執(zhí)行順序:
/etc/profile --> /etc/profile.d/*,sh --> ~/.bash_profile --> ~/.bashrc --> /etc/bashrc
3.2.2 非交互式登錄
su UserName
圖形界面下打開(kāi)的終端
執(zhí)行腳本
任何其它的bash實(shí)例
執(zhí)行順序:
/etc/profile.d/*.sh --> /etc/bashrc -->~/.bashrc
3.3 按功能劃分分類
profile類和bashrc類
3.3.1 profile類
profile類為交互式登錄的shell提供配置
全局: /etc/profile,/etc/profile.d/*.sh
個(gè)人:~/.bash_profile
作用:
- 用于定義環(huán)境變量
- 運(yùn)行命令或腳本
3.3.2 Bashrc類
bashrc類:為非交互式和交互式登錄的shell提供配置
全局: /etc/bashrc
個(gè)人:~/.bashrc
作用:
- 定義命令別名和函數(shù)
- 定義本地變量
3.4 編輯配置文件生效
修改profile和bashrc文件后有生效兩種方法
- 重新啟動(dòng)shell進(jìn)程
- source 或 . 配置文件
3.5 Bash 退出任務(wù)
保存在~/.bash_logout文件中 (用戶),在退出登錄shell時(shí)運(yùn)行
功能
- ? 創(chuàng)建自動(dòng)備份
- ? 清除臨時(shí)文件
4 流程控制
4.1 條件選擇
條件判斷:
if
case
&& ||
4.1.1 選擇執(zhí)行 if 語(yǔ)句
格式:
if CMDs; then CMDs;[ elif CMDs; then CMDs; ]... [ else CMDs; ] fi
單分支:
if 判斷條件;then
條件為真的分支代碼
fi
雙分支:
if 判斷條件; then
條件為真的分支代碼
e1se
條件為假的分支代碼
fi
多分支:
if 判斷條件1; then
條件1為真的分支代碼
elif 判斷條件2; then
條件2為真的分支代碼
...
e1se
以上條件都為假的分支代碼
fi
說(shuō)明:
- 多個(gè)條件時(shí),逐個(gè)條件進(jìn)行判斷,第一次遇為“真”條件時(shí),執(zhí)行其分支,而后結(jié)束整個(gè)if語(yǔ)句
- if 語(yǔ)句可嵌套
4.1.2 條件判斷 case 語(yǔ)句
格式:
case WORD in [PATTERN [| PATTERN]...) CMDs ;;]...esac
此處PATTERN指通配符的模式
case 變量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默認(rèn)分支
;;
esac
case支持glob風(fēng)格的通配符:
* 任意長(zhǎng)度任意字符
? 任意單個(gè)字符
[] 指定范圍內(nèi)的任意單個(gè)字符
| 或,如 a或b
-
G
R
E
E
N
"
清理日志
"
GREEN"清理日志"
GREEN"清理日志"END
;;
4.2 循環(huán)
4.2.1 循環(huán)執(zhí)行介紹
將某代碼段重復(fù)運(yùn)行多次,通常有進(jìn)入循環(huán)的條件和退出循環(huán)的條件
重復(fù)運(yùn)行次數(shù)
- 循環(huán)次數(shù)事先已知
- 循環(huán)次數(shù)事先未知
常見(jiàn)的循環(huán)的命令:for,while,until
4.2.2 for循環(huán)
4.2.2.1 格式1
for NAME [in WORDS ...] ; do COMMANDS; done
(WORD之間使用空白符(空格、Tab鍵、回車)分割)
或
for 變量名 in 列表;do
循環(huán)體
done
或
for 變量名 in 列表
do
循環(huán)體
done
執(zhí)行機(jī)制:依次將列表中的元素賦值給“變量名”, 每次賦值后即執(zhí)行一次循環(huán)體;直到列表中的元素耗盡,循環(huán)結(jié)束
for循環(huán)列表生成方式:
- 直接給出列表
- 整數(shù)列表
{start..end}
$(seq [start [step]] end)
- 返回列表的命令:
$(COMMAND)
- 使用glob,如:*.sh
- 變量引用,如:$@,$#,$*等位置參數(shù) ( ? 將變量視為單個(gè), *將變量視為單個(gè), ?將變量視為單個(gè),@將變量視為整體)
死循環(huán):
for((;;)) ; do CMDs;done
例:
[root@CentOS8 ~]#for i in {001..8..2};do echo $i; done
001
003
005
007
[root@CentOS8 ~]#for i in `seq 5`;do echo $i; done
1
2
3
4
5
[root@CentOS8 ~]#for i in $(seq 5);do echo $i; done
1
2
3
4
5
例:計(jì)算1+2+3+…+100的和
[root@CentOS8 ~]#seq -s+ 100|bc
5050
或
[root@CentOS8 ~]#sum=0;for i in {1..100};do let sum+=i;done;echo $sum
5050
例:
[root@CentOS8 scripts]#cat sum.sh
#!/bin/bash
sum=0
for i in $@ ;do
let sum+=i
done
echo $sum
[root@CentOS8 scripts]#chmod +x sum.sh
[root@CentOS8 scripts]#./sum.sh 1 3 4 5 6
19
例:九九乘法表的實(shí)現(xiàn)
[root@CentOS8 scripts]#cat ./9x9.sh
#!/bin/bash
for j in {1..9};do
for i in `seq $j`;do
echo -e "${i}x$j=$((i*j))\t\c"
done
echo
done
[root@CentOS8 scripts]#./9x9.sh
1x1=1
1x2=2 2x2=4
1x3=3 2x3=6 3x3=9
1x4=4 2x4=8 3x4=12 4x4=16
1x5=5 2x5=10 3x5=15 4x5=20 5x5=25
1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36
1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49
1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64
1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
面試題:要求將目錄YYYY-MM-DD/中所有文件,移動(dòng)到Y(jié)YYY-MM/DD/下
#1 yyyy-mm-dd10.sh ,準(zhǔn)備數(shù)據(jù)
#創(chuàng)建YYYY-MM-DD,當(dāng)前日期一年前365天到目前共365個(gè)目錄,里面有10個(gè)文件,$RANDOM.Tog
[root@centos8 ~]#cat for_dir20.sh
#!/bin/bash
for i in {1..365};do
DIR=`date -d "-$i day" +%F`
mkdir /data/test/$DIR
cd /data/test/$DIR
for n in {1..10};do
touch $RANDOM.log
done
done
#2 移動(dòng)到Y(jié)YYY-MM/DD/下
[root@centos8 ~]#cat for_mv20.sh
#!/bin/bash
#
DIR=/data/test
cd $DIR
for DIR in *;do
YYYY_MM=`echo $DIR |cut -d"-" -f1,2` #將年月和日期拆分成2部分,這里是年月
DD=`echo $DIR | cut -d"-" -f3` #這里是日期
[ -d $YYYY_MM/$DD ] || mkdir -p $YYYY_MM/$DD &> /dev/null
mv $DIR/* $YYYY_MM/$DD
done
面試題:掃描一個(gè)網(wǎng)段:10.0.0.0/24,判斷此網(wǎng)段中主機(jī)在線狀態(tài),將在線的主機(jī)的IP打印出來(lái)
NET=10.0.0
for ID in {1..254};do
{
ping -c1 -W1 $NET.$ID &> /dev/null && echo $NET.$ID is up || echo $NET.$ID is down
}& #實(shí)現(xiàn)并行
done
wait #默認(rèn)并行不會(huì)自動(dòng)退出,加上wait使其自動(dòng)執(zhí)行下一個(gè)命令
4.2.2.2 格式2
雙小括號(hào)方法,即((…))格式,也可以用于算術(shù)運(yùn)算,雙小括號(hào)方法也可以使bash Shell實(shí)現(xiàn)C語(yǔ)言風(fēng)格的變量操作
|=10;((l++))
for ((控制變量初始化;條件判斷表達(dá)式;控制變量的修正表達(dá)式))
do
循環(huán)體
done
說(shuō)明:
- 控制變量初始化:僅在運(yùn)行到循環(huán)代碼段時(shí)執(zhí)行一次
- 控制變量的修正表達(dá)式:每輪循環(huán)結(jié)束會(huì)先進(jìn)行控制變量修正運(yùn)算,而后再做條件判斷
例:實(shí)現(xiàn)1累加到100
for ((sum=0;i<=100;i++));do
let sum+=i
done
echo $sum
例:九九乘法表的實(shí)現(xiàn)_2
for ((i=1;i<10;i++));do
for((j=1;j<=i;j++));do
echo -e "${j}x$i=$((j*i))\t\c"
done
echo
done
例:實(shí)現(xiàn)國(guó)際象棋的棋盤(pán)(較垃圾)
for ((i=1;i<=8;i++));do
if((i%2));then
for((x=0;x<4;x++));do
for ((j=0;j<8;j++));do
if ((j%2));then
echo -e '\E[47m \E[0m\c' ;
else
echo -e '\E[40m \E[0m\c' ;
fi
done
echo
done
else
for((x=0;x<4;x++));do
for ((j=0;j<8;j++));do
if ((j%2));then
echo -e '\E[40m \E[0m\c' ;
else
echo -e '\E[47m \E[0m\c' ;
fi
done
echo
done
fi
done
例:實(shí)現(xiàn)三角形
for((i=1;i<=10;i++));do
#for((z=0;z<=10-i;z++));do
for((z=10;z-i>0;z--));do
echo -e ' \c';
done
for((j=1;j<=2*i-1;j++));do
echo -e '*\c'
done
echo
done
例:加入?yún)?shù)實(shí)現(xiàn)三角形
read -p "請(qǐng)輸入三角形的行數(shù):" line
for((i=1;i<=$line;i++));do
#for((z=0;z<=10-i;z++));do
for((z=$line;z-i>0;z--));do
echo -e ' \c';
done
for((j=1;j<=2*i-1;j++));do
echo -e '*\c'
done
echo
done
4.2.3 while循環(huán)
格式:
while COMMANDs; do COMMANDS; done
while CONDITION; do
循環(huán)體
done
說(shuō)明:
CONDITION:循環(huán)控制條件,進(jìn)入循環(huán)之前,先做一次判斷,每一次循環(huán)之后會(huì)再次做判斷;條件為“true",則執(zhí)行一次循環(huán),直到條件測(cè)試狀態(tài)為“false"終止循環(huán),因此:CONDTION一般應(yīng)該有循環(huán)控制變量,而此變量的值會(huì)在循環(huán)體不斷地被修正
- 進(jìn)入條件: CONDITION為true
- 退出條件: CONDITION為false
死循環(huán):
#方法1
while true; do
循環(huán)體
done
#方法2
while :; do
循環(huán)體
done
范例:使用while實(shí)現(xiàn)磁盤(pán)報(bào)警
WARNING=10 #閾值
while : ;do
USE=`df | sed -rn '/^\/dev\/sd/s#.* ([0-9]+)%.*#\1#p' |sort -nr | head -n1`
if [ $USE -gt $WARNING ] ; then
echo DISK will be full from `hostname -I` | mail -s "Disk Warning" 1299575088@qq.com
fi
sleep 10 #10秒循環(huán)一次
done
例:死循環(huán)
[root@CentOS8 scripts]#while :;do echo ok ;sleep 1 ;done
ok
ok
ok
ok
ok
4.2.4 until循環(huán)
格式:
until COMMANDS; do COMMANDS; done
until CONDITION; do
循環(huán)體
done
說(shuō)明:
- 進(jìn)入條件:CONDITION為false
- 退出條件:CONDITION為true
死循環(huán)
until false; do
循環(huán)體
done
4.2.4 循環(huán)控制語(yǔ)句 continue
continue[N]:提前結(jié)束第N層的本輪循環(huán),而直接進(jìn)入下一輪判斷,最內(nèi)層為第1層
格式:
while CONDITION1; do
CMD1
...
if CONDITION2; then
continue
CMDn
...
done
例:
#1
for((i=0;i<10;i++));do
for((j=0;j<10;j++));do
[ $j -eq 5 ] && continue 1 #1可以不寫(xiě),代表終止該層循環(huán)的本次循環(huán)
echo $j
done
echo -----------------
done
[root@CentOS8 scripts]#bash continue_for_1.sh
0
1
2
3
4
6
7
8
9
-----------------
0
1
2
3
4
6
7
8
9
-----------------
0
1
2
3
4
6
7
8
9
-----------------
0
1
2
3
4
6
7
8
9
-----------------
0
1
2
3
4
6
7
8
9
-----------------
0
1
2
3
4
6
7
8
9
-----------------
0
1
2
3
4
6
7
8
9
-----------------
0
1
2
3
4
6
7
8
9
-----------------
0
1
2
3
4
6
7
8
9
-----------------
0
1
2
3
4
6
7
8
9
-----------------
#2
for((i=0;i<10;i++));do
for((j=0;j<10;j++));do
[ $j -eq 5 ] && continue 2 #2代表終止外層循環(huán)的本次循環(huán),使外層循環(huán)進(jìn)入下次循環(huán)
echo $j
done
echo -----------------
done
[root@CentOS8 scripts]#bash continue_for_1.sh
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
4.2.5 循環(huán)控制語(yǔ)句 break
break [N]:提前結(jié)束第N層整個(gè)循環(huán),最內(nèi)層為第1層
格式:
while CONDITION1; do
CMD1
...
if CONDITION2; then
break
fi
CMDn
例:
#1
for((i=0;i<10;i++));do
for((j=0;j<10;j++));do
[ $j -eq 5 ] && break 1 #1可以不寫(xiě),表示結(jié)束內(nèi)層循環(huán),使外層循環(huán)進(jìn)入下一輪
echo $j
done
echo -----------------
done
[root@CentOS8 scripts]#bash continue_for_1.sh
0
1
2
3
4
-----------------
0
1
2
3
4
-----------------
0
1
2
3
4
-----------------
0
1
2
3
4
-----------------
0
1
2
3
4
-----------------
0
1
2
3
4
-----------------
0
1
2
3
4
-----------------
0
1
2
3
4
-----------------
0
1
2
3
4
-----------------
0
1
2
3
4
-----------------
#2
for((i=0;i<10;i++));do
for((j=0;j<10;j++));do
[ $j -eq 5 ] && break 2 #2表示結(jié)束外層循環(huán),即結(jié)束所有循環(huán)
echo $j
done
echo -----------------
done
[root@CentOS8 scripts]#bash continue_for_1.sh
0
1
2
3
4
例:菜品選擇
sum=0
COLOR1='echo -e \033[1;31m'
COLOR2='echo -e \033[1;32m'
END="\033[0m"
while :;do
echo -e "\033[33;1m\c"
cat <<-EOF
1)米
2)面
3)粉
4)湯
5)谷
6)肉
EOF
echo -e "\033[0m"
read -p "請(qǐng)選擇菜品:" MENU
case $MENU in
1|4)
$COLOR1'菜價(jià):$10'$END
let sum+=10
;;
3|5)
$COLOR1'菜價(jià):$20'$END
let sum+=20
;;
2)
$COLOR1'菜價(jià):$888'$END
let sum+=888
;;
6)
$COLOR2"你點(diǎn)的菜品總價(jià)是:$sum"$END
break
;;
*)
echo "輸入錯(cuò)誤!"
;;
esac
$COLOR2"你點(diǎn)的菜品總價(jià)是:$sum"$END
done
4.2.6 循環(huán)控制 shift 命令
shift[n]用于將參量列表list 左移指定次數(shù),缺省為左移一次。
參量列表list 一旦被移動(dòng),最左端的那個(gè)參數(shù)就從列表中刪除。while 循環(huán)遍歷位置參量列表時(shí),常用到 shift
例:shift批量創(chuàng)建用戶
PASS=110420
while [ "$1" ];do
useradd $1 && echo $1 is created || echo $1 is exists
echo $PASS | passwd --stdin $1
shift #shift 1 一次只跳過(guò)1個(gè),2則跳過(guò)2個(gè)
done
[root@CentOS8 scripts]#bash shift_user.sh Tom Alice
Tom is created
Changing password for user Tom.
passwd: all authentication tokens updated successfully.
Alice is created
Changing password for user Alice.
passwd: all authentication tokens updated successfully.
例:判斷某個(gè)ip訪問(wèn)的次數(shù)
[root@CentOS8 data]#sed -En '/^ESTAB/s#.*[: ]([^:]+):[0-9]+ $#\1#p' ss.log | sort |uniq -c
1 10.0.0.1
200 10.0.0.8
4.2.7 while read 特殊用法
while循環(huán)的特殊用法,遍歷文件或文本的每一行,即逐行處理
支持stdin
格式
while read line; do
循環(huán)體
done < /PATH/FROM/SOMEFILE
說(shuō)明:依次讀取/PATH/FROM/SOMEFILE文件中的每一行,且將行賦值給變量line
例:
[root@centos8 ~]#echo joyce | read X ; echo $X # 管道|開(kāi)啟子進(jìn)程
[rootcentos8 ~]#echo joyce | while read X ; do echo $X;done
joyce
[root@centos8 ~]#echo joyce | { read X ; echo $X; }
joyce
[root@centos8 ~]#echo joyce | ( read X ; echo $X )
joyce
[root@centos8 ~]#echo joyce wang zhang | ( read X Y Z; echo $X $Y $Z )
joyce wang zhang
[root@centos8 ~]#echo joyce wang zhang | while read X Y Z; do echo $X $Y $Z;done
joyce wang zhang
例:使用while read實(shí)現(xiàn)磁盤(pán)報(bào)警
WARNING=10
MAIL=1299575088@qq.com
df |sed -nr "/^\/dev\/sd/s#^([^ ]+) .* ([0-9]+)%.*#\1 \2#p" |while read DEVICE USE;do
if [ $USE -gt $WARNING ] ;then
echo "$DEVICE will be full,use:$USE" | mail -s "DISK WARNING" $MAIL
fi
done
例:查看/sbin/nologin的shell類型的用戶名和UID
while read line ;do
if [[ "$line" =~ /sbin/nologin$ ]] ; then
echo $line | cut -d: -f1,3
fi
done < /etc/passwd
例:存放大量單詞文件
[root@CentOS8 scripts]#wc -l /usr/share/dict/linux.words
479828 /usr/share/dict/linux.words
4.2.8 select 循環(huán)與菜單
格式:
select NAME [in WORDS ... ;] do COMMANDS; done
select variable in list ;do
循環(huán)體命令
done
說(shuō)明
- select 循環(huán)主要用于創(chuàng)建菜單,按數(shù)字順序排列的菜單項(xiàng)顯示在標(biāo)準(zhǔn)錯(cuò)誤上,并顯示 PS3 提示符,等待用戶輸入
- 用戶輸入菜單列表中的某個(gè)數(shù)字,執(zhí)行相應(yīng)的命令
- 用戶輸入被保存在內(nèi)置變量REPLY中
- select 是個(gè)無(wú)限循環(huán),因此要記住用 break 命令退出循環(huán),或用exit 命令終止腳本。也可以按 ctrl+c 退出循
- select經(jīng)常和 case 聯(lián)合使用
- 與for 循環(huán)類似,可以省略 in list,此時(shí)使用位置參量
5 函數(shù)介紹
5.1 管理函數(shù)
函數(shù)由兩部分組成:函數(shù)名和函數(shù)體
幫助參看:help function
5.1.1 定義函數(shù)
function name { COMMANDS ; } or name () { COMMANDS ; }
#語(yǔ)法一:
func_name () {
...函數(shù)體...
}
#語(yǔ)法二:
function func_name {
...函數(shù)體...
}
#語(yǔ)法三:
function func_name () {
...函數(shù)體...
}
例:創(chuàng)建disable_firewall_selinux函數(shù)以關(guān)閉firewalld和selinux
[root@centos8_3 ~]#systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2023-08-12 22:57:44 CST; 6min ago
[root@centos8_3 ~]#getenforce
Enforcing
# 函數(shù)主體:
[root@centos8_3 ~]#disable_firewall_selinux () {
> systemctl stop firewalld
> systemctl disable firewalld
> sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
> setenforce 0
> }
[root@centos8_3 ~]#disable_firewall_selinux
Removed /etc/systemd/system/multi-user.target.wants/firewalld.service.
Removed /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.
[root@centos8_3 ~]#systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
Active: inactive (dead)
[root@centos8_3 ~]#getenforce
Permissive
注意:函數(shù)操作后確實(shí)修改了文件,因此重啟后仍然保存。但是函數(shù)本身一次性使用,重啟后消失,因此想要保存函數(shù)需要寫(xiě)入到文件
例:分裝函數(shù)與調(diào)用于2個(gè)文件,可直接調(diào)用函數(shù)
#funtions文件,保存函數(shù)主體
[root@centos8_3 scripts]#cat functions
#函數(shù)1:disable_firewall_selinux()
disable_firewall_selinux () {
systemctl stop firewalld
systemctl disable firewalld
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce 0
}
#函數(shù)2:yum_repo()
yum_repo() {
cd /etc/yum.repos.d/
mkdir backup
mv *.repo backup
cat > base.repo <<EOF
[BaseOS]
name=aliyun BaseOS
baseurl = https://mirrors.aliyun.com/centos/8/BaseOS/x86_64/os/
https://repo.huaweicloud.com/centos/8-stream/BaseOS/x86_64/os/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
[AppStream]
name=aliyun AppStream
baseurl = https://mirrors.aliyun.com/centos/8/AppStream/x86_64/os/
https://repo.huaweicloud.com/centos/8-stream/AppStream/x86_64/os/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
[epel]
name=Extra Packages for Enterprise Linux \$releasever - \$basearch
# It is much more secure to use the metalink, but if you wish to use a local mirror
# place it's address here.
#baseurl=https://download.example/pub/epel/\$releasever/Everything/\$basearch
metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-\$releasever&arch=\$basearch&infra=\$infra&content=\$contentdir
enabled=1
gpgcheck=1
countme=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-8
[epel-debuginfo]
name=Extra Packages for Enterprise Linux \$releasever - \$basearch - Debug
# It is much more secure to use the metalink, but if you wish to use a local mirror
# place it's address here.
#baseurl=https://download.example/pub/epel/\$releasever/Everything/\$basearch/debug
metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-debug-\$releasever&arch=\$basearch&infra=\$infra&content=\$contentdir
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-8
gpgcheck=1
[epel-source]
name=Extra Packages for Enterprise Linux \$releasever - \$basearch - Source
# It is much more secure to use the metalink, but if you wish to use a local mirror
# place it's address here.
#baseurl=https://download.example/pub/epel/\$releasever/Everything/SRPMS
metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-source-\$releasever&arch=\$basearch&infra=\$infra&content=\$contentdir
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-8
gpgcheck=1
EOF
}
#函數(shù)3:install_package()
install_package() {
packages="
vim
tree
autofs
net-tools
apache
httpd
"
for i in $packages;do
rpm -q $i &> /dev/null || yum -q -y install $i
done
}
#disable_firewall_selinux.sh文件,調(diào)用disable_firewall_selinux函數(shù)
[root@centos8_3 scripts]#cat disable_firewall_selinux.sh
#!/bin/bash
. functions #引用functions文件
disable_firewall_selinux
#yum_repo.sh文件,調(diào)用yum_repo函數(shù)
[root@centos8_3 scripts]#cat yum_repo.sh
#!/bin/bash
. functions
yum_repo
#install_package.sh文件,調(diào)用install_package函數(shù)
[root@centos8_3 scripts]#cat install_package.sh
#!/bin/bash
. functions
install_package
5.1.2 查看函數(shù)
#查看當(dāng)前已定義的函數(shù)名
declare -F
#查看當(dāng)前已定義的函數(shù)定義
declare -f
5.1.3 刪除函數(shù)
格式:
unset func_name
5.2 函數(shù)調(diào)用
函數(shù)的調(diào)用方式
- 可在交互式環(huán)境下定義函數(shù)
- 可將函數(shù)放在腳本文件中作為它的一部分
- 可放在只包含函數(shù)的單獨(dú)文件中
調(diào)用:函數(shù)只有被調(diào)用才會(huì)執(zhí)行,通過(guò)給定函數(shù)名調(diào)用函數(shù),函數(shù)名出現(xiàn)的地方,會(huì)被自動(dòng)替換為函數(shù)代碼
函數(shù)的生命周期:被調(diào)用時(shí)創(chuàng)建,返回時(shí)終止。
不過(guò)調(diào)用函數(shù)時(shí),如果函數(shù)內(nèi)外有同名變量,調(diào)用函數(shù)后會(huì)修改函數(shù)內(nèi)外的變量,類似于全局變量,如果要使函數(shù)只修改函數(shù)內(nèi)部的變量而不影響函數(shù)外的同名變量,可以在函數(shù)內(nèi)聲明時(shí)加上local,使其成為本地變量,如local NAME=1
5.2.1 交互式環(huán)境調(diào)用函數(shù)
交互式環(huán)境下定義和使用函數(shù)
5.2.2 在腳本中定義及使用函數(shù)
函數(shù)在使用前必須定義,因此應(yīng)將函數(shù)定義放在腳本開(kāi)始部分,直至shell首次發(fā)現(xiàn)它后才能使用,調(diào)用函數(shù)僅使用其函數(shù)名即可
5.2.3 使用函數(shù)文件
例:調(diào)用系統(tǒng)函數(shù)
[root@CentOS8 ~]#. /etc/init.d/functions
[root@CentOS8 ~]#action "you an me"
you an me [ OK ]
[root@CentOS8 ~]#action "you an medsadsad" false
you an medsadsad [FAILED]
5.3 函數(shù)返回值
默認(rèn)情況下,我們使用exit 100,會(huì)返回100,但用在函數(shù)里,會(huì)使整個(gè)腳本退出而不執(zhí)行下面的代碼。
因此使用return #返回值,可只退出當(dāng)前函數(shù)而不退出腳本,不過(guò)只在函數(shù)中使用
函數(shù)的執(zhí)行結(jié)果返回值
- 使用echo等命令進(jìn)行輸出
- 函數(shù)體中調(diào)用命令的輸出結(jié)果
函數(shù)的退出狀態(tài)碼
-
默認(rèn)取決于函數(shù)中執(zhí)行的最后一條命令的退出狀態(tài)碼
-
自定義退出狀態(tài)碼,其格式為
-
return 從函數(shù)中返回,用最后狀態(tài)命令決定返回值
-
return 0 無(wú)錯(cuò)誤返回
-
return 1-255 有錯(cuò)誤返回
-
5.4 環(huán)境函數(shù)
類擬于環(huán)境變量,也可以定義環(huán)境函數(shù),使子進(jìn)程也可使用父進(jìn)程定義的函數(shù)
定義環(huán)境函數(shù):
export -f function_name
declare -xf function_name
查看環(huán)境函數(shù):
export -f
declare -xf
5.5 函數(shù)參數(shù)
函數(shù)可以接受參數(shù):
- 傳遞參數(shù)給函數(shù),在函數(shù)名后面以空白分隔給定參數(shù)列表即可,如: testfunc arg1 arg2…
- 在函數(shù)體中當(dāng)中,可使用$1, 2 , . . . 調(diào)用這些參數(shù),還可以使用 2,...調(diào)用這些參數(shù),還可以使用 2,...調(diào)用這些參數(shù),還可以使用@, ? , *, ?,#等特殊變量
5.6 函數(shù)變量
變量作用域:
- 普通變量:只在當(dāng)前shell進(jìn)程有效,為執(zhí)行腳本會(huì)啟動(dòng)專用子shell進(jìn)程;因此,本地變量的作用范圍是當(dāng)前shell腳本程序文件,包括腳本中的函數(shù)
- 環(huán)境變量:當(dāng)前shell和子shell有效
- 本地變量:函數(shù)的生命周期;函數(shù)結(jié)束時(shí)變量被自動(dòng)銷毀
注意:
- 如果函數(shù)中定義了普通變量,且名稱和局部變量相同,則使用本地變量
- 由于普通變量和局部變量會(huì)沖突,建議在函數(shù)中只使用本地變量
在函數(shù)中定義本地變量的方法
local NAME=VALUE
5.7 函數(shù)遞歸
函數(shù)遞歸:函數(shù)直接或間接調(diào)用自身,注意遞歸層數(shù),可能會(huì)陷入死循環(huán)
- 基例:確定值
- 鏈條:規(guī)律
遞歸示例:階乘
階乘是基斯頓·卡曼于1808 年發(fā)明的運(yùn)算符號(hào),是數(shù)學(xué)術(shù)語(yǔ),一個(gè)正整數(shù)的階乘(factorial)是所有小于及等于該數(shù)的正整數(shù)的積,并且有0的階乘為1,自然數(shù)n的階乘寫(xiě)作n!
n!=1x2x3x…xn
階乘亦可以遞歸方式定義: 0!=1,n!=(n-1)!xn
n!=n(n-1)(n-2)…1
n(n-1)!=n(n-1)(n-2)!
例:遞歸實(shí)現(xiàn)階乘
[root@CentOS8 functions]#cat fact.sh
fact() {
if [ $1 -eq 1 ];then
echo 1
else
echo $[`fact $[$1-1]`*$1]
fi
}
fact $1
[root@CentOS8 functions]#bash fact.sh 5
120
fork炸彈是一種惡意程序,它的內(nèi)部是一個(gè)不斷在 fork 進(jìn)程的無(wú)限循環(huán),實(shí)質(zhì)是一個(gè)簡(jiǎn)單的遞歸程序。由于程序是遞歸的,如果沒(méi)有任何限制,這會(huì)導(dǎo)致這個(gè)簡(jiǎn)單的程序迅速耗盡系統(tǒng)里面的所有資源
參考:https://en.wikipedia.org/wiki/Fork_bomb
函數(shù)實(shí)現(xiàn)
:(){ :|:& };: #無(wú)基例
bomb () {} bomb | bomb & }; bomb
腳本實(shí)現(xiàn)
cat Bomb.sh
#!/bin/bash
./$0|./$0&
OOM:內(nèi)存溢出
6 其它腳本相關(guān)工具
6.1 信號(hào)捕捉 trap
-
trap ‘觸發(fā)指令’ 信號(hào)
? 進(jìn)程收到系統(tǒng)發(fā)出的指定信號(hào)后,將執(zhí)行自定義指令,而不會(huì)執(zhí)行原操作
-
trap" 信號(hào)
? 忽略信號(hào)的操作
-
trap ‘-’ 信號(hào)
? 恢復(fù)原信號(hào)的操作
-
trap -p
? 列出自定義信號(hào)操作
-
trap finish EXIT
? 當(dāng)腳本退出時(shí),執(zhí)行finish函數(shù)
例:
[root@CentOS8 functions]#cat signal_trap1.sh
trap 'echo "Press ctrl+c"' int quit #將int 和quit信號(hào)改為echo "Press ctrl+c"
trap -p
for((i=1;i<=10;i++))
do
sleep 1
echo $i
done
trap '' int #將int信號(hào)改為空,即忽略
trap -p
for((i=11;i<21;i++ ))
do
sleep 1
echo $i
done
trap '-' int #恢復(fù)int信號(hào),接下來(lái)遇到int信號(hào)將執(zhí)行ctrl+c
trap -p
for(( i=21;i<31;i++))
do
sleep 1
echo $i
done
[root@CentOS8 functions]#bash signal_trap1.sh
trap -- 'echo "Press ctrl+c"' SIGINT
trap -- 'echo "Press ctrl+c"' SIGQUIT
1
^CPress ctrl+c
2
^CPress ctrl+c
3
^CPress ctrl+c
4
^CPress ctrl+c
5
^CPress ctrl+c
6
^CPress ctrl+c
7
^CPress ctrl+c
8
^CPress ctrl+c
9
^CPress ctrl+c
10
trap -- '' SIGINT
trap -- 'echo "Press ctrl+c"' SIGQUIT
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^[[A11
^[[A12
^[[A^[[A^[[A^C13
^C^C^C^C^C^C^C^C^C^C^C14
15
16
17
18
19
20
trap -- 'echo "Press ctrl+c"' SIGQUIT
21
22
23
24
25
26
^C
6.2 創(chuàng)建臨時(shí)文件 mktemp
mktemp命令用于創(chuàng)建并顯示臨時(shí)文件,可避免沖突
格式
mktemp [OPTION]... [TEMPLATE]
說(shuō)明:TEMPLATE:filenameXXX,X至少要出現(xiàn)三個(gè)
常見(jiàn)選項(xiàng):
-d 創(chuàng)建臨時(shí)目錄
-p DIR或--tmpdir=DIR 指明臨時(shí)文件所存放目錄位置
例:
#默認(rèn)放在tmp目錄下
[root@CentOS8 functions]#mktemp
/tmp/tmp.B9CHlgxwCL
[root@CentOS8 functions]#mktemp
/tmp/tmp.ur2MYKCZhU
[root@CentOS8 ~]#mktemp -d tmpdirXXXX #在當(dāng)前目錄下生成臨時(shí)目錄
tmpdirqWDe
[root@CentOS8 ~]#ll tmpdirqWDe -d
drwx------ 2 root root 6 Aug 13 14:51 tmpdirqWDe
例:實(shí)現(xiàn)rm的進(jìn)階寫(xiě)法
[root@CentOS8 scripts]#cat rm2.sh
DIR=`mktemp -d /tmp/trash-$(date +%F_%H-%M-%S)XXXXX`
mv $* $DIR
echo $* is move to $DIR
[root@CentOS8 scripts]#bash rm2.sh 123 321
123 321 is move to /tmp/trash-2023-08-13_14-59-58BDIIO
[root@CentOS8 scripts]#ls /tmp/trash-2023-08-13_14-59-58BDIIO/
123 321
[root@CentOS8 scripts]#alias rm=/data/scripts/rm2.sh
[root@CentOS8 scripts]#touch 1
[root@CentOS8 scripts]#chmod +x rm2.sh
[root@CentOS8 scripts]#rm 1
1 is move to /tmp/trash-2023-08-13_15-05-24kBeQl
6.3 安裝復(fù)制文件 install
功能相當(dāng)于cp、chmod、chown、chgrp等相關(guān)工具的集合
install命令格式
install [OPTION]... [-T] SOURCE DEST 單文件
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
insta11 [OPTION]... -d DIRECTORY...創(chuàng)建空目錄
選項(xiàng):
-m MODE,默認(rèn)755 #改權(quán)限
-o OWNER #改所有者
-g GROUP #改所屬組
-d DIRNAME
例:
[root@CentOS8 ~]#ll cal.txt
-rw-r--r-- 1 root root 0 Jul 28 19:29 cal.txt
[root@CentOS8 ~]#install -m 666 -o joyce -g bin cal.txt /data/test.txt
[root@CentOS8 ~]#ll /data/test.txt
-rw-rw-rw- 1 joyce bin 0 Aug 13 15:12 /data/test.txt
#創(chuàng)建空文件夾
[root@CentOS8 ~]#install -m 700 -o joyce -g daemon -d /data/testdir/
[root@CentOS8 ~]#ll -d /data/testdir/
drwx------ 2 joyce daemon 6 Aug 13 15:13 /data/testdir/
6.4 交互式轉(zhuǎn)化批處理工具 expect
expect 是由 Don Libes 基于 TcI ( Tool Command Language )語(yǔ)言開(kāi)發(fā)的,主要應(yīng)用于自動(dòng)化交互式操作的場(chǎng)景,借助 expect處理交互的命令,可以將交互過(guò)程如:ssh登錄,tp登錄等寫(xiě)在一個(gè)腳本上,使之自動(dòng)化完成尤其適用于需要對(duì)多臺(tái)服務(wù)器執(zhí)行相同操作的環(huán)境中,可以大大提高系統(tǒng)管理人員的工作效率
expect 語(yǔ)法:
expect [選項(xiàng)] [ -c cmds ] [ [ -[fIb] ] cmdfile ] [ args ]
常見(jiàn)選項(xiàng):
-c 從命令行執(zhí)行expect腳本,默認(rèn)expect是交互地執(zhí)行的
-d 可以輸出輸出調(diào)試信息
示例:
expect -c 'expect "\n" {send "pressed enter\n"}'
expect -d ssh.exp
expect中相關(guān)命令
spawn 啟動(dòng)新的進(jìn)程
expect 從進(jìn)程接收字符串
send 用于向進(jìn)程發(fā)送字符串
interact 允許用戶交互
exp_continue 匹配多個(gè)字符串在執(zhí)行動(dòng)作后加此命令
expect最常用的語(yǔ)法 ( tcl語(yǔ)言:模式-動(dòng)作 )
單一分支模式語(yǔ)法:只生效一次,遇到hi輸出后即結(jié)束
[root@centos8 test]#expect
expect1.1> expect "hi" {send "You said hi\n"}
hahahixixi
You said hi
例:非交互式發(fā)送文件至第二個(gè)主機(jī),自動(dòng)輸入yes和密碼
#!/usr/bin/expect #注意,不是bash
spawn scp /etc/fstab 10.0.0.7:/data
expect {
"yes/no" { send "yes\n",exp_continue }
"password" { send "110420\n" }
}
expect eof
例:非交互式自動(dòng)登錄至第二個(gè)ip主機(jī),自動(dòng)輸入yes和密碼
[root@centos8 scripts]#cat expect2
#!/usr/bin/expect
spawn ssh 10.0.0.209
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "110420\n" }
}
interact
[root@CentOS8 scripts]#chmod +x expect1.sh
[root@CentOS8 scripts]#./expect1.sh
spawn ssh 10.0.0.209
root@10.0.0.209's password:
hello Joyce!
Nna
Activate the web console with: systemctl enable --now cockpit.socket
Last login: Sun Aug 13 15:48:51 2023 from 10.0.0.201
[root@centos8_2 ~]#hostname
centos8_2
例:帶有參數(shù)的自動(dòng)登錄其他ip主機(jī)的腳本,自動(dòng)輸入yes和密碼
[root@CentOS8 scripts]#cat ./expect2.sh
#!/usr/bin/expect
set ip 10.0.0.209
set user root
set password 110420
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
interact #代表可以進(jìn)行交互
[root@CentOS8 scripts]#chmod +x expect2.sh
[root@CentOS8 scripts]#./expect2.sh
spawn ssh root@10.0.0.209
root@10.0.0.209's password:
hello Joyce!
Nna
Activate the web console with: systemctl enable --now cockpit.socket
Last login: Sun Aug 13 15:49:06 2023 from 10.0.0.201
例:使用位置參數(shù)實(shí)現(xiàn)自動(dòng)登錄其他ip的主機(jī)
[root@CentOS8 scripts]#cat ./expect3.sh
#!/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" }
}
interact
[root@CentOS8 scripts]#chmod +x expect3.sh
[root@centos8 scripts]#./expect3 10.0.0.209 root 110420
spawn ssh roota10.0.0.7
roota10.0.0.209's password:
Last login: wed Apr 29 15:34:14 2020 from 10.0.0.8
例:遠(yuǎn)程登陸主機(jī)并創(chuàng)建賬號(hào)設(shè)置密碼,完了退出
#!usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password"{ send"$password\n" }
}
# ]#即登陸后輸入內(nèi)容前面的標(biāo)識(shí)符
expect "]#" { send "useradd haha\n" }
expect "]#" { send "echo 110420 |passwd --stdin haha\n" }
send "exit\n"
expect eof
例:在bash腳本里調(diào)用expect以遠(yuǎn)程登錄主機(jī)并創(chuàng)建賬號(hào)設(shè)置密碼
[root@CentOS8 scripts]#cat ./expect5.sh
#!/bin/bash
ip=$1
user=$2
password=$3
expect <<EOF
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password"{ send"$password\n" }
}
# ]#即登陸后輸入內(nèi)容前面的標(biāo)識(shí)符
expect "]#" { send "useradd haha\n" }
expect "]#" { send "echo 110420 |passwd --stdin haha\n" }
send "exit\n"
expect eof
EOF
[root@CentOS8 scripts]#bash expect5.sh 10.0.0.209 root 110420
例:批量登錄不同主機(jī)并創(chuàng)建用戶設(shè)置密碼
#!/bin/bash
NET=10.0.0
user=root
password=110420
for ID in 209 210;do #循環(huán)遍歷不同IP的主機(jī)
ip=$NET.$ID
expect <<EOF
set timeout 20
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "#" { send "useradd test\n" }
expect "#" { send "exit\n" }
expect eof
EOF
done
例:批量登錄不同主機(jī)并修改selinux為disabled
#!/bin/bash
NET=10.0.0
user=root
password=110420
for ID in 209 210;do #循環(huán)遍歷不同IP的主機(jī)
ip=$NET.$ID
expect <<EOF
set timeout 20
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "#" { send "sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/confiig\n" }
expect "#" { send "setenforce 0\n" }
expect "#" { send "exit\n" }
expect eof
EOF
done
7數(shù)組
7.1 數(shù)組介紹
變量:存儲(chǔ)單個(gè)元素的內(nèi)存空間
數(shù)組:存儲(chǔ)多個(gè)元素的連續(xù)的內(nèi)存空間,相當(dāng)于多個(gè)變量的集合
數(shù)組名和索引
- 索引的編號(hào)從0開(kāi)始,屬于數(shù)值索引
- 索引可支持使用自定義的格式,而不僅是數(shù)值格式,即為關(guān)聯(lián)索引,bash4.0版本之后開(kāi)始支持
- bash的數(shù)組支持稀疏格式(索引不連續(xù))
7.2 聲明數(shù)組
#普通數(shù)組可以不事先聲明,直接使用
declare -a ARRAY_NAME
#關(guān)聯(lián)數(shù)組必須先聲明,再使用
declare -A ARRAY_NAME
注意:兩者不可相互轉(zhuǎn)換
7.3 數(shù)組賦值
數(shù)組元素的賦值
-
一次只賦值一個(gè)元素
ARRAY_NAME[INDEX]=VALUE
例:
[root@CentOS8 ~]#title[0]=ceo [root@CentOS8 ~]#echo ${title} ceo
-
一次賦值全部元素
ARRAY_NAME=("VAL1" "VAL2" "VAL3"...)
例:
title=("ceo""coo""cto") num=({0..10}) alpha=({a..g}) file=( *.sh )
-
只賦值特定元素
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
-
交互式數(shù)組值對(duì)賦值
read -a ARRAY [root@CentOS8 ~]#read -a number 1 3 5 7 9 [root@CentOS8 ~]#echo ${number[*]} 1 3 5 7 9 [root@CentOS8 ~]#echo ${number[2]} 5 [root@CentOS8 ~]#echo ${number} 1
7.4 顯示所有數(shù)組
顯示所有數(shù)組
declare -a
7.5 引用數(shù)組
引用數(shù)組元素
${ARRAY_NAME[INDEX]}
#如果省略[INDEX]表示引用下標(biāo)為0的元素
引用數(shù)組所有元素
${ARRAY_NAME[*]}
${ARRAY_NAME[@]}
例:
[root@CentOS8 ~]#NUM=({1..5})
[root@CentOS8 ~]#echo ${NUM[3]}
4
[root@CentOS8 ~]#echo ${NUM[2]}
3
[root@CentOS8 ~]#echo ${NUM[*]}
1 2 3 4 5
[root@CentOS8 ~]#echo ${NUM[@]}
1 2 3 4 5
例:
[root@CentOS8 ~]#read -a number
1 3 5 7 9
[root@CentOS8 ~]#echo ${number[*]}
1 3 5 7 9
[root@CentOS8 ~]#echo ${number[2]}
5
[root@CentOS8 ~]#echo ${number}
1
7.6 刪除數(shù)組
刪除數(shù)組中的某元素,會(huì)導(dǎo)致稀疏格式
unset ARRAY[INDEX]
例:
[root@centos8 ~]#echo $title[*]}
ceo coo cto
[root@centos8 ~]#unset title[1]
[root@centos8 ~]#echo $ftitle[*]]
ceo cto
刪除整個(gè)數(shù)組
unset ARRAY
例:
[root@centos8 ~]#unset title
[root@centos8 ~]#echo ${title[*]}
7.7 數(shù)組數(shù)據(jù)處理
數(shù)組切片
${ARRAY[@]:offset:number}
offset #要跳過(guò)的元素個(gè)數(shù)
number #要取出的元素個(gè)數(shù)
{ARRAY[@]:offset} #取偏移量之后的所有元素
例:
[root@centos8 ~]#num=({0..10})
[root@centos8 ~]#echo ${num[*]:2:3}
2 3 4
[root@centos8 ~]#echo ${num[*]:6}
6 7 8 9 10
向數(shù)組中追加元素:
ARRAY[${#ARRAY[*]}]=value
ARRAY[${#ARRAY[@]}]=value
例:
[root@CentOS8 ~]#num=({0..10})
[root@CentOS8 ~]#echo ${num[2]}
2
[root@CentOS8 ~]#echo ${num[*]}
0 1 2 3 4 5 6 7 8 9 10
[root@CentOS8 ~]#num[11]=11
[root@CentOS8 ~]#echo ${num[*]}
0 1 2 3 4 5 6 7 8 9 10 11
[root@CentOS8 ~]#echo ${#num[*]}
12
[root@CentOS8 ~]#num[${#num[*]}]=12
[root@CentOS8 ~]#echo ${#num[*]}
13
[root@CentOS8 ~]#echo ${num[*]}
0 1 2 3 4 5 6 7 8 9 10 11 12
7.8 關(guān)聯(lián)數(shù)組
declare -A ARRAY_NAME
ARRAY_NAME=([idx_namel]='val1' [idx_name2]='val2'...
注意:關(guān)聯(lián)數(shù)組必須先聲明再調(diào)用
例:
[root@CentOS8 ~]#declare -A name
[root@CentOS8 ~]#name[first]=Tom
[root@CentOS8 ~]#name[second]=Jack
[root@CentOS8 ~]#name[third]=Case
[root@CentOS8 ~]#echo ${name[first]}
Tom
[root@CentOS8 ~]#echo ${name[*]}
Case Jack Tom
[root@CentOS8 ~]#echo ${name[third]}
Case
7.9 范例
例:生成包含10個(gè)隨機(jī)數(shù)的數(shù)組,并顯示其中的最大值MAX和最小值MIN
[root@CentOS8 test]#cat random1.sh
#!/bin/bash
declare -i min max
declare -a nums
for ((i=0;i<10;i++));do
nums[$i]=$RANDOM
[ $i -eq 0 ] && min=${nums[0]} && max=${nums[0]}&& continue
[ ${nums[$i]} -gt $max ] && max=${nums[$i]}
[ ${nums[$i]} -lt $min ] && min=${nums[$i]}
done
echo "All numbers are ${nums[*]}"
echo Max is $max
echo min is $min
[root@CentOS8 test]#bash random1.sh
All numbers are 23203 6083 14719 18022 31925 25880 9039 10908 30266 23092
Max is 31925
min is 6083
8 字符串處理
8.1 字符串切片
基于偏移量取字符串
#返回字符串變量var的長(zhǎng)度
${#var}
#返回字符串變量var中從第offset個(gè)字符后(不包括第offset個(gè)字符)的字符開(kāi)始,到最后的部分,offset的取值在0到${#var}-1 之間(bash4.2后,允許為負(fù)值)
${var:offset]
#返回字符串變量var中從第offset個(gè)字符后(不包括第offset個(gè)字符)的字符開(kāi)始,長(zhǎng)度為number的部分
${var:offset:number}
#取字符串的最右側(cè)幾個(gè)字符,取字符串的最右側(cè)幾個(gè)字符,注意: 冒號(hào)后必須有一空白字符
${var: -length}
#從最左側(cè)跳過(guò)offset字符,一直向右取到距離最右側(cè)lengh個(gè)字符之前的內(nèi)容,即:掐頭去尾
${var:offset:-length}
#先從最右側(cè)向左取到length個(gè)字符開(kāi)始,再向右取到距離最右側(cè)offset個(gè)字符之間的內(nèi)容,注意: -length前空格
${var: -length:-offset}
例:
[root@CentOS8 test]#str=123abc哇哦
[root@CentOS8 test]#echo ${#str}
8
[root@CentOS8 test]#echo ${str:5}
c哇哦
[root@CentOS8 test]#echo ${str:5:2}
c哇
[root@CentOS8 test]#echo ${str: -2}
哇哦
[root@CentOS8 test]#echo ${str:3:-2}
abc
[root@CentOS8 test]#echo ${str: -3:-1} #取倒數(shù)3個(gè)和倒數(shù)1個(gè)之間的2個(gè)
c哇
基于模式取子串
從左往右:
#其中word可以是指定的任意字符,自左而右,查找var變量所存儲(chǔ)的字符串中,第一次出現(xiàn)的word,刪除字符串開(kāi)頭至第一次出現(xiàn)word字符串 (含) 之間的所有字符
${var#*word}:
#同上,貪婪模式,不同的是,刪除的是字符串開(kāi)頭至最后一次由word指定的字符之間的所有內(nèi)容
${var##*word}:
例:
[root@centos8 ~]#file="var/log/messages"
[root@centos8 ~]#echo ${file#*/}
log/messages
[root@centos8 ~]#echo ${file##*/}
messages
從右往左:
#其中word可以是指定的任意字符,功能,自右而左,查找var變量所存儲(chǔ)的字符串中,第一次出現(xiàn)的worc,刪除字符串最后一個(gè)字符向左至第一次出現(xiàn)word字符串 (含) 之間的所有字符
${var%word*}
#同上,只不過(guò)刪除字符串最右側(cè)的字符向左至最后一次出現(xiàn)word字符之間的所有字符
${var%%word*}
例:
[root@centos8 ~]#file="/var/log/messages"
[root@centos8 ~]#echo ${file%/*}
var/log
[root@centos8 ~]#echo ${file%%/*}
var
8.2 查找替換、
#查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替換之
${var/pattern/substr}
#查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替換之
${var//pattern/substr}
#查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替換之
${var/#pattern/substr}
#查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替換之
${var/%pattern/substr}
8.3 查找并刪除
#刪除var表示的字符串中第一次被pattern匹配到的字符串
${var/pattern}
#刪除var表示的字符串中所有被pattern匹配到的字符串
${var//pattern}
#刪除var表示的字符串中所有以pattern為行首匹配到的字符串
${var/#pattern}
#刪除var所表示的字符串中所有以pattern為行尾所匹配到的字符串
${var/%pattern}
8.4 字符大小寫(xiě)轉(zhuǎn)換
#把var中的所有小寫(xiě)字母轉(zhuǎn)換為大寫(xiě)
${var^^}
#把var中的所有大寫(xiě)字母轉(zhuǎn)換為小寫(xiě)
${var,,}
例:
[root@CentOS8 test]#echo $str
123abc哇哦
[root@CentOS8 test]#echo ${str^^}
123ABC哇哦
9 高級(jí)變量
9.1 高級(jí)變量賦值
變量配置方法 | str 沒(méi)有配置 | str 為空字符串 | str 已配置非為空字符串 |
---|---|---|---|
var=${str-expr} | var=expr | var= | var=$str |
var=${str:-expr} | var=expr | var=expr | var=$str |
var=${str+expr} | var= | var=expr | var=expr |
var=${str:+expr} | var= | var= | var=expr |
var=${str=expr} | str=expr var=expr |
str 不變 var= |
str 不變 var=$str |
var=${str:=expr} | str=expr var=expr |
str=expr var=expr |
str 不變 var=$str |
var=${str?expr} | expr輸出至stderr | var= | var=sstr |
var=${str:?expr} | expr輸出至stderr | expr輸出至stderr | var=sstr |
例:
[root@centos8 ~]#title=ceo
[root@centos8 ~]#name=${title-oops}
[root@centos8 ~]#echo $name
ceo
[root@centos8 ~]#title=
[root@centos8 ~]#name=${title-oops}
[root@centos8 ~]#echo $name
[root@centos8 ~]#unset title
[rootacentos8 ~]#name=$ftitle-oops}
[root@centos8 ~]#echo $name
oops
9.2 高級(jí)變量用法-有類型變量
Shell變量一般是無(wú)類型的,但是bash Shell提供了declare和typeset兩個(gè)命令用于指定變量的類型,兩個(gè)命令是等價(jià)的
declare [選項(xiàng)] 變量名
選項(xiàng):
-r 聲明或顯示只讀變量
-i 將變量定義為整型數(shù)
-a 將變量定義為數(shù)組
-A 將變量定義為關(guān)聯(lián)數(shù)組
-f 顯示已定義的所有函數(shù)名及其內(nèi)容
-F 僅顯示已定義的所有函數(shù)名
-x 聲明或顯示環(huán)境變量和函數(shù),相當(dāng)于export
-l 聲明變量為小寫(xiě)字母 declare -l var=UPPER
-u 聲明變量為大寫(xiě)字母 declare -u var=lower
9.3 變量間接引用
9.3.1 eval命令
eval命令將會(huì)首先掃描命令行進(jìn)行所有的置換,然后再執(zhí)行該命令。該命令適用于那些一次掃描無(wú)法實(shí)現(xiàn)其功能的變量,該命令對(duì)變量進(jìn)行兩次掃描,因此后面必須跟命令
例1:
[root@CentOS8 test]#CMD=hostname
[root@CentOS8 test]#$CMD
CentOS8.Joyce.person1
[root@CentOS8 test]#echo $CMD
hostname
[root@CentOS8 test]#eval $CMD
CentOS8.Joyce.person1
例2:
[root@CentOS8 test]#n=10
[root@CentOS8 test]#for i in {1..$n};do echo $i;done
{1..10}
[root@CentOS8 test]#for i in `eval echo {1..$n}`;do echo $i;done
1
2
3
4
5
6
7
8
9
10
9.3.2 間接變量引用
如果第一個(gè)變量的值是第二個(gè)變量的名字,從第一個(gè)變量引用第二個(gè)變量的值就稱為間接變量引用
variable1的值是variable2,而variable2又是變量名,variable2的值為value,間接變量引用是指通過(guò)variable1獲
得變量值value的行為
variablel=variable2
variable2=value
bash Shell提供了兩種格式實(shí)現(xiàn)間接變量引用文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-646889.html
eval tempvar= \$Svariable1
tempvar=${!variable1}
例:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-646889.html
[root@CentOS8 test]#ceo=name
[root@CentOS8 test]#name=Me
[root@CentOS8 test]#echo $ceo
name
[root@CentOS8 test]#echo $$ceo
7487ceo
[root@CentOS8 test]#echo \$$ceo
$name
[root@CentOS8 test]#eval echo \$$ceo
Me
#或:
[root@CentOS8 test]#echo ${!ceo}
Me
9.3.3變量引用reference
[root@centos8 ~]#cat test.sh
#!/bin/bash
ceo=ME
title=ceo
declare -n ref=$title
[ -R ref ] && echo reference
echo $ref
ceo=Joyce
echo $ref
[root@centos8 ~]#bash test.sh
reference
ME
Joyce
到了這里,關(guān)于Linux_5_Shell腳本編程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!