Shell腳本
Shell腳本
Shell是什么?
- Shell腳本語言屬于弱類型語言,解析用戶輸入的命令和程序,使得用戶可以與Linux進行交互;
- 適合處理純文本類型數(shù)據(jù)(日志、配置文件、文本、網(wǎng)頁文件、大多數(shù)純文本類型的文件)。
Shell概念
shebang
-
即文件的第一行前兩個字符
#!,后面的語句指定命令的解析器。#!/bin/sh 或 #!/bin/bash:執(zhí)行時會調用/bin/sh,即bash解析器。#!/bin/perl:執(zhí)行時。#!/usr/bin/python:python解析器。#!/usr/bin/env:跨平臺都能正常找到解析器的辦法。- 注意:
- 未指定
shebang時,默認用Shell解析。 - 當指定
shebang,但程序是不可執(zhí)行文件時,轉而用Shell解析。 - 指定
shebang解析器的路徑需要絕對路徑。 - 執(zhí)行時指定解析器執(zhí)行,會忽略
shebang解析器。
- 未指定
變量
- 命名規(guī)則: 只能數(shù)字、字母、下劃線,不能以數(shù)字開頭,區(qū)分大小寫。
- 數(shù)據(jù)類型:bash默認所有變量都是字符串。
- 作用域:只針對當前的Shell進程,每次調用
bash/sh解析器執(zhí)行腳本都會開啟一個子Shell。 - 進程樹:
# psTree 可以檢查進程樹
pstree
ps -ef --forest
常用命令
# \ 轉義
echo "print each param from \"\$*\""
# 變量定義與賦值之間不得有空格
name="I am Soul!"
# 單引號不識別特別語法,雙引號識別特殊符號
name2="${name}"
# 變量替換/引用
echo ${name}
echo $name
# 轉義 即 變量中定義命令
name = `ls`
# 1)執(zhí)行 name 變量時實際上是輸出 ls 指令
echo $name
# 常用命令
set # 輸出所有變量(局部、全局)
unset 變量名 # 刪除變量/函數(shù)
env # 輸出全局變量
declare # 輸出所有變量
export # 輸出/設置環(huán)境變量
export | awk -F '[ :=]' '{print $3}' # 輸出系統(tǒng)環(huán)境變量關鍵字
readonly # 設置只讀
let # 數(shù)值運算 即 (())
expr # 計算器
expr yn.png ":" ".*" # 統(tǒng)計 yn.png 文件字符個數(shù)
test -e soul.jpg && echo "OK" || echo "NO" # 三目運算
環(huán)境變量
- 常用環(huán)境變量
${USER} # 當前用戶名字
$UID # 當前用戶ID
$HOME # 當前用戶文件目錄
$(data) # 時間
- 配置文件
# 用戶個人配置
~/.bash_profile
~/.bashrc # 遠程登錄用戶特有文件
# 全局配置文件
/etc/profile
/etc/bashrc
/etc/profile.d/ # 系統(tǒng)建議創(chuàng)建在該目錄而非直接修改全局配置文件。
- 環(huán)境變量文件加載順序

特殊變量
# 特殊參數(shù)變量
$0 # 獲取Shell腳本文件名,以及腳本路徑
$n # 獲取腳本的第n個參數(shù)(n>=1),當大于9時,需要寫成 ${10}
$# # 獲取執(zhí)行的Shell腳本后面的參數(shù)的總數(shù)
$* # 獲取Shell腳本所有參數(shù),加雙引號時,將所有參數(shù)視為一份數(shù)據(jù)
$@ # 獲取Shell腳本所有參數(shù),加雙引號時,仍然將每個參數(shù)視為獨立的數(shù)據(jù)
# 特殊狀態(tài)變量
$? # 上一次執(zhí)行命令的狀態(tài)返回值:0:成功,否則失敗。
$$ # 當前Shell腳本的進程號
$! # 上一次后臺進程的PID
$_ # 獲取上一次執(zhí)行命令最后一個參數(shù)
擴展變量
${parameter:-word} # param參數(shù)為空時返回word值
${parameter:=word} # param參數(shù)為空時將word值返回給param參數(shù)并作為返回值
${parameter:?word} # param參數(shù)為空時返回word作為提示信息返回
${parameter:+word} # param參數(shù)為空時不做處理,否則返回word
bash的內置命令
echo -n # 不換行輸出
echo -e # 解析字符串中的特殊字符(\n:換行,\r:回車,\t:制表符,\b:退格)
echo $name | wc -l # 統(tǒng)計有多少行
echo $name | wc -L # 統(tǒng)計長度
echo "8.8*2" | bc # 執(zhí)行 bc 運算
eval ls;cd /tmp # 執(zhí)行多個命令
exec # 不創(chuàng)建子進程,執(zhí)行后續(xù)命令,且執(zhí)行完畢后自動退出
seq # 生成序列指令
seq -s ":" 10 # 生成 1~10的序列并用分號隔開
time # 計算執(zhí)行時間
read -t 5 -p "請輸入:" # 提示用戶,輸入信息(-p),-t 超時時間
# 運算
# 1) 求和運算
echo {1..100} | tr " " "+" | bc # 求和運算
echo $((`seq -s "+" 100`)) # (())
seq -s " + " 100 | xargs expr # xargs expr
# 2)awk方式運算
echo "3.2 2.2" | awk '{print $1+$2}'
# 測試
test
[ ] # 也可以用中括號 [ ] 代替test,前后必須有空格,變量必須用""
test -e a.txt -a -f a.txt # -a 即: and && 條件
test -e a.txt -o -f a.txt # -o 即: or || 條件
# 1) 文件相關
test -e # 文件是否存在
test -f # 是否為文件
test -d # 是否為目錄
test -r # 文件是否有[可讀]屬性
test -w # 文件是否有[可寫]屬性
test -x # 文件是否有[可執(zhí)行]屬性
# 2)字符串
test -z # 字符串為空時為真,否則為假。
test -n # 字符串為空時為假,否則為真。
# 3)比較
test -eq # 兩個數(shù)值相等
test -nq # 兩個數(shù)值不相等
test -gt # a 大于 b
test -lt # a 小于 b
test -ge # a 大于等于 b
test -le # a 小于等于 b
test = #
test != #
test ! #
shell子串
${name} # 返回變量值
${#name} # 返回變量長度,字符串長度
${name:start} # 從第幾位截取返回子字符串
${name:start:length} # 從第幾位開始,長度為length進行截取
${name#word} # 從變量開頭刪除最短的word字符串
${name##word} # 從變量開頭刪除最長word字符串
${name%word} # 從變量結尾刪除最短的word字符串
${name%%word} # 從變量結尾刪除最長的word字符串
${name/pattern/string} # 用string替換第一個匹配的pattern
${name//pattern/string} # 用string替換所有的pattern
# 統(tǒng)計子串長度
echo ${#name} # 返回長度(最快)
echo $name | wc -l # wc 統(tǒng)計有多少行
echo $name | wc -L # wc 統(tǒng)計長度
expr length "${name}" # expr 計算數(shù)值
echo $name | awk '{print length($0)}' # awk 統(tǒng)計長度,length函數(shù)
執(zhí)行方式
# 進程列表,并在子進程運行
(cd ~;pwd;ls;cd /tmp/;pwd;echo $BASH_SUBSHELL)
# 利用()括號開啟子進程執(zhí)行命令,$BASH_SUBSHELL檢查當前進程層數(shù)(子進程大于1),常用于多進程處理提高程序并發(fā)執(zhí)行的效率。
# 方式一(常用): 文件沒有執(zhí)行權限 或 腳本沒有指定 shebang 都可以執(zhí)行,會開啟子進程執(zhí)行Shell(獲取不了當前進程的變量)
bash script.sh
sh script.sh
# 方式二:絕對/相對路徑執(zhí)行腳本,需要文件含有X權限。
bash /usr/script.sh
bash .script.sh
# 方式三:不會開啟子進程執(zhí)行Shell,可以獲取當前進程的變量。
source script.sh
. script.sh
# 方式四:
sh < script.sh
Shell腳本應用
批量操作文件名
# 批量創(chuàng)建文件
touch soul_config{1..2}.xml
# 批量修改文件名
for item in `ls *config.xml` # 獲取當前目錄下部分后綴的文件名
do mv $item `echo ${item//config/}` # 將config文件名替換為空
done
數(shù)據(jù)備份并刪除過期數(shù)據(jù)
# find xargs
find ${dir_path:=/data/mysql_back_data/} -name '*.tar.gz' -type f -mtime +7 | xargs rm -f
# 避免dir_path為空時取指定路徑下的數(shù)據(jù)
運算腳本開發(fā)
#!/bin/bash/
# 開發(fā)一個 運算腳本
# 定義打印函數(shù)
print_soul(){
printf "Please enter an interger!\n"
exit 1
}
# 接收用戶輸入的第一個數(shù)字
read -p "Please input your number: " firstnum
# [] 中括號前后必須有空格
# -n 判斷條件是否為空字符串
# sed 將所有數(shù)字替換為空
if [ -n "`echo $firstnum|sed 's/[0-9]//g'`" ]; then
print_soul
fi
# 接收用戶輸入的 運算符
read -p "Please input your operator:" operator
if [ "${operator}" != "+" ] && [ "${operator}" != "-" ] && [ "${operator}" != "/" ] && [ "${operator}" != "*" ]; then
echo "只允許 輸入 + | - | * | /"
exit 2
fi
# 接收用戶輸入的第二個數(shù)字
read -p "Please input your number: " sencondnum
if [ -n "`echo ${sencondnum} | sed 's/[0-9]//g'`" ]; then
print_soul
fi
# 運算
echo "${firstnum}${operator}${sencondnum}結果是:" $((${firstnum}${operator}${sencondnum}))
Nginx存活檢測
#!/bin/bash/
# 失敗次數(shù)
fails=0
success=0
# 空轉檢查
while true
do
# 請求網(wǎng)站是否能打開
wget --timeout=5 --tries=1 http://baidu.com/ -q -0 /dev/null
# -ne 不等于
if [ $? -ne 0 ]; then
let fails=fails+1
else
let success+=1
fi
# -ge 大于,網(wǎng)站正常
if [ $success -ge 1 ]; then
echo "The inter site is Ok!!!"
exit 0
fi
# -ge 大于,網(wǎng)站異常、并發(fā)送郵件
if [ $fails -ge 2 ]; then
mail -s "`date +%F-%T`" 16042334@qq.com "The inter site is fails!!!"
exit 2
fi
done
Mysql存活檢測
#!/bin/bash/
# netstat、ss、lsof 三種方式檢查Mysql是否運行、失敗即發(fā)送郵件提醒。
# 郵件發(fā)送,需要配置:vim /etc/mail.rc,配置發(fā)送者的賬號信息
if [ `netstat -tunlp | grep mysql | wc -l` != "1" -a `ss -tunlp | grep mysql | wc -l` -nq "1" -a `lsof -i tcp:3306 | wc -l` = "0" ];then
mail -s "`date +%F-%T`" 16042334@qq.com "The mysql is stopped!!!"
fi
Mysql備份
#!/bin/bash/
DATE=$(date +%F_%H-%M-%S)
HOST=localhost
USER=backup
PASS=123.com
BACKUP_DIR=/data/db_backup
DB_LIST=$(mysql -h$HOST -u$USER -p$PASS -s -e "show databases;" 2> /dev/null | egrep -v "Database|information_schema|mysql|performance_schema|sys")
for item in $DB_LIST; do
BACKUP_NAME=${BACKUP_DIR}/${DB}_${DATE}.sql
if ! mysqldump -h$HOST -u$USER -p$PASS -B $DB > ${BACKUP_NAME} 2> /dev/null;then
echo "$BACKUP_NAME is Backup failed !!!"
fi
done

浙公網(wǎng)安備 33010602011771號