shell 編程之基礎(chǔ)篇

腳本規(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)行

  1. 賦予執(zhí)行權(quán)限,在命令行上指定腳本絕對或相對路徑
  2. bash yourscirpt.sh

shell script 調(diào)試

  1. 檢測腳本中的語法錯誤
    bash -n /path/to/some_script
  2. 調(diào)試執(zhí)行
    bash -x /path/to/some_script

變量

命名法則

  1. 數(shù)字、字母和下劃線
  2. 變量名大寫
  3. 局部變量小寫
  4. 函數(shù)名小寫
  5. 駝峰命名法

種類

名稱 含義
局部變量 生效范圍為當(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)部變量
位置變量 1,2,...來表示,用于調(diào)用腳本時傳遞參數(shù)值
特殊變量 $?, $0, $*, $@, $#, $$

局部變量

  1. 賦值
形式 說明
name= "root" 字符串
name= "$USER" 變量引用
name= `COMMAND` or name=$(COMMAND) 命令引用
  1. 變量引用 ${name} or $name
  • " " : 強(qiáng)引用,其中的變量引用會被替換為變量值
  • ' ':弱引用,其中變量引用不會被替換為變量值,保持原字符串
  1. 顯示已定義的所有變量
    set
  2. 刪除變量
    unset name

環(huán)境變量

  1. 變量聲明和賦值:
  • export name=value
  • declare -x name=value
  1. 變量引用
  • $name
  • ${name}
  1. 顯示所有環(huán)境變量
  • env
  • printenv
  • export
  • declare -x
  1. 刪除變量
    unset name
  2. 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ù)

只讀變量

聲明后,不能修改和刪除

  1. 聲明
  • readonly name
  • declare -r name
  1. 查看
    readonly -p

位置變量

name implication
$1,$2,... 對應(yīng)第1、第2各參數(shù),shift[n]換位置
$0 命令本身
$* 傳遞給腳本的所有參數(shù),全部參數(shù)合為一個字符串
$@ 傳遞給腳本的所有參數(shù),每個參數(shù)為獨立字符串
$# 傳遞給腳本的參數(shù)的個數(shù)
set -- 清空所有位置變量
note @ 和*只在被雙引號包起來的時候才會有差異

程序退出狀態(tài)

  1. 0 代表成功, 1-255代表失敗的code
  2. $? 變量保存最近的命令退出狀態(tài)
  3. exit [n] 自定義退出碼

算術(shù)運(yùn)算

  1. 運(yùn)算符號:+, -, *, /, %, **
  2. 實現(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

  1. 隨機(jī)數(shù)生成器變量 $RANDOM (0-32767)
echo $[$RANDOM%50]

賦值

  1. +=,-=,*=,/=,%=
  2. ++,--

邏輯運(yùn)算

  1. true: 0,false:1
  2. and
  3. or
  4. 非!
  5. 短路運(yùn)算
  6. 異或 ^

條件測試

測試命令

  1. test EXPRESSION
  2. [ EXPRESSION ]
  3. [[ EXPRESSION ]]
  4. 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

組合測試條件

  1. 第一種方式
  • EXPRESSION1 -a EXPRESSION2 并且

  • EXPRESSION1 -o EXPRESSION2 或者

  • ! EXPRESSION

  • 必須使用test,[[]]不支持 []支持么?

  1. 第二種方式
  • 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í)行操作符

  1. COMMAND1 && COMMAND2
    如果command1成功則執(zhí)行command2
  2. COMMAND1 $$ COMMAND2
    如果command1失敗則執(zhí)行command2
  3. 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語句

  1. 單分支
if 判斷條件;then
    條件為真的分支代碼
fi
  1. 雙分支
if 判斷條件;then
    條件為真的分支代碼
else
    條件為假的分支代碼
fi
  1. 多分支
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如何展開命令行 ?????

  1. 把命令行分成單個命令詞
  2. 展開別名
  3. 展開大括號的聲明({})
  4. 展開波浪符聲明(~)

防止擴(kuò)展

  1. \
  2. ' '

bash配置文件

全局配置文件

  1. /etc/profile
  2. /etc/profile.d/*.sh
  3. /etc/bashrc

個人配置文件

  1. ~/.bash_profile
  2. ~/.bashrc

shell登錄2中方式

  1. 交互式登錄
  • 通過終端輸入賬號密碼登錄
  • 使用 “su - UserName” 切換的目錄
  • 配置文件執(zhí)行順序:/etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile --> ~/.bashrc --> /etc/bashrc
  1. 非交互式登錄
  • su UserName
  • 圖形界面下打開的終端
  • 執(zhí)行腳本
  • 其它bash實例
  • 配置文件執(zhí)行順序:/etc/profile.d/*.sh --> /etc/bashrc --> ~/.bashrc

功能劃分

  1. Profile類
  • 為交互式登錄的shell提供配置
  • 全局:/etc/profile, /etc/profile.d/*.sh
  • 個人:~/.bash_profile
  • 公用:(1)用于定義環(huán)境變量(2)運(yùn)行命令或腳本
  1. 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)境變量等

配置文件生效方法

  1. source
  2. .

bash退出任務(wù)

  1. 保存在~/.bash_logout
  2. 退出shell登錄時運(yùn)行
  3. 功用:
  • 創(chuàng)建自動備份
  • 清除臨時文件

實踐

  1. 編寫腳本 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
  1. 編寫腳本 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]
  1. 編寫腳本 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]
  1. 編寫腳本 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
  1. 編寫腳本 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"
  1. 編寫腳本 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'
  1. 編寫腳本 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"
  1. 編寫腳本 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"

  1. 編寫腳本 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" 
  1. 編寫腳本 nologin.sh和 login.sh,實現(xiàn)禁止和允許普通用戶登錄系統(tǒng)
獲取普通用戶
usermod -L/-U
  1. 編寫腳本 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
  1. 編寫腳本 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

  1. 編寫腳本 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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容