腳本規(guī)范I
首行shebang
#! /bin/bash
script開頭描述說明
#!/bin/bash
#
#*****************************************************************
#Author: yourname
#QQ: 12345678
#Date: 2020-08-24
#FileName: /data/test.sh
#URL: http://www.itdecent.cn/u/8b95c627fb18
#Description: The test script
#Copyright(C): 2020All rights reserved
*****************************************************************
在vim配置文件~/.vimrc中添加以下代碼自動生成script description
set tabstop=4
set softtabstop=
set shiftwidth=4
set expandtab
set ignorecase
set cursorline
set autoindent
auto BufNewFile *.sh exec ":call SetTitle()"
func SetTitle()
if expand("%:e") == 'sh'
call setline(1,"#!/bin/bash")
call setline(2,"#")
call setline(3,"#*****************************************************************")
call setline(4,"#Author: yourname")
call setline(5,"#QQ: 12345678")
call setline(6,"#Date: ".strftime("%Y-%m-%d"))
call setline(7,"#FileName: ".expand("%"))
call setline(8,"#URL: http://www.itdecent.cn/u/8b95c627fb18")
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
腳本的基本結(jié)構(gòu)
section 1: #!SHEBANG
section 2: CONFIGURATION_VARIABLES
section 3: FUNCTION_DEFINITIONS
section 4: MAIN_CODE
shell script 運(yùn)行
- 賦予執(zhí)行權(quán)限,在命令行上指定腳本絕對或相對路徑
- bash yourscirpt.sh
shell script 調(diào)試
- 檢測腳本中的語法錯誤
bash -n /path/to/some_script
- 調(diào)試執(zhí)行
bash -x /path/to/some_script
變量
命名法則
- 數(shù)字、字母和下劃線
- 變量名大寫
- 局部變量小寫
- 函數(shù)名小寫
- 駝峰命名法
種類
| 名稱 |
含義 |
| 局部變量 |
生效范圍為當(dāng)前shell進(jìn)程;對當(dāng)前shell之外的其它shell進(jìn)程,包括當(dāng)前shell的子進(jìn)程均無效 |
| 環(huán)境變量 |
生效范圍為當(dāng)前shell進(jìn)程及其子進(jìn)程 |
| 本地變量 |
函數(shù)內(nèi)部變量 |
| 位置變量 |
2,...來表示,用于調(diào)用腳本時傳遞參數(shù)值 |
| 特殊變量 |
$?, $0, $*, $@, $#, $$ |
局部變量
- 賦值
| 形式 |
說明 |
| name= "root" |
字符串 |
| name= "$USER" |
變量引用 |
| name= `COMMAND` or name=$(COMMAND) |
命令引用 |
- 變量引用 ${name} or $name
- " " : 強(qiáng)引用,其中的變量引用會被替換為變量值
- ' ':弱引用,其中變量引用不會被替換為變量值,保持原字符串
- 顯示已定義的所有變量
set
- 刪除變量
unset name
環(huán)境變量
- 變量聲明和賦值:
- export name=value
- declare -x name=value
- 變量引用
- 顯示所有環(huán)境變量
- env
- printenv
- export
- declare -x
- 刪除變量
unset name
- bash內(nèi)建的環(huán)境變量
| name |
implication |
| PATH |
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin" |
| SHELL |
SHELL="/bin/bash" |
| USER |
USER="root" |
| UID |
| HOME |
HOME="/root" |
| PWD |
PWD="/root" |
| SHLVL |
SHLVL="1" |
| LANG |
LANG="en_US.UTF-8" |
| MAIL |
MAIL="/var/spool/mail/root" |
| HOSTNAME |
| HISTSIZE |
| _下劃線 |
前一個命令的最后一個參數(shù) |
只讀變量
聲明后,不能修改和刪除
- 聲明
- readonly name
- declare -r name
- 查看
readonly -p
位置變量
| name |
implication |
| $1,$2,... |
對應(yīng)第1、第2各參數(shù),shift[n]換位置 |
| $0 |
命令本身 |
| $* |
傳遞給腳本的所有參數(shù),全部參數(shù)合為一個字符串 |
| $@ |
傳遞給腳本的所有參數(shù),每個參數(shù)為獨立字符串 |
| $# |
傳遞給腳本的參數(shù)的個數(shù) |
| set -- |
清空所有位置變量 |
| note |
*只在被雙引號包起來的時候才會有差異 |
程序退出狀態(tài)
- 0 代表成功, 1-255代表失敗的code
- $? 變量保存最近的命令退出狀態(tài)
- exit [n] 自定義退出碼
算術(shù)運(yùn)算
- 運(yùn)算符號:+, -, *, /, %, **
- 實現(xiàn)算術(shù)運(yùn)算
| number |
mode |
| 1 |
let var=算術(shù)表達(dá)式 |
| 2 |
var=$[算術(shù)表達(dá)式] |
| 3 |
var=$((算術(shù)表達(dá)式)) |
| 4 |
var=$(expr arg1 arg2 arg3 ...) |
| 5 |
declare -i var = 數(shù)值 |
| 6 |
echo “算術(shù)表達(dá)式” | bc |
[root@my_host ~]# let var=3+4
[root@my_host ~]# echo $var
7
[root@my_host ~]# var=$[4+4]
[root@my_host ~]# echo $var
8
[root@my_host ~]# var=$((5+4))
[root@my_host ~]# echo $var
9
[root@my_host ~]# echo 6+4 | bc
10
- 隨機(jī)數(shù)生成器變量 $RANDOM (0-32767)
echo $[$RANDOM%50]
賦值
- +=,-=,*=,/=,%=
- ++,--
邏輯運(yùn)算
- true: 0,false:1
- and
- or
- 非!
- 短路運(yùn)算
- 異或 ^
條件測試
測試命令
- test EXPRESSION
- [ EXPRESSION ]
- [[ EXPRESSION ]]
- NOTE: EXPRESSION前后必須有空白字符
數(shù)值測試
| Format |
comments |
| -v VAR |
變量VAR是否設(shè)置 |
| -gt |
是否大于 |
| -ge |
是否大于等于 |
| -eq |
是否等于 |
| -ne |
是否不等于 |
| -lt |
是否小于 |
| -le |
是否小于等于 |
字符串測試
| format |
comments |
| = |
是否等于 |
| > |
基于ascii碼比較 |
| < |
基于ascii碼比較 |
| != |
是否不等于 |
| =~ |
左側(cè)字符串能否被右側(cè)PATTERN所匹配,一般用在[[ ]]中,[[ $file =~ .sh$ ]] |
| -z $VAR |
字符串是否為空 |
| -n $VAR |
字符串是否不為空 |
| note |
字符串比較操作符前后要有空格,例如:'hello' = 'world' |
文件測試
存在性測試
| Format |
comments |
| -a FILE |
同 -e |
| -e FILE |
文件存在性測試,存在為真 |
存在性及類別測試
| Formant |
comments |
| -b FILE |
是否存在并且為塊設(shè)備 |
| -c FILE |
是否存在且為字符設(shè)備 |
| -d FILE |
是否存在且為目錄文件 |
| -f FILE |
是否存在且為普通文件 |
| -h FILE OR -L FILE |
是否存在且為符號鏈接文件 |
| -p FILE |
是否存在且為命名管道文件 |
| -S FILE |
是否存在且為套接字文件 |
文件權(quán)限測試
| Format |
Comments |
| -r FILE |
是否存在且可讀 |
| -w FILE |
是否存在且可寫 |
| -x FILE |
是否存在且可執(zhí)行 |
| -u FILE |
是否存在且用于suid權(quán)限 |
| -g FILE |
是否存在且擁有sgid權(quán)限 |
| -k FILE |
是否存在且擁有sticky權(quán)限 |
文件大小測試
| Format |
Comments |
| -s FILE |
是否存在且非空 |
文件打開測試
| Fortmat |
Comments |
| -t fd |
fd文件描述符是否在某終端已經(jīng)打開 |
| -N FILE |
文件自上一次被讀取之后是否被修改過 |
| -O FILE |
當(dāng)前有效用戶是否是文件所有者 |
| -G FILE |
當(dāng)前有效用戶是否是文件屬組 |
雙目測試
| Format |
comments |
| FILE1 -ef FILE2 |
FILE1是否是FILE2的硬鏈接 |
| FILE1 -nt FILE2 |
FILE1是否新于FILE2(mtime) |
| FILE1 -ot FILE2 |
FILE1是否舊于FILE2 |
組合測試條件
- 第一種方式
- 第二種方式
- COMMAND1 && COMMAND2 并且,短路與
- COMMAND1 || COMMAND2 或者,短路或
- ! COMMAND 非
file=/data/check_disk.sh; [[ $file =~ \.sh$ ]] && [ -x "$file" ] && $file
[ -r testfile -a -w testfile ] && echo file is rw
[ -z "$HOSTNAME" -o "$HOSTNAME"=="localhost.localdomain" ] && hostname www.elearnings.cn
條件性的執(zhí)行操作符
- COMMAND1 && COMMAND2
如果command1成功則執(zhí)行command2
- COMMAND1 $$ COMMAND2
如果command1失敗則執(zhí)行command2
- COMMAND1 && COMMAND2 || COMMAND3
如果command1成功,那么執(zhí)行command2,否則執(zhí)行command3
grep -q no_sush_usr /etc/passwd || echo 'No such user'
ping -c1 -w2 station1 &> /dev/null \
&& echo "station1 is up" \
|| (echo 'station1 is unreadable'; exit1)
使用read接受命令行輸入?yún)?shù)
| options |
comments |
| -p |
指定要顯示的提示 |
| -s |
靜默輸入,一般用于密碼 |
| -n N |
指定輸入的字符長度N |
| -d 'char' |
指定輸入結(jié)束符 |
| -t N |
TIMEOUT為N秒 |
if語句
- 單分支
if 判斷條件;then
條件為真的分支代碼
fi
- 雙分支
if 判斷條件;then
條件為真的分支代碼
else
條件為假的分支代碼
fi
- 多分支
if 判斷條件1;then
條件1為真的分支代碼
elif 判斷條件2;then
條件2為真的分支代碼
elif 判斷條件3;then
條件3為真的分支代碼
else
以上條件都為假的分支代碼
fi
case語句
case 變量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默認(rèn)分支
;;
esac
bash如何展開命令行 ?????
- 把命令行分成單個命令詞
- 展開別名
- 展開大括號的聲明({})
- 展開波浪符聲明(~)
防止擴(kuò)展
- \
- ' '
bash配置文件
全局配置文件
- /etc/profile
- /etc/profile.d/*.sh
- /etc/bashrc
個人配置文件
- ~/.bash_profile
- ~/.bashrc
shell登錄2中方式
- 交互式登錄
- 通過終端輸入賬號密碼登錄
- 使用 “su - UserName” 切換的目錄
- 配置文件執(zhí)行順序:/etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile --> ~/.bashrc --> /etc/bashrc
- 非交互式登錄
- su UserName
- 圖形界面下打開的終端
- 執(zhí)行腳本
- 其它bash實例
- 配置文件執(zhí)行順序:/etc/profile.d/*.sh --> /etc/bashrc --> ~/.bashrc
功能劃分
- Profile類
- 為交互式登錄的shell提供配置
- 全局:/etc/profile, /etc/profile.d/*.sh
- 個人:~/.bash_profile
- 公用:(1)用于定義環(huán)境變量(2)運(yùn)行命令或腳本
- bashrc類
- 為非交互式和交互式登錄提供配置
- 全局:/etc/bashrc
- 個人:~/.bashrc
- 公用:(1)定義別名和函數(shù)(2)定義本地變量
#1.讓所有用戶的PATH環(huán)境變量的值多出一個路徑,例如: /usr/local/apache/bin
1) vim /etc/profile.d/env.sh
2) PATH= /usr/local/apache/bin:$PATH
#2. 用戶 root 登錄時,將命令指示符變成紅色,并自動啟用如下別名
1) vim /home/root/.bashrc
2) 編輯PS1=[\u@\h \W]\$
3)定義別名
#3. 任意用戶登錄系統(tǒng)時,顯示紅色字體的警示提醒信息“Hi,dangerous!”
1)vim /etc/bashrc
#4. 編寫用戶的環(huán)境初始化腳本reset.sh,包括別名,登錄提示符,vim的設(shè)置, 環(huán)境變量等
配置文件生效方法
- source
- .
bash退出任務(wù)
- 保存在~/.bash_logout
- 退出shell登錄時運(yùn)行
- 功用:
實踐
- 編寫腳本 systeminfo.sh,顯示當(dāng)前主機(jī)系統(tǒng)信息,包括主機(jī)名,IPv4地址, 操作系統(tǒng)版本,內(nèi)核版本,CPU型號,內(nèi)存大小,硬盤大小,多顏色輸出。
input=$1
if [ -z $input ]; then
input=red
fi
setColor(){
#red:31, green:32, yellow:33, blue:34, purple:35, cyan:36, white:37
case $input in
green)
color=32
;;
yellow)
color=33
;;
blue)
color=34
;;
purple)
color=35
;;
cyan)
color=36
;;
white)
color=37
;;
*)
color=31 # red
;;
esac
coloredText="\033[;$color;1m$*033[0m"
echo -e $coloredText
}
echo "This host configuration is:"
echo "*******************************************************************************************"
echo -ne "主機(jī)名:\t\t\t" ; setColor `hostname`
echo -ne "ip地址:\t\t\t" ; setColor `ip -4 -br a | grep eth | tr -s ' ' | cut -d' ' -f3`
echo -ne "操作系統(tǒng)版本:\t\t" ; setColor `cat /etc/centos-release`
echo -ne "內(nèi)核版本:\t\t" ; setColor `uname -r`
echo -ne "cpu型號:\t\t" ; setColor `lscpu | grep "Model name" | cut -d: -f2 | grep -E -o "[^' '].*"`
echo -ne "內(nèi)存大小:\t\t" ; setColor `cat /proc/meminfo | grep MemTotal | cut -d: -f2 | grep -E -o "[^' '].*"`
echo -ne "硬盤大小:\t\t" ; setColor `fdisk -l | grep "Disk /dev/" | cut -d' ' -f3,4 | tr -d ','`
2、編寫腳本 backup.sh,可實現(xiàn)每日將/etc/目錄備份到/backup/etcYYYYmm-dd中
source=/etc/
dest="/backup/etc`date +%F`"
if [ -d $dest ];then
echo 'you have already backup /etc/ today'
else
`cp -a $source $dest`
fi
- 編寫腳本 sumid.sh,計算/etc/passwd文件中的第10個用戶和第20用戶的 UID之和
function getUID(){
userID=`cat -b /etc/passwd | grep -E "^[ ]+$1" | cut -d: -f3`
echo $userID
}
usrID10=$(getUID 10)
usrID20=$(getUID 20)
echo $[$usrID10+$usrID20]
- 編寫腳本 sumspace.sh,傳遞兩個文件路徑作為參數(shù)給腳本,計算這兩個 文件中所有空白行之和
if [ $# -ne 2 ]; then
echo please input right file name
exit 154
elif [ -e $1 ] && [ -e $2 ]; then
spaceF1=`grep -E "^$" $1 | wc -l`
spaceF2=`grep -E "^$" $2 | wc -l`
else
echo invalid file
exit 155
fi
echo $[$spaceF1+$spaceF2]
- 編寫腳本 sumfile.sh,統(tǒng)計/etc, /var, /usr 目錄中共有多少個一級子目錄和 文件
dir_etc=/etc/
dir_var=/var/
dir_usr=/usr
function count_files() {
file_num=`ls -al $1 | grep -E "^-" | wc -l`
dir_num=`ls -al $1| grep -E "^d" | wc -l`
echo $[$file_num+$dir_num]
}
#echo $(count_files $dir_etc)
sum=0
for dir in $dir_etc $dir_var $dir_usr;
do
each_sum=$(count_files $dir)
let sum=$sum+$each_sum
done
echo $sum
- 編寫腳本 argsnum.sh,接受一個文件路徑作為參數(shù);如果參數(shù)個數(shù)小于1, 則提示用戶“至少應(yīng)該給一個參數(shù)”,并立即退出;如果參數(shù)個數(shù)不小于1,則 顯示第一個參數(shù)所指向的文件中的空白行數(shù)
[ $# -lt 1 ] && (echo please input a file; exit 1)
[ -f $1 -a -r $1 ] && cat $1 | egrep "^$" | wc -l || echo "$1 is invalid"
- 編寫腳本 hostping.sh,接受一個主機(jī)的IPv4地址做為參數(shù),測試是否可連 通。如果能ping通,則提示用戶“該IP地址可訪問”;如果不可ping通,則提 示用戶“該IP地址不可訪問”
[ $# -ne 1 ] && (echo 'please input a ip'; exit 150)
ping -c1 $1 &>/dev/null && echo 'pingable' || echo 'fail to ping'
- 編寫腳本 checkdisk.sh,檢查磁盤分區(qū)空間和inode使用率,如果超過80%, 就發(fā)廣播警告空間將滿
level=80
used_inode=`df | egrep "^/dev" | tr -s ' ' | cut -d' ' -f3`
avai_inode=`df | egrep "^/dev" | tr -s ' ' | cut -d' ' -f4`
utl_disk=`df | egrep "^/dev" | tr -s ' ' | cut -d' ' -f5 | tr -d '%'`
utl_inode=`echo "$used_inode*100/$avai_inode" | bc`
[ $utl_disk -ge $level ] && echo "Warning: disk utility is above 80%"
[ $utl_inode -ge $level ] && echo "Warning: disk indoe utility is above 80%"
[ $utl_disk -ge $level -o $utl_inode -ge $level ] && echo "Warning: disk utiltiy above 80%" || echo "Disk utility is safe"
- 編寫腳本 per.sh,判斷當(dāng)前用戶對指定參數(shù)文件,是否不可讀并且不可寫
if [ $# -lt 1 ];then
echo 'please input file'
exit 150
fi
if [ ! -e $1 ]; then
echo "file:$1 is not existing"
exit 151
fi
[ -r $1 -a -w $1 ] && echo "files can read and write" || echo "file:$1 can not be read and writen"
- 編寫腳本 excute.sh ,判斷參數(shù)文件是否為sh后綴的普通文件,如果是,添 加所有人可執(zhí)行權(quán)限,否則提示用戶非腳本文件
if [ $# -lt 1 ]; then
echo "please input file"
exit 150
fi
[ -f $1 ] && [[ $1 =~ .sh$ ]] && chmod a+x $1 || echo "error"
- 編寫腳本 nologin.sh和 login.sh,實現(xiàn)禁止和允許普通用戶登錄系統(tǒng)
獲取普通用戶
usermod -L/-U
- 編寫腳本 createuser.sh,實現(xiàn)如下功能:使用一個用戶名做為參數(shù),如果 指定參數(shù)的用戶存在,就顯示其存在,否則添加之;顯示添加的用戶的id號等 信息
if [ $# -lt 1 ]; then
echo "please input a user"
exit 150
fi
getent passwd $1 &>/dev/null
if [ $? -eq 0 ]; then
echo "$1 exist"
else
useradd $1
getent passwd $1
fi
- 編寫腳本 yesorno.sh,提示用戶輸入yes或no,并判斷用戶輸入的是yes還是 no,或是其它信息。
read -p "Enter Yes or No: " answer
if [[ $answer =~ y|Y|[yY][eE][sS] ]]; then
echo "your answer is YES"
elif [[ $answer =~ n|N|[nN][oO] ]]; then
echo "your anser is NO"
else
echo invalid input
fi
- 編寫腳本 filetype.sh,判斷用戶輸入文件路徑,顯示其文件類型(普通,目 錄,鏈接,其它文件類型
if [ $# -lt 1 ];then
echo "please input a file"
exit 150
fi
type=`ls -ald $1 | head -c1`
case $type in
'-')
echo "this is common file"
;;
'd')
echo "this is a directory"
;;
'l')
echo "this is a link"
;;
'p')
echo "this is a pipe file"
;;
's')
echo "this is a socket file"
;;
*)
echo "this is other file"
;;
esac
: