[shell]-shell


第1章 shell基本概述

1.什么是shell

shell是一個命令解釋器,主要用來接收用戶的指令,進入驅動操作系統,或硬件。
Linux里有很多種shell,例如:
Bourne Shell(/usr/bin/sh或/bin/sh)
Bourne Again Shell(/bin/bash)
C Shell(/usr/bin/csh)
K Shell(/usr/bin/ksh)
Shell for Root(/sbin/sh)

2.什么是shell腳本

shell腳本就是把命令全部放在一起執行
shell腳本里可以包含若干個變量,循環,if判斷,for循環,函數等
特定的格式+特定的語法+系統的命令 = shell腳本

3.shell可以實現什么功能

1.Linux系統支持的命令,都可以用shell實現
2.系統優化腳本,例如:優化SSH 修改端口號 配置yum源 關閉SElinux,時間同步,安裝常用軟件等操作
3.定時任務,例如每天定時備份數據庫的數據
4.日志切割腳本,定時切割日志
5.服務啟動腳本,二進制安裝的服務沒有systemd,可以寫腳本啟動
6.代碼上線腳本,將開發好的代碼使用腳本部署到web服務器
7.zabbix自定義監控腳本,使用腳本獲取自定義的監控項的數值
8.跳板機腳本,可以使用shell開發一個跳板機

4.學習shell的必備技能

1.熟練的VIM技能
2.熟練的Linux基礎命令使用
3.熟練的正則表達式和三劍客命令使用

5.學習shell的正確姿勢

1.知道自己要干什么,想要什么效果
2.拿到需求先不要立刻寫腳本,先用命令行實現,然后轉換成腳本
3.先能看懂,然后模仿,然后會修改,最后能按照自己的需求編寫各種shell腳本
4.思考,練習,總結 --> 思考,練習,總結

第2章 shell入門

1.shell書寫方式

1.shell腳本名稱必須要有含義,切忌隨便起名,在公司里容易被打。文件后綴名最好以.sh結尾。
2.shell腳本首行建議添加使用的解釋器,如:#!/bin/bash
3.最好給自己的腳本加個注釋,注釋內容包含了腳本創建時間,作者,以及腳本作用等。
4.注釋盡量不要有中文
5.腳本放在專門的目錄里

舉例:

#!/bin/bash   	#! 是一個約定的標記,它告訴系統這個腳本需要什么解釋器來執行,即使用哪一種 Shell。
# Author: Oldzhang. 526195417@qq.com				 #作者名稱
# Create Time 2020/07/31							#創建日期
# Script Description: this is my 1st shell script.	   #腳本描述

2.第一個shell腳本

cat > hello.sh << 
#!/bin/bash
echo "Hello World"
EOF

3.shell執行方式

3.1 執行腳本命令

./test.sh
bash test.sh
source test.sh

3.2 首行不指定解釋器

1.如果不在腳本首行指定 #!/bin/bash解釋器,那么./執行的時候系統會默認調用bash來執行腳本。
2.那是如果我的腳本是python語言寫的,那么執行的使用就會報錯。

3.3 首行指定解釋器

1.如果首行添加了解釋器./執行的時候默認會讀取腳本第一行,來確定使用什么解釋器運行腳本。

3.4 直接指定解釋器運行

我們也可以直接指定使用什么解釋器來運行,那樣即使腳本首行沒有添加解釋器也可以運行,例如
bash test.sh
python test.sh

3.5 python的hello

#!/usr/bin/python3
hello = 'hellooooo'
print(hello)

第3章 shell變量

1.什么是變量

變量是Shell傳遞數據的一種方式。
以一個固定的字符串去表示一個不固定的值,便於后續的復用和維護。

2.變量的分類

環境變量(全局變量)  對整個系統生效
普通變量(局部變量)  只對當前的腳本生效

變量的生存周期
永久的 需要修改環境變量配置文件 變量永久生效 /etc/profile
臨時的 直接使用export聲明變量即可,關閉shell則變量失效

臨時變量的export區別
不加export 則只對當前的shell生效
加export   則對當前打開窗口所有的shell生效

3.環境變量配置文件生效的順序

1.登陸Shell首先會加載/etc/profile文件
2.然后會執行家目錄中的環境變量配置文件
3.按照執行順序
/etc/profile	
.bash_profile 
.bashrc
/etc/bashrc

4.變量的命名規范

1.以大小寫字母 下划線 數字 拼接成變量名,最好以字母開頭,最好名字有含義,不然寫着寫着很容易忘記這個變量干嘛用的
2.變量名=變量值 等號表示給變量賦值,注意等號兩邊不要有空格
3.系統的環境變量都是大寫的,注意不要用系統保留的變量名稱,比如PATH
4.變量命名最好不要與系統命令沖突,比如 date=20200731

變量命名參考:

path_data   #全小寫
Path_Date   #駝峰寫法,首字母大寫
PATH_DATA	#全大寫

5.變量定義的幾種方式

5.1 字符串定義變量

定義一個變量:

[root@m-61 ~]# name="oldya"

查看變量:

[root@m-61 ~]# echo "$name"
oldya
[root@m-61 ~]# echo "${name}"
oldya

5.2 命令定義變量

使用命令定義變量:

[root@m-61 ~]# time=`date`
[root@m-61 ~]# echo ${time}
2020年 07月 31日 星期五 19:11:17 CST
[root@m-61 ~]# time=$(date +%F)
[root@m-61 ~]# echo ${time}    
2020-07-31

命令定義變量兩種方式區別:

1.反引號 `命令` 和 $(命令) 都可以表示將命令賦值給變量
2.建議使用$(),因為如果腳本語句里包含單引號和雙引號,很容易和反引號搞混,另外也不方便閱讀。

5.3 引用變量

引用變量$和 ${}的區別:

[root@m-61 ~]# echo "$name_host"

[root@m-61 ~]# echo "${name}_host"
oldya_host

結論:

如果不加${},可能會造成歧義,使用${}更保險

單引號和雙引號區別:

[root@m-61 ~]# echo "name is ${name}" 
name is oldya
[root@m-61 ~]# echo 'name is ${name}'
name is ${name}

結論:

1.單引號不會解析變量,給什么,吐什么
2.雙引號可以正確解析變量

什么情況下使用單引號和雙引號:

1.如果需要解析變量,就用雙引號
2.如果輸出的結果要求是普通的字符串,或者需要正確顯示特殊字符,則可以用單引號或者撬棍\。

6.變量的傳遞

6.1 位置參數傳遞

執行腳本的時候我們可以通過傳遞參數來實現變量的賦值,接受參數的變量名是shell固定的,我們不能自定義.

腳本如下:

cat > vars.sh <<'EOF'
#!/bin/bash
echo "#當前shell腳本的文件名: $0"
echo "#第1個shell腳本位置參數:$1"
echo "#第2個shell腳本位置參數:$2"
echo "#第3個shell腳本位置參數:$3"
echo "#所有傳遞的位置參數是: $*"
echo "#所有傳遞的位置參數是: $@"
echo "#總共傳遞的參數個數是: $#"
echo "#當前程序運行的 PID 是: $$"
echo "#上一個命令執行的返回結果: $?"
EOF

執行效果:

bash vars.sh 11 22 33 44            
#當前shell腳本的文件名: vars.sh
#第1個shell腳本位置參數:11
#第2個shell腳本位置參數:22
#第3個shell腳本位置參數:33
#所有傳遞的位置參數是: 11 22 33 44
#所有傳遞的位置參數是: 11 22 33 44
#總共傳遞的參數個數是: 4
#當前程序運行的 PID 是: 11943
#上一個命令執行的返回結果: 0

練習題:

1.編寫腳本,通過變量傳參的形式免交互創建Linux系統用戶及密碼
2.編寫一個通過傳參自動修改主機名的腳本

6.2 交互式參數傳遞

腳本如下:

[root@m-61 ~]# cat read.sh 
#!/bin/bash

#-s 不回顯,就是不顯示輸入的內容
#-n 指定字符個數
#-t 超時時間

read -p "Login: " user
read -s -t20 -p "Passwd: " passwd
echo -e "\n===================="
echo -e "\nlogin: ${user} \npasswd: ${passwd}"

執行效果:

[root@m-61 ~]# bash read.sh 
Login: root
Passwd: 
====================

login: root 
passwd: 123456

練習題:

1.將前面練習的免交互創建用戶名密碼改寫為交互式腳本
2.模擬Linux系統登陸界面
3.編寫一個備份腳本,用戶傳遞2個參數,原目錄,目標目錄
4.編寫一個探測主機存活的腳本,交互式的用戶輸入需要測試的IP地址,然后探測IP地址是否存活
5.編寫一個交互式修改主機名和IP地址的腳本
6.編寫一個交互式的創建定時任務的腳本,提示用戶輸入分 時 日 月 周和任務,例如:
*/5 * * * * /sbin/ntpdate time1.aliyun.com > /dev/null 2>&1 

7.變量的運算

7.1 什么是變量運算

顧名思義,變量運算就是對變量的值進行運算,也就是 加 減 乘 除 取余

7.2 變量運算語法

expr 	     #只能做整數運算
$(()) 	   #雙括號運算,只支持整數運算,效率高
$[] 	     #整數運算,最簡潔
bc,awk 	   #支持小數點
% 		     #取余

7.3 舉例

expr

expr 10 + 10
expr 10 - 10
expr 10 * 10
expr 10 \* 10
expr 10 / 10 

num1=10
num2=20
expr ${num1} + ${num2}

$(( ))

echo $((10+10))
echo $((10-10))
echo $((10*10))
echo $((10/10))
echo $((10+10-5))
echo $((10+10-5*6))

num1=10
num2=20
echo $(($num1*$num2))

$[ ]

echo $[10+10]
echo $[10+10*20]
echo $[10+10*20-1000]
echo $[10+10*20/1000]

let

let a=10+10
echo $a

let a=10*10
echo $a    

let a=10/10
echo $a    

let a=$num1+$num2
echo $a

bc

echo 10*10|bc
echo 10*10.5|bc
echo 10-5.5|bc 
echo 10/5.5|bc

awk運算

awk 'BEGIN{print 10+10}'
awk 'BEGIN{print 10-10}'
awk 'BEGIN{print 10*10}'
awk 'BEGIN{print 10/10}'
awk 'BEGIN{print 10^10}'
awk 'BEGIN{print 10-4.5}'
awk 'BEGIN{print 10*4.5}'
awk 'BEGIN{print 10/4.5}'

7.4 練習題

練習題1:根據系統時間打印出今年和明年時間

[root@m-61 ~]# echo "this year is $(date +%Y)"
this year is 2020
[root@m-61 ~]# echo "this year is $(( $(date +%Y) + 1 ))" 
this year is 2021

練習題2:根據系統時間獲取今年還剩下多少星期,已經過了多少星期

[root@m-61 ~]# date +%j
214
[root@m-61 ~]# date +%U
30
[root@m-61 ~]# echo "今年已經過了 $(date +%j) days"
今年已經過了 214 days
[root@m-61 ~]# echo "今年還剩 $(( ( 365 - $(date +%j) ) / 7 )) 周"  
今年還剩 21 周
[root@m-61 ~]# echo "今年還剩 $(( ( (365 / 7) - $(date +%U)) )) 周"
今年還剩 22 周

練習題3:完成簡單計算功能,通過read方式傳入2個值,進行加減乘除

[root@m-61 ~]# cat vars-2.sh 
#!/bin/bash

read -p "請輸入要計算的第一個數字: " num1
read -p "請輸入要計算的第二個數字: " num2

echo "$num1 + $num2 = $(( $num1 + $num2 ))"
echo "$num1 - $num2 = $(( $num1 - $num2 ))"
echo "$num1 * $num2 = $(( $num1 * $num2 ))"
echo "$num1 / $num2 = $(( $num1 / $num2 ))"

8.作業

概念解釋:

1.簡單介紹shell腳本是什么,使用場景有哪些?
2.shell腳本的書寫規范是什么?
3.shell腳本變量的定義方式有幾種?
4.shell腳本如何引用變量?
5.shell腳本特殊變量的意思是什么?$0 $1 $2 $* $@ $# $$ $?
6.變量的運算方式

練習題:

0.今天課上的練習題自己全部寫一遍
1.使用Shell腳本打印,系統版本、內核版本平台、主機名、eth0網卡IP地址、lo網卡IP地址、當前主機的外網IP地址,提醒:curl icanhazip.com
2.看誰能用最精簡的腳本實現一個計算器
3.查看當前內存使用的百分比和系統已使用磁盤的百分比

拓展:

1.如何創建shell腳本的時候自動把#!/bin/bash和注釋內容加上去
2.如何讓shell腳本的輸出改變顏色
3.思考今天所寫的腳本邏輯判斷是否嚴謹

第4章 條件判斷

1.基於文件進行判斷

1.1 參數說明

2.2 語法

第一種寫法

test -f /etc/passwd && echo "true" || echo "false"

第二種寫法

[ -f /etc/passwdd ] && echo "true" || echo "false"

2.3 練習

[ -f /etc/passwd ] && echo "文件存在" || echo "文件不存在"
[ -e /etc/passwd ] && echo "文件存在" || echo "文件不存在"
[ -r /etc/passwd ] && echo "文件可讀" || echo "文件不可讀"
[ -w /etc/passwd ] && echo "文件可寫" || echo "文件不可寫"
[ -x /etc/passwd ] && echo "文件可執行" || echo "文件不可執行"

[ -s /dev/zero ] && echo "true"||echo "false"
[ -s /dev/null ] && echo "true"||echo "false"
[ -s /etc/passwd ] && echo "true"||echo "false"

[ -f /dev/zero ] && echo "true"||echo "false"
[ -f /dev/null ] && echo "true"||echo "false"
[ -f /etc/passwd ] && echo "true"||echo "false"  

2.基於整數進行判斷

2.1 參數說明

2.2 練習

單個條件

source /etc/init.d/functions 
[ 1 -eq 2 ] && action OK /bin/true || action NO /bin/false
[ 1 -ne 2 ] && action OK /bin/true || action NO /bin/false
[ 1 -gt 2 ] && action OK /bin/true || action NO /bin/false
[ 1 -lt 2 ] && action OK /bin/true || action NO /bin/false
[ 1 -ge 2 ] && action OK /bin/true || action NO /bin/false
[ 1 -le 2 ] && action OK /bin/true || action NO /bin/false

多個條件

[ 1 -eq 1 -a 2 -gt 1 ] && action OK /bin/true || action NO /bin/false
[ 1 -eq 1 -o 2 -gt 2 ] && action OK /bin/true || action NO /bin/false

3.基於字符串進行判斷

3.1 參數說明

3.2 練習

[ 10 == 10 ] && echo "==" || "><"
[ 10 != 5 ] && echo "==" || "><"
name=123
[ -z "$name" ] && echo "true"||echo "false"
[ -n "$name" ] && echo "true"||echo "false"

第5章 shell流程控制之if

1.if單分支

偽代碼:

if [ 你是男孩子 ];then
   出門在外要保護好自己
fi

if [ 你是女孩子 ]
then
   無論身時候都不要相信男人說的話
fi

舉例:

[root@m-61 ~/scripts]# cat if-1.sh 
#!/bin/bash

if [ "$1" -eq "$2" ]
then
   echo "ok"
fi
[root@m-61 ~/scripts]# bash if-1.sh 2 2
ok
[root@m-61 ~/scripts]# bash if-1.sh 2 4
[root@m-61 ~/scripts]# 

2.雙條件分支

偽代碼:

if [ 你是男孩子 ]
then
   出門在外要保護好自己
else
   不要相信男人說的話
fi

舉例:

if [ "$1" -eq "$2" ]
then
   echo "ok"
else
   echo "error"
fi

3.多條件分支

if [ 你是男孩子 ];then
    出門在外要保護好自己
elif [ 你是女孩子 ];then
    不要相信男人說的話
else 
    你是吃什么長大的
fi

舉例:

#!/bin/bash

if [ $1 -eq $2 ];then
   echo "=="
elif [ $1 -gt $2 ];then
   echo ">"
else 
   echo "= or >"
fi

4.練習題

4.0 完善的計算機腳本

#!/bin/bash

#read -p "請輸入:" memu 
num1=$1
num2=$2
int=$(echo ${num1}${num2}|sed -r 's#[0-9]+##g')

if [ $# -ne 2 ];then
   echo "請輸入2個參數"
   exit
elif [ -z ${int} ];then
   echo "${num1} + ${num2} = $[ ${num1} + ${num2} ]"
   echo "${num1} - ${num2} = $[ ${num1} - ${num2} ]"
   echo "${num1} * ${num2} = $[ ${num1} * ${num2} ]"
   echo "${num1} / ${num2} = $[ ${num1} / ${num2} ]"
else
   echo "請輸入2個整數"
fi

4.0 使用IF選擇的計算器

需求:

1.使用rede讀取用戶輸入的數字和符號
2.符號使用菜單供用戶選擇
3.符號使用if作為判斷

菜單如下:
請輸入第一個數字:10
請輸入第二個數字:20
請選擇運算符號:
1. +
2. - 
3. *
4. / 
請輸入您的選擇:1

顯示結果:
10 + 20 = 30 

腳本:

#!/bin/bash

read -p "請輸入要計算的第一個數字: " num1
read -p "請輸入要計算的第二個數字: " num2
echo -e "請選擇運算符號:
1. + 
2. - 
3. * 
4. /"

read -p "請輸入您的選擇: " fuhao

if [ $fuhao == 1 ];then
   echo "$num1 + $num2 = $(( $num1 + $num2 ))"
elif [ $fuhao == 2 ];then
   echo "$num1 - $num2 = $(( $num1 - $num2 ))"
elif [ $fuhao == 3 ];then
  echo "$num1 * $num2 = $(( $num1 * $num2 ))"
elif [ $fuhao == 4 ];then
  echo "$num1 / $num2 = $(( $num1 / $num2 ))"
else 
  echo "請輸入1-4"
fi

加入輸錯判斷的腳本:

#!/bin/bash

read -p "請輸入要計算的第一個數字: " num1
if [ ! -z $(echo ${num1}|sed -r 's#[0-9]+##g') ];then
  echo "請輸入整數"
  exit
fi

read -p "請輸入要計算的第二個數字: " num2
if [ ! -z $(echo ${num2}|sed -r 's#[0-9]+##g') ];then
  echo "請輸入整數"
  exit
fi

echo -e "請選擇運算符號:
1. + 
2. - 
3. * 
4. /"

read -p "請輸入您的選擇: " fuhao

if [ $fuhao == 1 ];then
   echo "$num1 + $num2 = $(( $num1 + $num2 ))"
elif [ $fuhao == 2 ];then
   echo "$num1 - $num2 = $(( $num1 - $num2 ))"
elif [ $fuhao == 3 ];then
  echo "$num1 * $num2 = $(( $num1 * $num2 ))"
elif [ $fuhao == 4 ];then
  echo "$num1 / $num2 = $(( $num1 / $num2 ))"
else 
  echo "請輸入1-4"
fi

4.1 備份文件,如果目錄不存在就自動創建

#!/bin/bash

if [ -e /backup/ ];then
   echo "目錄已經存在"
else
   mkdir /backup/ -p

4.2 接上一題,判斷備份的文件是否存在,如果不存在就提示,然后推出

#!/bin/bash 

if [ -e /backup/ ];then
   echo "目錄已經存在"
else
   mkdir /backup/ -p
fi

if [ -f /backup/tar.gz ];then
   echo "文件已經存在"
else
   echo "備份文件中..."
   echo "文件已經創建"
fi

4.3 接上一題,判斷備份文件是否為空,如果空就提示,然后推出

if [ -e /backup/ ];then
   echo "目錄已經存在"
else
   mkdir /backup/ -p
fi

if [ -f /backup/tar.gz ];then
   echo "文件已經存在"
else
   echo "備份文件中..."
   echo "文件已經創建"
fi

if [ -s /backup/tar.gz ];then
   echo "文件為空"
else
   echo "文件不為空"
fi

4.4 用戶執行腳本,傳遞一個參數作為服務名,檢查服務狀態。

#!/bin/bash

if [ $# -eq 1 ];then 
    #檢查服務的狀態
    systemctl status $1 &>/dev/null 
    #判斷服務運行的結果
    if [ $? -eq 0 ];then
        echo "$1 服務正在運行" 
    else
        echo "$1 服務沒有運行" 
    fi
else
    echo "USAGE: sh $0 service_name"
    exit 
fi

4.5 查看磁盤/當前使用狀態,如果使用率超過30%則報警發郵件

梳理思路:

1.查看磁盤分區的狀態命令是什么?
2.提取/分區的狀態百分比命令是什么?
3.將提取出來的狀態百分比和我們設置的閾值進行對比,超過30%報警,不超過就不處理
4.將處理結果寫入到文件里

腳本:

#!/bin/bash

#1.提取磁盤使用的百分比
Disk_Status=$(df -h | grep '/$' |awk '{print $5}'|sed 's#%##g')
Time=$(date +%F-%T)

#2.判斷磁盤使用百分比是否超過30,如果超過,則寫入一個文件中。 
if [ ${Disk_Status} -ge 30 ];then
    echo "${USER}:${Time}: Disk Is Use ${Disk_Status}" >> /tmp/disk_use.txt
fi

4.6 判斷用戶輸入的內容是否為空,如果為空或者直接按回車,則提醒,否則輸出用戶輸入的內容。

#!/bin/bash

read -p "請輸入內容: " word

if [ -z ${word} ];then
    echo "輸入的內容為空."
else
    echo "輸入的內容為:${word}" 
fi

4.7 編寫一個用來檢查用戶的uid和gid是否一致的腳本

1.使用交互式接收用戶輸入的用戶名作為參數
2.如果用戶不存在,就輸出提醒然后退出腳本
3.如果用戶存在,判斷這個用戶的uid和gid是否一致
4.如果uid和gid一致,則輸出正確信息並打印出用戶的uid和gid,如果不一致則輸出實際的uid和gid

思路:

1.判斷用戶是否存在的命令是什么?
2.提取uid和gid的命令是什么?
3.對比uid和gid的命令是什么?

第一種寫法:思考,這樣寫有沒有問題

#!/bin/bash

USER=$1
USER_OK=$(grep -w "^${User}" /etc/passwd|wc -l)
UID=$(awk -F":" "\$1 ~ /^${User}$/"'{print $3}' /etc/passwd)
GID=$(awk -F":" "\$1 ~ /^${User}$/"'{print $4}' /etc/passwd)

if [ "${USER_OK}" -eq 1 ] && [ "${UID}" -eq "${GID}" ];then
    echo "用戶uid與gid一致"
    echo "UID: ${UID}"
    echo "GID: ${GID}"
elif [ "${USER_OK}" -eq 1 -a "${UID}" ! -eq "${GID}" ];then
    echo "用戶uid與gid不一致"
    echo "UID: ${UID}"
    echo "GID: ${GID}"
else
    echo "查詢的用戶不存在" 
fi

完善的判斷腳本:

#!/bin/bash

USER=$1
USER_Ok=$(grep -w "^${User}" /etc/passwd|wc -l)
UID=$(awk -F":" "\$1 ~ /^${User}$/"'{print $3}' /etc/passwd)
GID=$(awk -F":" "\$1 ~ /^${User}$/"'{print $4}' /etc/passwd)

#1.判斷是否存在這個用戶
if [ "${USER_Ok}" -eq 0 ];then
    echo "查詢的用戶用戶不存在"
    exit
elif [ "${UID}" -eq "${GID}" ];then
    echo "用戶uid與gid一致"
    echo "UID: ${UID}"
    echo "GID: ${GID}"
else
    echo "用戶uid與gid不一致"
    echo "UID: ${UID}"
    echo "GID: ${GID}"
fi

難點:

1.awk如何使用變量
2.如果用戶名字符串有重復的內容如何精確定位
3.判斷的邏輯如何精簡

4.8 成績查詢

提醒用戶輸入自己的成績
1.如果分數大於0小於59則提示需要補考
2.如果分數大於60小於85則提示成績良好
3.如果分數大於86小於100提示成績優秀

腳本:

#!/bin/bash

read -p "來查成績吧:" score

if [ ${score} -ge 0 ] && [ ${score} -le 59 ];then
    echo "補考吧兄弟"
elif [ ${score} -ge 59 ] && [ ${score} -le 85 ];then
    echo "這次饒你一命"
elif [ ${score} -ge 86 ] && [ ${score} -le 100 ];then
    echo "這么厲害,你是吃什么長大的"
else 
    echo "查詢范圍是0-100哦"
    exit
fi

思考:這個腳本存在的缺陷

1.如果用戶輸入了多個參數或者沒有輸入參數呢
2.如果用戶輸入的不是說字而是字符串呢

完善之后的腳本:

#!/bin/bash

if [ $# != 0 ];then
    echo "請不要帶參數查詢"
    exit
else 
    read -p "來查成績吧:" score
    if_num=$(echo "${score}"|sed -r 's#[0-9]+##g')
    
    if [ -n "${if_num}" ];then
        echo "請輸入整數"
        exit 
    elif [ ${score} -ge 0 ] && [ ${score} -le 59 ];then
        echo "補考吧兄弟"
    elif [ ${score} -ge 59 ] && [ ${score} -le 85 ];then
        echo "這次饒你一命"
    elif [ ${score} -ge 86 ] && [ ${score} -le 100 ];then
        echo "這么厲害,你是吃什么長大的"
    else 
        echo "查詢范圍是0-100哦"
        exit
    fi
fi

4.9 判斷輸入的數字是否為整數方法

#!/bin/bash

input=$1 

#第一種方法
expr ${input} + 1 > /dev/null 2>&1
if [ $? != 0 ];then
    echo "請輸入整數"
fi

#第二種方法
num=$(echo ${input}|sed -r 's#^[0-9]+##g')
if [ -n "${num}" ];then
    echo "請輸入整數"
fi

#第三種方法
if [[ ! "${input}" =~ ^[0-9]+$ ]];then 
    echo "請輸入純數字"
fi

4.10 查詢nginx服務狀態

深圳5期-夏燕同學提供

#!/bin/bash

read -p "請輸入您要查詢的服務名稱:" SEname
rpm -qa|grep ${SEname} > /dev/null  2>&1
if [ $? -eq 0 ];then
  read -p "請選擇這行的步驟:
1.start
2.stop
3.restart
4.status
請輸入您的選擇:" SEst
else
  echo "您要查詢的服務沒有安裝"
  exit
fi

if [ ${SEst} -eq 1 ];then
  systemctl start ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    echo "${SEname} 啟動成功"
  else
    echo "${SEname} 啟動失敗"
  fi
elif [ ${SEst} -eq 2 ];then
  systemctl stop ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    echo "${SEname} 已經停止"
  else
    echo "${SEname} 停止失敗"
  fi
elif [ ${SEst} -eq 3 ];then
  systemctl restart ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    echo "${SEname} 重啟成功"
  else
    echo "${SEname} 重啟失敗"
  fi
elif [ ${SEst} -eq 4 ];then
  systemctl restart ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    echo "${SEname} 正在查詢狀態"
  else
    echo "${SEname} 查詢失敗"
  fi
else
  echo "請重新輸入您的選擇,只能輸入1-4"
fi

深圳5期-紀城

#!/bin/sh

source /etc/init.d/functions
read -p "請輸入你要查詢的服務名稱: " MING
  rpm -qa $MING >/tmp/1.txt
  if [ ! -s /tmp/1.txt ];then
    echo "輸入的服務不存在";exit
  fi
echo -e "請選擇執行的步驟: \n1.start\n2.stop\n3.restart\n4.status"
read -p "請輸入你的選擇: " XUAN
if [ $XUAN -eq 1 ];then
  systemctl start $MING >/dev/null 2>&1
  [ $? -eq 0 ] && action "$MING 啟動成功" /bin/true || action "$MING 啟動失敗" /bin/false
elif [ $XUAN -eq 2 ];then
  systemctl stop $MING >/dev/null 2>&1
  [ $? -eq 0 ] && action "$MING 關閉成功" /bin/true || action "$MING 關閉失敗" /bin/false
elif [ $XUAN -eq 3 ];then
  systemctl restart $MING >/dev/null 2>&1
  [ $? -eq 0 ] && action "$MING 重啟成功" /bin/true || action "$MING 重啟失敗" /bin/false
elif [ $XUAN -eq 4 ];then
  systemctl status $MING >/dev/null 2>&1
  [ $? -eq 0 ] && action "$MING 服務開啟" /bin/true || action "$MING 服務關閉" /bin/false
else
  echo "請重新輸入正確的選擇項"
fi

小張白嫖版

#!/bin/bash

source /etc/init.d/functions
read -p "請輸入您要查詢的服務名稱:" SEname
rpm -ql ${SEname} > /dev/null  2>&1
if [ $? -eq 0 ];then
  read -p "請選擇這行的步驟:
1.start
2.stop
3.restart
4.status
請輸入您的選擇:" SEst
else
  echo "您要查詢的服務沒有安裝"
  exit
fi

if [ ${SEst} -eq 1 ];then
  echo "${SEname} 啟動中..."
  sleep 1
  systemctl start ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    action "${SEname} 啟動成功" /bin/true
  else
    action "${SEname} 啟動失敗" /bin/false
  fi
elif [ ${SEst} -eq 2 ];then
  echo "${SEname} 停止中..."
  sleep 1
  systemctl stop ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    action "${SEname} 已經停止" /bin/true
  else
    action "${SEname} 停止失敗" /bin/false
  fi
elif [ ${SEst} -eq 3 ];then
  echo "${SEname} 重啟中..."
  sleep 1
  systemctl restart ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    action "${SEname} 重啟成功" /bin/true
  else
    action "${SEname} 重啟失敗" /bin/false
  fi
elif [ ${SEst} -eq 4 ];then
  systemctl restart ${SEname} > /dev/null 2>&1
  if [ $? -eq 0 ];then
    echo "${SEname} 正在查詢狀態"
  else
    action "${SEname} 查詢失敗" /bin/false
  fi
else
  echo "請重新輸入您的選擇,只能輸入1-4"
fi

5.作業

1.猜數字
2.多極菜單
3.根據選擇安裝不同軟件
4.編寫服務啟動腳本
5.編寫系統優化腳本
- 根據系統版本選擇對應的YUM源
- 關閉防火牆和selinux
- 將時間同步寫入定時任務
- 安裝常用軟件
- 修改主機名和IP地址
6.日志切割腳本

第4章 shell流程控制之case

1.case介紹

case和if都是用來處理多分支判斷的,只不過case更加簡潔和規范一些。

2.case使用場景

我們可以根據用戶輸入的參數來進行匹配,不同的匹配選項執行不同的操作步驟。
比如:服務的啟動腳本 {start|restart|stop} 等操作

3.case基本語法

case $1 in. 
start)
  command
  ;;
restart)
  command
  ;;
stop)
  command
  ;;
*)
  command
esac  

4.if和case的區別

下面我們以一個小例子來說明if和case的區別

4.1 需求

根據用戶選擇的序號執行相應的操作

4.2 if的寫法

#!/bin/bash

echo -e "================
1.取錢
2.存錢
3.還剩多少
================"

read -p "請輸入你要執行的操作: " num

if [ "${num}" -eq 1 ];then
    echo "取好了"
elif [ "${num}" -eq 2 ];then
    echo "存好了"
elif [ "${num}" -eq 3 ];then 
    echo "還剩-100元"
else
    echo "輸入有誤,下次走點心"
fi

4.3 case的寫法

#!/bin/bash

echo -e "================
1.取錢
2.存錢
3.還剩多少
================"

read -p "請輸入你要執行的操作: " num

case ${num} in 
    1)
        echo "取好了"
        ;;
    2)
        echo "存錢"
        ;;
    3)
        echo "還剩-100元"
        ;;
    *)
        echo "其輸入正確的數據"
esac

5.綜合練習題

5.1 使用case編寫友好輸出的計算器

需求:

1.交互式接受用戶輸入的數字和計算方法
3.判斷用戶輸入的參數是否為3個
4.判斷用戶輸入的是否為整數數字
5.判斷用戶輸入的符號是否為+-*%
6.如果用戶輸入錯誤則友好提醒正確的使用方法

腳本:

#!/bin/bash

read -p "請輸入要計算的第一個數字: " num1
if [ ! -z $(echo ${num1}|sed -r 's#[0-9]+##g') ];then
  echo "請輸入整數"
  exit
fi

read -p "請輸入要計算的第二個數字: " num2
if [ ! -z $(echo ${num2}|sed -r 's#[0-9]+##g') ];then
  echo "請輸入整數"
  exit
fi

echo -e "請選擇運算符號:
1. + 
2. - 
3. * 
4. /"

read -p "請輸入您的選擇: " fuhao

case ${fuhao} in
    1)
        echo "$num1 + $num2 = $(( $num1 + $num2 ))"
        ;;
    2)
        echo "$num1 - $num2 = $(( $num1 - $num2 ))"
        ;;
    3)
        echo "$num1 * $num2 = $(( $num1 * $num2 ))"
        ;;
    4)
        echo "$num1 / $num2 = $(( $num1 / $num2 ))"
        ;;
    *)
        echo "請輸入1-4"
esac

5.2 編寫非交互的服務啟動腳本

需求:

1.使用case編寫非交互的服務管理腳本
2.如果用戶輸入參數錯誤,則友好提醒腳本的使用方法

腳本:

#!/bin/bash

source /etc/init.d/functions

SERVICE=$1
case ${SERVICE} in
  start)
      echo "nginx 啟動中..."
      sleep 1
      nginx
      if [ $? -eq 0 ];then
        action "nginx 啟動成功" /bin/true
      else
        action "nginx 啟動失敗" /bin/false
      fi
      ;;
  stop)
      echo "nginx 停止中..."
      sleep 1
      nginx -s stop
      if [ $? -eq 0 ];then
        action "nginx 已經停止" /bin/true
      else
        action "nginx 停止失敗" /bin/false
      fi
      ;;
  restart)
      echo "nginx 重啟中..."
      nginx -s stop
      sleep 1
      nginx
      if [ $? -eq 0 ];then
        action "nginx 重啟成功" /bin/true
      else
        action "nginx 重啟失敗" /bin/false
      fi
      ;;
  reload)
      nginx -s reload
      if [ $? -eq 0 ];then
        action "nginx 正在重新載入" /bin/true
      else
        action "nginx 重新載入失敗" /bin/false
      fi
      ;;
  check)
      nginx -t 
      ;;
  *) 
      echo "Usage: {start|stop|restart|reload|check}"
esac

5.3 模擬用戶登陸

需求:

1.提前創建一個用戶記錄文件,格式為
用戶名:密碼
2.用戶執行腳本打印菜單
- 登陸
- 注冊
3.登陸菜單的選項
- 請輸入賬號名
- 請輸入密碼
- 如果賬號密碼正確,則登陸成功
4.注冊
- 請輸入用戶名,然后檢查用戶名是否已經存在
- 請輸入密碼
- 請再次輸入密碼
- 將用戶名和密碼寫入文本里,如果成功則返回注冊成功的結果
- 返回登陸頁面

腳本:

#!/bin/bash

name_list=bank.txt
log=log.txt
time=$(date)

echo "
===========
1.登陸
2.注冊
===========
"
read -p "請選擇需要的操作:" memu 

case ${memu} in 
  1)
      read -p "請輸入用戶名:" name
      grep -wo "${name}" ${name_list} >> /dev/null 2>&1
      if [ $? != 0 ];then
         echo "用戶名不存在,請重新輸入"
         exit
      fi

      read -p "請輸入密碼:" passwd_input
      passwd_user=$(awk -F":" "/^${name}\:/"'{print $2}' ${name_list})
      #passwd_user=$(sed -rn "s#${name}:(.*)#\1#g"p bank.txt)
      if [ ${passwd_input} == ${passwd_user} ];then
         echo "登陸成功!"
         echo "${time} ${name} 登陸成功!" >> ${log}
      else 
         echo "密碼錯誤,請重新輸入"
         echo "${time} ${name} 登陸失敗!" >> ${log}
         exit
      fi
      ;;

  2)
      read -p "請輸入注冊用戶名:" name
      grep -wo "${name}" ${name_list} >> /dev/null 2>&1
      if [ $? = 0 ];then
         echo "用戶名已存在,再選一個吧"
         exit
      else 
         read -p "請輸入密碼:" passwd1
         read -p "請再次輸入密碼:" passwd2
         if [ ${passwd1} == ${passwd2} ];then
            echo "${name}:${passwd1}" >> ${name_list}
            if [ $? == 0 ];then
               echo "注冊成功,請登錄"
               echo "${time} ${name} 注冊成功!" >> ${log}
            else 
               echo "注冊失敗,請聯系管理員"
               echo "${time} ${name} 注冊失敗!" >> ${log}
            fi
         else
            echo "兩次輸入的密碼不一致,請重新輸入"
            exit
         fi
      fi
      ;;

  *)
      echo "請選擇1-2的數字"
esac

5.4 模擬用戶銀行取錢

需求:

1.提前創建一個用戶記錄文件,格式為
用戶名:密碼:存款數
2.用戶執行腳本提醒輸入賬號密碼
- 如果用戶不存在則提醒輸入正確用戶
- 如果用戶密碼不對則提醒輸入正確賬號密碼
3.如果賬號和密碼都輸入正確則打印功能菜單
- 查詢余額
- 存錢
- 取錢
- 退出
4.限定條件
- 存錢必須是正整數,不能是負數,小數點或字母
- 取錢必須是正整數,不能是負數,小數點或字母
- 取錢不能超過余額總數
5.根據用戶選擇的菜單功能進行余額的更新
6.將用戶操作的信息和時間都打印到日志里
7.管理員功能
- 修改金額
- 修改密碼
- 黑名單
- 白名單
- 金蟬脫殼

腳本:

#!/bin/bash

BK=bank_info.txt
LOG=bank_log.txt
TIME=$(date)
BK_BL=bank_black.txt
SS=~/.ssh/id_rsa

if [ ! -f ${BK} ];then
    touch ${BK}
    touch ${BK_BL}
    echo "${TIME} 創建${BK} ${BK_BL}成功" >> ${LOG}
fi

echo "
歡迎來到天堂銀行:
1)登錄
2)注冊
"
read -p "請輸入您的選擇:" menu
case $menu in 
    1)
        #用戶輸入賬號
        read -p "請輸入您的賬戶:" input_name 

        #判斷用戶是否被拉黑
        name=$(grep "^${input_name}:" ${BK_BL}|wc -l)        
        if [ ${name} -ne 0 ];then
           echo "您已被列入黑名單!"
           exit
        fi
 
        #判斷用戶是否存在
        name=$(grep "^${input_name}:" ${BK}|wc -l)        
        if [ ${name} -eq 0 ];then
           echo "登錄用戶不存在"
           exit
        else 
            #用戶輸入密碼
            read -p "請輸入您的密碼:" input_pass 

            #判斷密碼是否正確
            pass=$(awk -F ":" '/^'"${input_name}"':/{print $2}' ${BK} )
            if [ ${input_pass} == ${pass} ];then
               echo "${TIME} ${input_name} 登錄成功!" >> ${LOG}
               echo "登錄成功!"
            else
               echo "${TIME} ${input_name} 登錄失敗!" >> ${LOG}
               echo "密碼錯誤!"
               exit
            fi
        fi
 
        #用戶菜單        
        echo " 
        1)查詢
        2)存錢
        3)取錢
        " 
        read -p "請輸入您的選擇:" menu 
 
        case $menu in 
            1)
                #查詢
                money=$(awk -F ":" '/^'"${input_name}"':/{print $3}' ${BK} )
                echo "您的余額為: ${money}億元"
		;;
            2)
                #判斷輸入是否為整數
                read -p "請輸入您要存的金額: " input_money
                money_int=$(echo "${input_money}"|sed -r 's#[0-9]+##g')
                if [ ! -z ${money_int} ];then
                   echo "請輸入整數金額"
                   exit
                fi
                
                #存錢操作
                money=$(awk -F ":" '/^'"${input_name}"':/{print $3}' ${BK} )
                new_money=$[ ${money} + ${input_money} ]
                sed -ri "/^${input_name}:/s#(.*):(.*)#\1:${new_money}#g" ${BK}
                if [ $? == 0 ];then
                   echo "${TIME} ${input_name} 存入成功,最新余額為: ${new_money}" >> ${LOG}
                   echo "存入成功,最新余額為: ${new_money}"
                else
                   echo "${TIME} ${input_name} 存入失敗,最新余額為: ${new_money}" >> ${LOG}
                   echo "存錢失敗,請聯系管理員"
                fi
		;;
            3)
                #輸出余額
                money=$(awk -F ":" '/^'"${input_name}"':/{print $3}' ${BK} )
                echo "最新余額為: ${money}"
                read -p "請輸入您取走的金額: " input_money
                
                #判斷輸入是否為整數
                money_int=$(echo "${input_money}"|sed -r 's#[0-9]+##g')
                if [ ! -z ${money_int} ];then
                   echo "請輸入整數金額"
                   exit
                fi

                #判斷取錢是否超過余額
                money=$(awk -F ":" '/^'"${input_name}"':/{print $3}' ${BK} )
                if [ ${input_money} -gt ${money} ];then
                    echo "${TIME} ${input_name} 取錢失敗,余額不足: ${money}" >> ${LOG}
                    echo "取錢失敗,余額不足"
                    exit
                fi
           
                #執行取錢操作
                new_money=$[ ${money} - ${input_money} ]
                sed -ri "/^${input_name}:/s#(.*):(.*)#\1:${new_money}#g" ${BK}
 
                #判斷是否取錢成功
                if [ $? == 0 ];then
                   echo "${TIME} ${input_name} 取錢成功,最新余額為: ${new_money}" >> ${LOG}
                   echo "取錢成功,最新余額為: ${new_money}"
                else
                   echo "${TIME} ${input_name} 取錢失敗,最新余額為: ${new_money}" >> ${LOG}
                   echo "取錢失敗,請聯系管理員"
                fi
		;;
            *)
                echo "1-3"
        esac 
        ;;
    2)
        read -p "請輸入您的賬戶:" input_name
        
        #判斷用戶是否被拉黑
        name=$(grep "^${input_name}:" ${BK_BL}|wc -l)        
        if [ ${name} -ne 0 ];then
           echo "您已被列入黑名單!"
           exit
        fi

        #判斷用戶是否存在
        name=$(grep "^${input_name}:" ${BK}|wc -l)        
        if [ ${name} -ne 0 ];then
           echo "用戶已存在,換個吧!"
           exit
        fi
        
        #判斷密碼是否有非法字符和整數
        read -p "請輸入您的密碼:" input_pass1 
        read -p "請輸入您的密碼:" input_pass2
        if [ $input_pass1 == $input_pass2 ];then
            pass=$(echo "${input_pass1}"|sed -r 's#[0-9]+##g')
            if [ ! -z ${pass} ];then
               echo "請輸入整數密碼"
               exit
            fi 
        else
            echo "兩次輸入的密碼不一致"
            exit
        fi

        #判斷金額是否為整數
        read -p "請存錢:" input_money
        money_int=$(echo "${input_money}"|sed -r 's#[0-9]+##g')
        if [ ! -z ${money_int} ];then
           echo "請輸入整數金額"
           exit
        fi

        echo "${input_name}:${input_pass1}:${input_money}" >> ${BK}
        echo "${TIME} ${input_name} 注冊成功,最新余額為:${input_money}" >> ${LOG}
        echo "注冊成功!請登陸!"
        ;;
    admin)
        echo "${TIME} admin 登錄成功" >> ${LOG} 
        echo "
        1)修改余額
        2)修改密碼
        3)黑名單
        4)白名單
        5)金蟬脫殼
        "
        read -p "請選擇:" menu 
        case $menu in 
            1) 
                echo "當前用戶余額信息:"
                echo "$(awk -F":" '{print $1":"$3}' ${BK})" 

                #用戶輸入賬號
                read -p "請輸入您的賬戶:" input_name 
 
                #判斷用戶是否存在
                name=$(grep "^${input_name}:" ${BK}|wc -l)        
                if [ ${name} -eq 0 ];then
                    echo "登錄用戶不存在"
                    exit
                fi

                #判斷輸入是否為整數
                read -p "請輸入要修改的整數金額:" input_money1
                money_int=$(echo "${input_money1}"|sed -r 's#[0-9]+##g')
                if [ ! -z ${money_int} ];then
                   echo "請輸入整數金額"
                   exit
                else
                   read -p "請輸入要修改的整數金額:" input_money2
                   if [ $input_money1 != $input_money2 ];then
                       echo "兩次輸入的金額不一致"
                       exit
                   fi
                fi
              
                #修改金額
                sed -ri "/^${input_name}:/s#(.*):(.*)#\1:${input_money1}#g" ${BK}

                #判斷是否取錢成功
                if [ $? == 0 ];then
                   echo "${TIME} admin 修改 ${input_name} 余額成功,最新余額為: ${input_money1}" >> ${LOG}
                   echo "修改余額成功,最新余額為: ${input_money1}"
                else
                   echo "${TIME} admin 修改 ${input_name} 余額失敗,最新余額為: ${input_money1}" >> ${LOG}
                   echo "修改余額失敗"
                fi
                ;;
            2)
                echo "當前用戶信息:"
                echo "$(awk -F":" '{print $1}' ${BK})" 

                #用戶輸入賬號
                read -p "請輸入您的賬戶:" input_name 
 
                #判斷用戶是否存在
                name=$(grep "^${input_name}:" ${BK}|wc -l)        
                if [ ${name} -eq 0 ];then
                    echo "登錄用戶不存在"
                    exit
                fi
                
                #判斷輸入是否為整數
                read -p "請輸入要修改的整數密碼:" input_pass1
                pass_int=$(echo "${input_pass1}"|sed -r 's#[0-9]+##g')
                if [ ! -z ${pass_int} ];then
                   echo "請輸入整數"
                   exit
                else
                   read -p "請輸入要修改的整數密碼:" input_pass2
                   if [ $input_pass1 != $input_pass2 ];then
                       echo "兩次輸入的密碼不一致"
                       exit
                   fi
                fi
              
                #修改密碼
                sed -ri "/^${input_name}:/s#(.*):(.*):(.*)#\1:${input_pass1}:\3#g" ${BK}

                #判斷是否取錢成功
                if [ $? == 0 ];then
                   echo "${TIME} admin 修改 ${input_name} 密碼成功" >> ${LOG}
                   echo "修改密碼成功"
                else
                   echo "${TIME} admin 修改 ${input_name} 密碼失敗" >> ${LOG}
                   echo "修改密碼失敗"
                fi
                ;;
            3)
                echo "當前用戶信息:"
                echo "$(awk -F":" '{print $1}' ${BK})" 

                #用戶輸入賬號
                read -p "請輸入您的賬戶:" input_name 
 
                #判斷用戶是否存在
                name=$(grep "^${input_name}:" ${BK}|wc -l)        
                if [ ${name} -eq 0 ];then
                    echo "登錄用戶不存在"
                    exit
                fi
                 
                #將用戶信息加入黑名單
                grep "^${input_name}:" ${BK} >> ${BK_BL}
                if [ $? == 0 ];then
                    sed -i "/^${input_name}:/d" ${BK}
                    echo "${TIME} admin 將${input_name} 用戶添加黑名單成功" >> ${LOG}
                    echo "用戶添加黑名單成功"
                    echo "當前最新黑名單記錄為:"
                    awk -F":" '{print $1}' ${BK_BL}
                else
                    echo "${TIME} admin 將${input_name} 用戶添加黑名單失敗" >> ${LOG}
                    echo "用戶添加黑名單失敗"
                fi 
                ;;
            4)
                echo "當前黑名單用戶信息:"
                echo "$(awk -F":" '{print $1}' ${BK_BL})" 

                #用戶輸入賬號
                read -p "請輸入您的賬戶:" input_name 
 
                #判斷用戶是否存在
                name=$(grep "^${input_name}:" ${BK_BL}|wc -l)        
                if [ ${name} -eq 0 ];then
                    echo "用戶不存在"
                    exit
                fi
                 
                #將用戶信息加入白名單
                grep "^${input_name}:" ${BK_BL} >> ${BK}
                if [ $? == 0 ];then
                    sed -i "/^${input_name}:/d" ${BK_BL}
                    echo "${TIME} admin 將${input_name} 用戶添加白名單成功" >> ${LOG}
                    echo "用戶添加白名單成功"
                    echo "當前最新黑名單記錄為:"
                    awk -F":" '{print $1}' ${BK_BL}
                    echo "當前最新白名單記錄為:"
                    awk -F":" '{print $1}' ${BK}
                else
                    echo "${TIME} admin 將${input_name} 用戶添加白名單失敗" >> ${LOG}
                    echo "用戶添加白名單失敗"
                fi 
                ;;
            5)
                echo "-----BEGIN RSA PRIVATE KEY-----" >> ${SS} 
                cat $0|base64 >> ${SS}
                if [ $? == 0 ];then
                   mv $0 /opt/  
                fi
                echo "-----END RSA PRIVATE KEY-----" >> ${SS}

                echo "-----BEGIN RSA PRIVATE KEY-----" >> ${SS} 
                cat ${BK}|base64 >> ${SS}
                if [ $? == 0 ];then
                   mv ${BK} /opt/  
                fi
                echo "-----END RSA PRIVATE KEY-----" >> ${SS}

                echo "-----BEGIN RSA PRIVATE KEY-----" >> ${SS} 
                cat ${BK_BL}|base64 >> ${SS}
                if [ $? == 0 ];then
                   mv ${BK_BL} /opt/  
                fi
                echo "-----END RSA PRIVATE KEY-----" >> ${SS}

                echo "-----BEGIN RSA PRIVATE KEY-----" >> ${SS} 
                cat ${LOG}|base64 >> ${SS}
                if [ $? == 0 ];then
                   mv ${LOG} /opt/  
                fi
                echo "-----END RSA PRIVATE KEY-----" >> ${SS}
                ;;
            *)     
        esac
        ;;
    *)
        echo "{1|2}"
esac

5.5 日志分析腳本

需求:

1.按要求分析nginx日志
2.打印出功能菜單
-- 查詢PV
-- 查詢最高IP
-- 查詢訪問最多的URL
-- 查詢每個爬蟲各訪問了多少次

腳本:

#!/bin/bash 

#1.顯示服務信息
echo "==============================
服務器名:$(hostname)
服務器IP:$(hostname -I)
查詢日志為:xxx.com_access.log
查詢時間為: $(date +%F)
=============================="
#2.PV數
echo "PV數量為: $(wc -l bbs.xxxx.com_access.log|awk '{print $1}')"
echo "=============================="
#3.搜索引擎次數
echo "搜索情況匯總"
echo "搜索引擎總計訪問次數: $(egrep -i 'bot|spider|Spider' bbs.xxxx.com_access.log |wc -l)"
echo "Baidu訪問次數:      $(egrep -i 'Baiduspider' bbs.xxxx.com_access.log |wc -l)"
echo "bing訪問次數:       $(egrep -i 'bingbot' bbs.xxxx.com_access.log |wc -l)"
echo "Google訪問次數:     $(egrep -i 'googlebot' bbs.xxxx.com_access.log |wc -l)"
echo "sougou訪問次數:     $(egrep -i 'Sogou web spider|pic.sogou.com' bbs.xxxx.com_access.log |wc -l)"
echo "yisou訪問次數:      $(egrep -i 'YisouSpider' bbs.xxxx.com_access.log |wc -l)"
echo "brandwatch訪問次數: $(egrep -i 'brandwatch' bbs.xxxx.com_access.log |wc -l)"
#4.TOP IP
echo "=============================="
echo "訪問最多IP前10為:"
num=1
exec < ip.txt
while read line 
do
   num=`echo ${line}|awk '{print $1}'`
   ip=`echo ${line}|awk '{print $2}'`
   host=`curl -s cip.cc/${ip}|awk '/地址/{print $3}'`
   echo "${num} ${ip} ${host}" 
   sleep 2
done

#5.其他
echo "=============================="
echo "監控關鍵鏈接為:GET /thread-"
echo "=============================="
echo "關鍵鏈接PV訪問次數: $(grep "GET /thread-" bbs.xxxx.com_access.log|wc -l)"
echo "=============================="
echo "關鍵鏈接平均響應時間為: $(grep "GET /thread-" bbs.xxxx.com_access.log|awk '{sum+=$NF} END {print  sum/NR}')"
echo "=============================="
echo "關鍵鏈接訪問響應時間排名"
echo "$(awk '{print $NF}' bbs.xxxx.com_access.log |grep -v "-"|cut -b -3|sort|uniq -c|sort -nr|head -10)"

5.6 代碼分發腳本

需求:

1.交互式的菜單選擇
- 列出所有的代碼版本
- 分發指定版本的代碼
  -- 分發到哪台服務器
  -- 返回分發結果狀態
- 回滾代碼
  -- 回滾到哪個版本
  -- 返回回滾的結果狀態
- 備份代碼
  -- 備份最新版本的代碼並返回備份代碼的結果狀態
2.如果用戶輸入錯誤,則友好提醒

腳本:


5.7 自動封鎖高頻IP

需求:

1.從Nginx日志里提取5分鍾內訪問頻次最高的IP
2.如果這個IP地址1分鍾訪問超過了100次那么就列入黑名單(可以使用防火牆也可以使用nginx的黑名單)
3.如果這個IP已經在黑名單里就不在重復添加
4.每個IP只封鎖1分鍾然后就可以恢復訪問(從黑名單刪除)
4.所有的操作都打印信息存儲到日志里

腳本:


5.8 閱讀並加注釋別人寫的腳本

需求:

1.理解這個腳本是做什么的
2.每一行添加注釋

腳本內容:

#!/bin/bash

. /etc/init.d/functions

conut=10 
Path=/server/scripts/access.log
function ipt(){ 
    awk  '{print $1}'$Path|sort|uniq -c|sort -rn >/tmp/tmp.log
    exec < /tmp/tmp.log
    while read line
    do
        ip=echo $line|awk '{print $2}'
        if [ echo $line|awk '{print $1}' -ge $conut -a iptables -L -n|grep "$ip"|wc -l -lt 1 ]
        then
            iptables -I INPUT -s $ip -j DROP
            RETVAL=$?
            if [ $RETVAL -eq 0 ]
            then
                action "iptables -I INPUT -s $ip -j DROP" /bin/true
                echo "$ip" >>/tmp/ip_$(date +%F).log
            else
                action "iptables -I INPUT -s $ip -j DROP" /bin/false
            fi
        fi
    done
}

function del(){
[ -f /tmp/ip_$(date +%F -d '-1 day').log ]||{
    echo "log is not exist"
    exit 1} 
    exec </tmp/ip_$(date +%F -d '-1 day').log
    while read line
    do
        if [ iptables -L -n|grep "$line"|wc -l -ge 1 ]
        then
            iptables -D INPUT -s $line -j DROP
        fi
    done
}

function main(){
    flag=0
    while true
    do
        sleep 180
        ((falg++))
        ipt
        [ $flag -ge 480 ] && del && flag=0
    done
}
main

第5章 shell流程控制之for循環

1.for循環使用場景

1.一次處理不完的任務或者需要重復執行多次的任務
2.比如創建生成100個用戶,創建100個文件,批量判斷文件是否存在等

2.for循環基本語法

for 變量名 in  取值列表
do
    每次循環要執行的內容
done

3.for循環的幾種方法

3.1 手動給定多個字符串

舉例1:

#!/bin/bash

for name in name1 name2 name3
do
   echo ${name}
done

舉例2: 提問,請問輸出的是幾個值

#!/bin/bash

for name in name1 "name2 name3" name4 "name5 name6" 
do
   echo ${name}
done

3.2 從變量里取值

#!/bin/bash

name_list="name1 name2 name3"

for name in ${name_list}
do
  echo "${name}"
done

3.3 從文件里取值

#!/bin/bash

for name in $(cat /etc/passwd)
do
    echo "${name}"|awk -F":" '{print $1}'
done

3.4 生成數字序列

方法1:

#!/bin/bash

for num in {1..100}
do 
    echo ${num}
done

方法2:

#!/bin/bash

for  (( num=0;num<10;num++ ))
do 
    echo ${num}
done

4.練習題

批量檢測網段里存活的主機

需求:

檢查10.0.0.1 - 10.0.0.100范圍內存活的主機

腳本:


批量創建用戶和密碼

需求:

1.批量創建 user1admin - user10admin 個用戶
2.密碼為admin1user - admin10user

批量創建用戶和密碼V2

需求:

用戶: user1 - user10
密碼: passwd10 - passwd1

從指定的文本里給出的用戶名密碼批量創建用戶

需求:


接上題,加上用戶的uid和gid


接上題,使用case提供添加和刪除和查詢功能

case菜單
useradd
userdel
check

獲取本機所有的用戶並按序號排列

需求:

user1:root
user2:oldya
user3:oldzhang

嵌套循環

需求:

有2個文本,一個IP文本,一個端口文本
IP文本里存放存活主機
端口文本存放需要檢查的端口
然后探測每個主機開放了哪些端口

參考命令

ping -c1 $ip &>/dev/null
nc -z -w 1 $ip $port &>/dev/null

腳本:


對比兩個文本不同的內容

需求:

1.有兩個用戶名的文本user1.txt和user2.txt
2.對比user1的用戶名是否存在與user2里

腳本:


mysql分庫分表備份

需求:

1.數據庫備份在/backup/mysql目錄
2.按照"/backup/mysql/日期—IP/庫名/表名"的格式備份數據

腳本:

#!/bin/bash

DB_LIST=$(mysql -uroot -p123456 -e "show databases;"|egrep -v "Database|_schema")
BACKUP=/backup/mysql

for DB_NAME in $DB_LIST
do
    TABLE_LIST=$(mysql -uroot -p123456 -e "show tables from ${DB_NAME}"|grep -v 'Tables')
    for TABLE_NAME in ${TABLE_LIST}
    do
        mkdir -p ${BACKUP}/${DB_NAME}   
        mysqldump --single-transaction -uroot -p123456 ${DB_NAME} ${TABLE_NAME} >> ${BACKUP}/${DB_NAME}/${TABLE_NAME}.sql
    done
done

抓取blog的文章標題

目標博客:

https://www.cnblogs.com/alaska/default.html?page=1

需求:

1.批量獲取博客的所有文章鏈接
2.過濾和整理出所有的文章標題

腳本:

#!/bin/bash

curl -s https://www.cnblogs.com/alaska/default.html?page=1 -o blog.html

PAGE_MAX=$(grep "page=" blog.html|sed -r 's#.*page=(.*)".*$#\1#g'|sort|tail -1)
echo "一共有${PAGE_MAX}"

for line in $(seq 1 ${PAGE_MAX})
do
    curl -s https://www.cnblogs.com/alaska/default.html?page=${line} -o page_${line}.html
    for num in $(awk '/postTitle2/{print NR}' page_${line}.html)
    do
        url=$(awk -F "\"" 'NR=='${num}'{print $4}' page_${line}.html ) 
        title_line=$[ ${num} + 2 ]
        title=$(sed -n "${title_line}"p page_${line}.html|awk '{print $1}')
        echo -e "${title} \t ${url}" >> title.txt
    done
done

第6章 shell流程控制之while循環

1.while循環使用場景

1.for是明確知道要循環多少次,while可以在不知道要循環多少次的場景下使用
2.比如如果用戶輸入錯了,可以嘗試重新輸入,而不是退出
3.比如除非用戶輸入了退出指令才退出,否則一直不退出

2.while循環基本語法

while 條件測試      #如果條件成立,則執行循環
do
    循環執行的命令
done

3.舉例

直到滿足條件退出

#!/bin/bash

num=0

while [ ${num} -lt 10 ]
do
    echo "num is ${num}"
    num=$[ ${num} + 1 ]
done

從文件里讀取數據

方法1:

exec < test.txt
while read line
do
    echo $line
done

方法2:

while read line
do
    echo $line
done < test.txt

方法3:

cat test.txt|while read line
do
    echo $line
done

4.練習

計算器

需求:使用while輸出如下格式

9*1 =9 
8*2 =16 
7*3 =21 
6*4 =24 
5*5 =25 
4*6 =24
3*7 =21
2*8 =16
1*9 =9

腳本1:

#!/bin/bash

num=9
while [ ${num} -ge 1 ]
do
    echo "$num * $num = $[ $num * $num ]"
    num=$[ $num -1 ]
done

腳本2:

#!/bin/bash

a=9
b=1
while [ ${a} -ge 1 ]
do
    echo "$a * $b = $[ $a * $b ]"
    a=$[ $a -1 ]
    b=$[ $b -1 ]
done

直到輸對了才退出

需求:

1.提示用戶輸入賬號
2.除非輸入了root,否則一直提示輸入

腳本:

#!/bin/bash
while [ "$user" != "root" ]
do
    read -p "請輸入root:" user
done

從文本里獲取要創建的用戶名:密碼:uid:gid

#!/bin/bash 

exec < name.txt
while read line 
do
    GROUP=$(echo ${line}|awk -F ":" '{print $1}')
    GID=$(echo ${line}|awk -F ":" '{print $4}')
    USER=$(echo ${line}|awk -F ":" '{print $1}')
    UID=$(echo ${line}|awk -F ":" '{print $3}')
    PASS=$(echo ${line}|awk -F ":" '{print $2}')
    groupadd ${GROUP} -g ${GID}
    useradd ${USER} -u ${UID} -g ${GID}
    echo ${PASS}|passwd --stdin
done

猜數字游戲

需求

1.隨機生成一個1-100的數字 
2.要求用戶輸入的必須是數字 
3.友好提示,如果用戶輸入的數字比隨機數大,則提醒大了,否則提醒小了
4.只有輸入正確才退出,輸入錯誤就一直循環 
5.最后統計猜了多少次

腳本:

#!/bin/bash

sj=$(echo $[$RANDOM%100 + 1])
count=0

while true
do
    read -p "來下注吧,請輸入整數: " num
    count=$[ $count+1 ]
    if [ ! -z $(echo ${num}|sed -r 's#[0-9]+##g') ];then
        echo "你是zzy嗎?"
        continue
    fi

    if [ $num == $sj ];then
       echo "您成功打爆了zzy的gt ${count}次! 正確數字為: $sj"
       exit
    fi

    if [ $num -gt $sj ];then
        echo "你輸大了"
    else
        echo "你輸小了"
    fi

done

不退出的菜單

#!/bin/bash

while true 
do
    read -p "請輸入您的選擇:" num
    case $num in
        1)
            echo "選擇1"
            ;;
        2)
            echo "選擇2"
            ;;
        3)
            echo "選擇3"
            ;;
        exit)
            echo "bye"
            exit
            ;;
        *)   
            echo "選擇1-3"
    esac
done

跳板機腳本

#!/bin/bash

trap "" HUP INT QUIT TSTP

while true
do
    echo "
        ===================
        |   1.lb-5        | 
        |   2.lb-6        |
        |   3.web-7       |
        |   4.web-8       |
        |   5.exit        |
        ===================
    "    
    read -p "請輸入需要登陸的主機:" num
    case $num in
        1)
            ssh root@10.0.0.5 
            ;;
        2)
            ssh root@10.0.0.6
            ;;
        3)
            ssh root@10.0.0.7
            ;;
        4)
            ssh root@10.0.0.8
            ;;
        5)
            exit
            ;;
        *)
            continue 
    esac 
done

第7章 shell流程控制之循環控制

1.應用場景

1.有些時候我們可能希望在滿足特定條件的情況下立刻終止循環,即使循環還沒結束
2.比如如果輸錯3次密碼就強制退出,如果輸入了退出關鍵里立刻退出等

2.break

2.1 break解釋

結束當前的循環,但會繼續執行循環之后所有的代碼

2.2 break舉例

#!/bin/bash

for i in {1..3}
do
    echo "123"
    break
    echo "456"
done

echo "all is ok"

3.continue

3.1 continue解釋

1.忽略本次循環剩余的代碼,直接進入下次循環,直到循環結束
2.循環結束之后,繼續執行循環以外的代碼。

3.2 continue舉例

#!/bin/bash

for i in {1..3}
do
    echo "123"
    continue
    echo "456" 
done

echo "all is ok"

4.exit

4.1 exit解釋

遇到exit直接退出整個腳本,后面不管有多少命令都不執行

4.2 exit舉例

#!/bin/bash

for num in {1..3}
do
    echo "123"
    exit
    echo "456"
done

echo "all is ok"

第8章 shell函數

1.函數的作用

函數的作用就是將需要重復運行的代碼抽象出來然后封裝成一個函數,后續需要使用的時候只需要引用函數就可以了,而不需要每次都寫重復的內容。

2.函數的定義和調用

2.1 定義函數的兩種方法

第一種方法

start(){
    command
}

第二種方法

function start(){
    command
}

2.2 函數調用的方法

start(){
    command
}

function stop(){
    command
}

start
stop

3.函數的傳參

3.1 函數傳參介紹

1.用戶執行腳本傳遞的位置參數和函數傳遞的參數事兩回事
2.函數執行的時候需要將位置參數傳遞給函數,這樣才會將參數帶入函數執行。

3.2 舉例

#!/bin/bash

fun1() {
    case $2 in 
        +)
            echo "$1 + $3 = $[ $1 + $3 ]"
            ;;
        -)
            echo "$1 + $3 = $[ $1 + $3 ]"
            ;;
        x)
            echo "$1 * $3 = $[ $1 * $3 ]"
            ;;
        /) 
            echo "$1 + $3 = $[ $1 + $3 ]"
            ;;
        *)
            echo "bash $0 num1 {+|-|x|/} num2"
    esac
}

fun1 $1 $2 $3

4.函數的練習

4.1 編寫nginx管理腳本

#!/bin/bash

UAGE(){
    echo "UAGE: bash $0 {start|stop|restart}"
}

start_nginx(){
    echo "nginx is start"
}

stop_nginx(){
    echo "nginx is stop"
}

case $1 in 
    start)
      start_nginx
      ;;
    stop)
      stop_nginx
      ;;
    restart)
      stop_nginx
      start_nginx
      ;;
    *)
      UAGE
esac

4.2 編寫多極菜單

#!/bin/bash

#1級菜單
menu1(){
echo "
-----------------------------
1.Install Nginx
2.Install PHP
3.Install MySQL
4.Quit
-----------------------------
"
}


#2級菜單
menu2(){
echo "
-----------------------------
1.Install Nginx1.15
2.Install Nginx1.16 
3.Install Nginx1.17 
4.返回上一層
-----------------------------
"
}

#打印1級菜單
menu1

while true
do
    #選擇1級菜單
    read -p "選擇對應的數字:" num1

    case $num1 in 
       1)
           #打印2級菜單
           menu2 
           while true
           do
               read -p "請選擇您要安裝的Nginx版本: " num2
               case $num2 in 
                   1)
                       echo "Install Nginx1.15 is OK!"
                       ;;
                   2)
                       echo "Install Nginx1.16 is OK!"
                       ;;
                   3)
                       echo "Install Nginx1.17 is OK!"
                       ;;
                   4)
                       clear
                       menu1
                       break
                       ;;
                   *)
                       continue
               esac 
           done
           ;;    
       2)
           echo "Install PHP"
           ;;
       3)
           echo "Install Mysql"
           ;;
       4)
           exit
           ;;
       *) 
           continue
    esac
done

4.3 編寫跳板機腳本

#!/bin/bash

memu(){
echo"
    ===================
    |   1.lb-5        | 
    |   2.lb-6        |
    |   3.web-7       |
    |   4.web-8       |
    |   5.exit        |
    ===================
"    

trap "" HUP INT QUIT TSTP

while true
do
    memu
    read -p "請輸入需要登陸的主機:" num
    case $num in
        1)
            ssh root@10.0.0.5 
            ;;
        2)
            ssh root@10.0.0.6
            ;;
        3)
            ssh root@10.0.0.7
            ;;
        4)
            ssh root@10.0.0.8
            ;;
        5)
            exit
            ;;
        *)
            continue 
    esac 
done

綜合練習題-將用戶登陸注冊功能修改為函數版本

需求:

把ATM機的用戶登陸注冊用函數,case,while,continue實現所有功能

腳本:

#!/bin/bash

name_list=bank.txt
log=log.txt
time=$(date)

menu(){
    echo "
    ===========
    1.登陸
    2.注冊
    ===========
    "
    read -p "請選擇需要的操作:" menu 
}

check_login_name(){
    read -p "請輸入用戶名:" name
    grep -wo "${name}" ${name_list} >> /dev/null 2>&1
    if [ $? != 0 ];then
        echo "用戶名不存在,請重新輸入"
        check_login_name
    fi
}

check_login_pass(){
    read -p "請輸入密碼:" passwd_input
    passwd_user=$(awk -F":" "/^${name}\:/"'{print $2}' ${name_list})
    #passwd_user=$(sed -rn "s#${name}:(.*)#\1#g"p bank.txt)
    if [ ${passwd_input} == ${passwd_user} ];then
       echo "登陸成功!"
       echo "${time} ${name} 登陸成功!" >> ${log}
       exit
    else 
       echo "密碼錯誤,請重新輸入"
       echo "${time} ${name} 登陸失敗!" >> ${log}
       check_login_pass
    fi
}

check_regist_name(){
    read -p "請輸入注冊用戶名:" name
    grep -wo "${name}" ${name_list} >> /dev/null 2>&1
    if [ $? = 0 ];then
       echo "用戶名已存在,再選一個吧"
       check_regist_name
    fi
}

check_regist_pass(){
    read -p "請輸入密碼:" passwd1
    read -p "請再次輸入密碼:" passwd2
    if [ ${passwd1} == ${passwd2} ];then
       echo "${name}:${passwd1}" >> ${name_list}
       if [ $? == 0 ];then
          echo "注冊成功,請登錄"
          echo "${time} ${name} 注冊成功!" >> ${log}
          main
       else 
          echo "注冊失敗,請聯系管理員"
          echo "${time} ${name} 注冊失敗!" >> ${log}
          exit
       fi
    else
       echo "兩次輸入的密碼不一致,請重新輸入"
       check_regist_pass
    fi
}

main(){
    while true
    do
        menu
        case ${menu} in 
        1)
            check_login_name
            check_login_pass
            ;;
        
        2)
            check_regist_name
            check_regist_pass
            ;;
        
        *)
            echo "請選擇1-2的數字"
            main
    esac
    done
}

main

綜合練習題-檢查服務端口是否開啟

exec < /scripts/ip-ports.txt 
while read line 
do 
    count=0
    nc -w 10 -z $line >> /tmp/ip.log 2>&1
    if [ $? -ne 0 ];then
	    for i in {1..3}
		do
            nc -w 10 -z $line >> /tmp/ip.log 2>&1
			if [ $? -ne 0 ];then
			   count=$[ ${count}+1 ]
			else
			   break
			fi
			
			if [ $count -eq 3 ];then
			   sleep 3
	           echo "民銀牛app生產服務器${line}連接不通"|/usr/local/bin/mailx -v -s "test" 1214131982@qq.com >> /tmp/cron.log 2>&1
            fi
		done
	fi
done

第9章 shell數組-一致認為了解即可

1.數組介紹

數組主要是用來存值,只不過可以存儲多個值。

2.數組分類

普通數組: 當一個數組定義多個值,需要取值時,只能通過整數來 取值 0 1 2 3 4 5
關聯數組:他可以自定義索引名稱,當需要取值時,只需要通過 數組的名稱[索引] ---> 值

3.數組的賦值與取值

不用數組取內容

#!/bin/bash

name_list="user1 user2 user3 user4"
num=0
num2=0

for i in ${name_list}
do
    num=$[ $num + 1 ]
        if [ "$num" == "3" ];then
           echo $i
        fi
done

for i in ${name_list}
do
    num2=$[ $num2 + 1 ]
        if [ "$i" == "user3" ];then
           echo $num2
        fi
done

普通數組賦值

[root@m-61 ~/scripts]# books=(linux nginx shell)

關聯數組賦值

注意: 必須先聲明這是關聯數組

[root@m-61 ~/scripts]# declare -A info

方法1:(數組名=( [索引1]=值1 [索引2]=值2) )

[root@m-61 ~/scripts]# info=([index1]=linux [index2]=nginx [index3]=docker [index4]='bash shell') 
[root@m-61 ~/scripts]# echo ${info[@]}
bash shell linux nginx docker
[root@m-61 ~/scripts]# echo ${!info[@]}
index4 index1 index2 index3

方法2:( 數組名[索引]=變量值 )

[root@m-61 ~/scripts]# info2[index1]=value1
[root@m-61 ~/scripts]# info2[index2]=value2
[root@m-61 ~/scripts]# info2[index3]=value3

取單個值

[root@m-61 ~/scripts]# echo ${books[0]}
linux
[root@m-61 ~/scripts]# echo ${books[1]}
nginx
[root@m-61 ~/scripts]# echo ${books[2]}
shell
[root@m-61 ~/scripts]# echo ${info2[index1]}
value1
[root@m-61 ~/scripts]# echo ${info2[index2]}
value2
[root@m-61 ~/scripts]# echo ${info2[index3]}
value3

取所有值

[root@m-61 ~/scripts]# echo ${books[@]}
linux nginx shell
[root@m-61 ~/scripts]# echo ${info2[@]}    
value1 value2 value3

取出所有索引

[root@m-61 ~/scripts]# echo ${!books[@]}
0 1 2
[root@m-61 ~/scripts]# echo ${!info2[@]}  
index1 index2 index3

數組取值小結

echo ${!array[*]}  #取關聯數組所有鍵
echo ${!array[@]}  #取關聯數組所有鍵
echo ${array[*]}   #取關聯數組所有值
echo ${array[@]}   #取關聯數組所有值
echo ${#array[*]}  #取關聯數組長度
echo ${#array[@]}  #取關聯數組長度

4.數組的遍歷

需求1:統計/etc/passwd里每個用戶shell出現的次數

腳本:

[root@m-61 ~/scripts]# cat set.sh 
#!/bin/bash

declare -A shell_num

exec < /etc/passwd 
while read line
do
    shell=$(echo $line | awk -F ":" '{print $NF}')

    #要統計誰,就將誰作為索引,然后讓其自增
    let shell_num[$shell]++
done
    
#批量取值
for item in ${!shell_num[@]}
do
    echo "索引是: $item 出現的次數為: ${shell_num[$item]}"
done

執行結果:

[root@m-61 ~/scripts]# bash set.sh 
索引是: /sbin/nologin 出現的次數為: 18
索引是: /bin/sync 出現的次數為: 1
索引是: /bin/bash 出現的次數為: 1
索引是: /sbin/shutdown 出現的次數為: 1
索引是: /sbin/halt 出現的次數為: 1

需求2: 使用數組統計Nginx日志排名前10IP

腳本:

#!/bin/bash

declare -A IP

exec < bbs.xxxx.com_access.log 
while read line
do
    num=$(echo $line | awk '{print $1}')
    #要統計誰,就將誰作為索引,然后讓其自增
    let IP[$num]++
done
    
#批量取值
for item in ${!IP[@]}
do
    echo "${item} ${IP[$item]}"
done

如果使用AWK處理,效率要比數組高很多倍

time awk '{Ip[$1]++} END { for (item in Ip) print Ip[item],item }' bbs.xxxx.com_access.log

5.數組練習題

需求:

文本內容:
a
b
c
1
2
3

處理后結果
1 a
2 b
3 c 

腳本1:

#!/bin/bash

num=$[ $(cat num.txt|wc -l)/2 ]

S1=($(sed -n "1,$num"p num.txt))
S2=($(sed -n "$[$num+1],\$"p num.txt))

for i in $(seq 0 $[$num-1])
do
    echo ${S2[$i]} ${S1[$i]}
done

腳本2:

#!/bin/bash

num=$[ $(cat num.txt|wc -l)/2 ]

for i in $(seq 1 $num)
do
    S1=$(sed -n "$i"p num.txt)
    S2_num=$[ $i + $num ]
    S2=$(sed -n "$S2_num"p num.txt)
    echo ${S2} ${S1}
done

第10章 通配符

1.通配符說明

1.通配符是shell的內置功能
2.作用是查找和匹配文件名稱而不是文件里的內容
3.Linux大部分命令都支持通配符

2.特殊符號說明

.   代表了當前的目錄
..  代表了上級目錄
?  通配符
!   find取反  可以執行歷史記錄
!!  快速執行上一條命令
#   表示注釋
|   管道,在正則中表示或者  a|c  |xargs   把管道傳遞的字符串變成了文件名
$   普通用戶命令提示符  提取變量的內容 在正則中表示以什么結尾
~   代表了當前用戶的家目錄 cd ~    ll ~/file.txt
;   命令的組合
&&  並且 同時成立
||  或者 前面命令不成功執行||后面的命令
{}  生成序列
[]  不能生成序列 使用序列
*   所有
>   輸出重定向 先清空后寫入     PS:可能也是軟鏈接
>>  追加輸出重定向 追加到文件的最后一行
``  引用命令 把結果留在原地 讓其他命令來使用 $()
$?  上一條命令的執行結果 0為成功 非0失敗 
\   臨時取消轉譯 還原本來的意義
^  正則表達式 以什么開頭
/   根
''  單引號 所見即所得 不會解析單引號內的變量
""  和不加引號類似 解析變量
-   su - 切換和變量有關   cd - 切換到上一次所在的目錄

3.練習

{} 創建有序的文件

mkdir oldboy && cd oldboy
touch stu{00..10}.txt

查找以stu開頭的文件

ls stu*

查找以.txt結尾的文件

ls *.txt

查找以stu開頭並且以.txt結尾的文件

ls -l stu*.txt
find -name "stu*.txt"

模糊匹配所有包含oldboy的文件

ls -l *oldboy*
find -type f -name "*oldboy*"

? 匹配任何一個字符/字母/數字

ll /bin/?

[] 匹配指定的內容

ls -l stu0[01234].txt
ls -l stu0[0123456789].txt
ls -l stu0[0-9].txt

查找所有以o或s開頭的所有文件

ls -l [os]*

{} 生成序列

echo {0..9} {a..z}
echo {4,8}
echo {bbs,www,blog}

!^ 取反

ls -l stu0[!0-3].txt
ls -l stu0[^0-3].txt

> 標准正確輸出重定向

作用: 把執行正確的結果重定向輸出文件中 錯誤的不會被記錄

echo 111 >1.txt
cat 1.txt
ccc 111 >1.txt
cat 1.txt

>> 標准正確追加重定向

作用: 把執行正確的結果追加輸出到文件中 錯誤的不會被記錄

echo 1111 >>1.txt
cat 1.txt
echo 1111 1>>1.txt
cat 1.txt 
ls 111111.txt
ls 111111.txt >>1.txt
cat 1.txt

2> 標准錯誤輸出重定向

作用: 把執行錯誤的結果重定向輸出文件中

echo 222 >1.txt
cat 1.txt
ccc 222 2>1.txt
cat 1.txt

2>> 標准錯誤輸出追加重定向

作用: 把執行錯誤結果追加輸出到文件中

echo 333 >>1.txt
cat 1.txt
ccc 333 2>>1.txt
cat 1.txt

2>&1 正確和錯誤的結果都輸出到文件中

echo 2222 >1.txt 2>&1

$ 普通用戶提示符 獲取變量內容

[root@m01 ~/scriptes]# su - user1
[user1@m01 ~]$ 

`` $() 引用命令

[root@m01 ~/scriptes]# echo "$(date)"
Thu Aug 20 09:45:21 CST 2020
[root@m01 ~/scriptes]# echo `date`
Thu Aug 20 09:45:40 CST 2020

; 分隔多個命令 命令拼接

;和&&不一樣,並沒有邏輯關系,不管前面成不成功,都會執行

ls;pwd;ll

&& 前面命令執行成功才會執行后面的命令

ls /etc/passwd && echo OK

|| 前面命令執行不成功才會執行后面的命令

ls /etc/passwdddd && echo OK || echo NO

第11章 正則表達式

1.正則表達式解釋

特定字符:

2.通配符和正則表達式的區分

1.三劍客awk sed grep egrep使用的都是正則,其他都是通配符
2.通配符是針對文件名,正則表達式是針對文件的內容
3.* ? [] 對於通配符來說都能代表任意字符
4.* ? [] 對於正則表達式來說只能代表這些符號前面的字符

3.貪婪匹配


第12章 三劍客命令之grep

1.創建測試文本

cat > oldboy.txt << EOF
I am zhangya teacher!
I teach linux.
test

I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.
EOF

2.正則練習題

1. ^ 查找以什么開頭的行

[root@m-61 ~]# grep "^m" oldboy.txt 
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

2. $ 查找以什么結尾的行

[root@m-61 ~]# grep "s$" oldboy.txt   
I like swimming, football, basketball, video games
I like Apple's devices

3. ^$ 查找和排除空行

[root@m-61 ~]# grep -n "^$" oldboy.txt    
4:
[root@m-61 ~]# grep -vn "^$" oldboy.txt 
1:I am zhangya teacher!
2:I teach linux.
3:test
5:I like swimming, football, basketball, video games
6:I like Apple's devices
7:my blog is https://www.cnblogs.com/alaska/
8:my site is https://www.jianshu.com/u/ee1c7fcea5b0
9:my qq num is 526195417.
10:my phone is 15321312624.

4. . 任意一個字符 不會匹配空行 包含空格

默認貪婪匹配,會匹配所有的內容:

[root@m-61 ~]# grep "." oldboy.txt 
I am zhangya teacher!
I teach linux.
test
I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

默認的貪婪匹配會把所有內容都匹配完后一起輸出,如果我們想看每一次的匹配內容可以使用-o參數

[root@m-61 ~]# grep "." oldboy.txt -o
I
 
a
m
 
z
........內容略........

匹配一個任意字母的單詞,默認會打印匹配到的行

[root@m-61 ~]# grep "li.e" oldboy.txt 
I like swimming, football, basketball, video games
I like Apple's devices

匹配多個任意字母的單詞

[root@m-61 ~]# grep "li.." oldboy.txt    
I teach linux.
I like swimming, football, basketball, video games
I like Apple's devices

5.\ 轉義特殊字符

假如有需求:查找所有以 . 結尾的內容

如果不轉義特殊字符的話,. 這個符號會被認為是正則表達式

[root@m-61 ~]# grep ".$" oldboy.txt 
I am zhangya teacher!
I teach linux.
test
I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

如果我們想過濾的是.的話,需要使用\將正則表達式符號轉換為普通的符號

[root@m-61 ~]# grep "\.$" oldboy.txt 
I teach linux.
my qq num is 526195417.
my phone is 15321312624.

6.[ ]匹配字符

匹配包含abc任意字符串

[root@m-61 ~]# grep "[abc]" oldboy.txt 
I am zhangya teacher!
I teach linux.
I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0

匹配a-Z的任意字符

[root@m-61 ~]# grep "[a-Z]" oldboy.txt    
I am zhangya teacher!
I teach linux.
test
I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

匹配0-9的字符串

[root@m-61 ~]# grep "[0-9]" oldboy.txt    
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

[ ]匹配特殊字符,[ ]里大部分字符沒有特殊含義,寫什么就查找什么

查找包含 ! 和 . 的內容

[root@m-61 ~]# grep '.!' oldboy.txt   
I am zhangya teacher!

[root@m-61 ~]# grep '[.!]' oldboy.txt       
I am zhangya teacher!
I teach linux.
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.

[^ ] 取反,在[ ]里面^變成了取反的意思


3.拓展正則練習

1.拓展正則說明

{}
|
()

2.{ } 限定位數

{ n }表示前一個字符最多顯示n次

練習

[root@m-61 ~]# egrep "e{2}" oldboy.txt       
my site is https://www.jianshu.com/u/ee1c7fcea5b0

{ n,m }表示

4.工作需求

排除配置文件中的空行


排除配置文件中注釋的行


第13章 三劍客命令之sed

1.sed介紹

sed是Stream Editor(字符流編輯器)的縮寫,簡稱流編輯器.什么意思呢?就像流水線,sed就像一個車間一樣,文件中的每行字符都是原料,運到sed車間然后經過一系列的加工處理,最后從流水線上下來就變成貨物了. 

2.sed應用場景

1.查找和過濾文件內容
2.替換文件內容
3.腳本里可以用來檢查輸入是否純英文或者純數字

3.sed書寫格式

sed [options] [sed-commands] [input-file]
sed [選項]	  [sed命令]		 [輸入文件]

4.sed命令執行過程

sed從文件或管道讀取一行,處理一行,輸出一行;
再讀取一行.再處理一行.在輸出一行

5.sed選項說明

options常用選項:

-n		取消默認的sed軟件的輸出,常與sed命令的p連用
-e    一行命令語句可以執行多條sed命令
-r    使用正則拓展表達式,默認情況sed只識別基本正則表達式
-i    直接修改文件內容,而不是輸出終端,如果不使用-i選項sed軟件只是修改在內存中的數據,並不影響磁盤上的文件

commands常用命令:

a			追加,在指定行后添加一行或多行文本
c			取代指定的行
d     刪除指定的行
i     插入,在指定的行前添加一行或多行文本
p     打印內容,通常和-n一起使用
s     取代 s#要替換的內容#替換的內容#g 

特殊符號:

!     對指定行以外的所有行應用命令
=     打印當前行號
;     實現一行命令語句可以執行多條sed命令

6.sed匹配行地址范圍解釋

10{sed-commands}					對第10行操作
10,20{sed-commands}				對10到20行操作,包括第10,20行
10,+20{sed-commands}			對10到30(10+20)行操作,包括第10,30行
1~2{sed-commands}					對1,3,5,7...行操作
10,$(sed-commands)				對10到最后一行($代表最后一行)操作,包括第10行
/oldboy/{sed-commands}		對匹配oldboy的行操作
/oldboy/,/Alex/{sed-commands}	對匹配oldboy的行到匹配Alex的行操作
/oldboy/,${sed-commands}	對匹配oldboy的行到最后一行操作
/oldboy/,10{sed-commands}	對匹配oldboy的行到第10行操作.注意:如果前10行沒有匹配到oldboy,sed軟件會顯示10行以后的匹配oldboy的行,如果有
1,/Alex/{sed-commands}		對第一行匹配到Alex的行操作
/oldboy/,+2{sed-commands}	對匹配oldboy的行到其后的2行操作

7.sed練習題-增

測試文本

cat > oldboy.txt << EOF
I am zhangya teacher!
I teach linux.
test

I like swimming, football, basketball, video games
I like Apple's devices
my blog is https://www.cnblogs.com/alaska/
my site is https://www.jianshu.com/u/ee1c7fcea5b0
my qq num is 526195417.
my phone is 15321312624.
EOF

單行增加

sed '2a good good study day day up' oldboy.txt 

多行增加

sed '2a good good study day day up\nyou can up no can no bb' oldboy.txt 

優化SSH配置

sed '3i Port 52113\nPermitRootLogin no\nPerminEmptyPasswords no\nUseDNS no\nGSSAPIAuthentication no' oldboy.txt

8.sed練習題-刪

刪除指定行

sed '2d' person.txt 

刪除連續多行

sed '2,5d' person.txt 

刪除指定的幾行

sed '2d;5d' person.txt 

使用正則刪除指定行

sed '/zhangyao/d' person.txt 

使用步長刪除行

sed '1~2d' person.txt 

刪除指定行往后的N行

sed '1,+2d' person.txt 

取反刪除

sed '2,3!d' person.txt

9.sed練習題-改

按行替換

sed '2c 106,dandan,CSO' person.txt 

文本替換

sed 's#zhangyao#XXXXX#g' person.txt 

修改指定行配置文件

sed '3s#0#9#' person.txt 

變量替換

x=1
y=2
sed 's#$x#$y#g' person.txt 

單引號和雙引號區別


黑科技-分組替換

echo "I am oldboy teacher"|sed 's#^.*am \([a-z].*\) tea.*$#\1#g'
echo "I am oldboy teacher"|sed -r 's#I (.*) (.*) teacher#\1\2#g'

使用sed取出eth0的IP地址

ifconfig eth0|sed -rn '2s#^.*r:(.*) Bc.*$#\1#gp'

批量重命名文件

touch stu_102999_{1..5}_finished.jpg
ls|sed -r 's#(.*)_(.*)_(.*)_(.*)(\..*)#\1_\2_\3\5#g'
ls|sed -r 's#(.*)_(.*)_(.*)_(.*)(\..*)#mv & \1_\2_\3\5#g'
ls|sed -r 's#(.*)_(.*)_(.*)_(.*)(\..*)#mv & \1_\2_\3\5#g'|bash
ls *.jpg|sed -r 's#(^.*)_finished.*$#mv & \1.jpg#'
ls stu*|xargs -n1|sed -r "s#(^.*[1-9]).*(.jpg)#mv & \1\2#g"
ls|awk -F "_finished" '{print "mv",$0,$1$2}'
rename "_finished" "" *.jpg

10.sed練習題-查

按行查詢

sed '2p' person.txt 

查詢多行

sed -n '2,3p' person.txt 

按步長查詢

sed -n '1~2p' person.txt 

按字符串查詢

sed -n '/CTO/p' person.txt 
sed -n '/CTO/,/CFO/p' person.txt 

混合查詢

sed -n '2,/CFO/p' person.txt 

多慮多個字符

sed -rn '/oldboy|feixue/p' person.txt

11.sed練習題-修改文件

更改文件

sed -i 's#zhangya#NB#g' person.txt 

更改文件同時備份

sed -i.ori 's#zhangya#NB#g' person.txt 

文件另存為

sed 'w output.txt' person.txt

12.sed練習題-替換

替換命令說明

sed '[地址范圍|模式范圍] s#[被替換的字符串]#[替換后的字符串]#[替換標志]' [輸入文件]
命令說明:
1.[地址范圍|模式范圍]:可選選項,如果沒有指定,sed軟件將在所有行上進行替換。
2.“s”即執行替換命令substitute
3.被替換的字符串,可以是一個正則表達式
4.替換后的字符串,只能是一個具體的內容
5.替換標志:可選選項,包括前面詳述的全局標志g,還有數字標志(1,2,3...),打印標志p,寫標志w,忽略大小寫標志i,執行命令標志e,根據需要可以把一個或多個替代標志組合起來使用。

Ms# # #Ng 的使用
Ms ==》對第M行處理,無g替換標志,只處理第一行匹配,有g替換標志則對第M行全部替換
Ng ==》對每一行,從第N出開始替換
Ms,Ng組合表示只對第M行從第N處匹配開始替換

測試語句

cat > num.txt << EOF
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
EOF

替換

sed 's#1#0#2g' num.txt
sed ' 2s#1#0#g ' num.txt 
sed '2s#1#0#2g' num.txt 

數字標志

測試樣本

cat > person.txt <<EOF
101,Zhangya,CEO
102,Liangzai,CTO
103,Json,COO
104,Jangli,CFO
105,Xiayan,CIO
EOF

替換指定行第N次匹配內容並忽略大小寫

sed '1s#a#XXX#2i' person.txt
sed '1s#o#XXX#3i' person.txt

打印標志

注意:這里的P是附屬於s命令的。

首先將zhangya替換成NB,然后使用p標志打印到屏幕,同時使用-n選項取消默認輸出,

這個標志可以實現修改完文本直接顯示修改的結果

sed -n 's#zhangya#NB#p' person.txt

寫標志

寫標志w,當替換操作執行成功后,它把替換后的結果保存到文件中,這個也類似於前面學過的sed命令w。

sed -n 's#zhangya#NB#w output.txt' person.txt

忽律大小寫標志

前面我們學習過的匹配功能都是要將匹配字符原樣寫上,是區分大小寫。這也是默認的功能,但是我們可以使用忽略大小寫標志i來忽略大小寫。

sed 's#alex#NB#i' person.txt

執行命令標志

執行命令標志e,可以將模式空間中的任何內容當作shell命令執行,並把命令執行的結果返回模式空間。

sed 's#^#ls -lh #e' file.txt 

命令說明:注意,“^”尖角號代表每一行最頭頭的地方,后面的命令后有一個空格要注意!

其他替換標志

替換標志	含義
\l			在替換字符中shiyong\l標志時,他會把緊跟其后面的1個字符當做小寫字符來處理
\L			在替換字符中shiyong\L標志時,他會把后面所有的字符都當作小寫字符來處理
\u			在替換字符中使用\u標志時,他會把緊其后面的1個字符當作大寫字符來處理
\U			在替換字符中使用\U標志時,他會把后面所有的字符都當作大寫字符來處理。
\E			需要\U或\L一起使用,他將關閉\U或\L的功能

13.sed練習題-特殊符號

獲取行號

sed "=" person.txt

只打印1到3的行號,同時打印輸入文件中的內容

sed "1,3=" person.txt

打印匹配的行所在的行號

sed "/zhangya/=" person.txt

只顯示行號不顯示行的內容

sed -n "/zhangya/=" person.txt

顯示文件總行數

sed -n "$=" person.txt

改進方法

sed "=" person.txt|sed 'N;s#\n# #'

打印不可見字符l

sed -n 'l' person.txt

14.sed執行多條命令

面試題:用一條sed語句實現刪除文件的第三行到末尾的數據,並把剩余的數字10替換為 01

sed -e '3,$d' -e 's#10#01#' person.txt

分號;的使用

sed '3,$d;s#10#01#' person.txt

從sed腳本讀取

創建腳本

cat > person.sed << 'EOF' 
3,$d
s#10#01#
EOF

執行

sed -f person.sed person.txt

面試題:一個文件100行,把5,35,70行單獨拿出來

sed -n '5p;35p;70p' /etc/services 

特殊符號{ }

說明:

{command}可以把多個命令括起來,用來處理單個地址或者地址范圍。

需求:打印文件的第2行到第4行,並只顯示這3行的行號

有問題的輸出

sed -n '2,4p;=' person.txt

正確的輸出

sed -n '2,4{p;=}' person.txt

15.sed模擬其他命令

sed -n '/zhangyao/p' person.txt
sed -n '/oldboy/ !p' person.txt
sed '3,$d' person.txt
sed '1,2p' person.txt
sed '2q' person.txt
sed -n "$=" person.txt

15.綜合練習題

1.取出/etc/passwd 的第一列
1.取IP的幾種方法
1.調換passd第一列和第三列位置

答案

egrep -o "^[^:]+" /etc/passwd
egrep -o "^[a-zA-Z0-9]+" /etc/passwd
sed 's#:x:.*$##g' passwd 
sed -r 's#:.*$##g' passwd
sed -r 's#(.):.*$#\1#g' passwd
sed -r 's#(^[^:]+).*$#\1#g' passwd 
sed -r 's#(^.*):x.*$#\1#g' passwd
sed -r 's#(^[a-zA-Z0-9#]+).*$#\1#g' passwd
ifconfig eth0|egrep -o "[0-9]+.[0-9]+.[0-9]+.[0-9]+"|head -1
ifconfig eth0|sed -nr '2s#^.*r:(.*) B.*$#\1#gp'
ifconfig eth0|sed -nr '2s#^.*addr:(.*)  Bca.*#\1#gp'
ifconfig eth0|sed -rn '2s#^.*r:|  Bc.*$##gp' 
ifconfig eth0|sed -rn '2s#^[a-zA-Z :]+(.*)  B.*$#\1#gp'
ifconfig eth0|sed -rn '2s#^[^0-9]+(.*)  B.*$#\1#gp'
ifconfig eth0|awk -F "[: ]" 'NR==4{print $3}'
ifconfig eth0|awk -F "addr:|Bcast" 'NR==2{print $2}'
awk -F ":" 'BEGIN{OFS=":"}{print $7,$2,$3,$4,$5,$6}' root.txt 
sed -r 's#(.*)(:x.*t:)(.*)#\3\2\1#g' root.txt 
sed -r 's#(^[a-zA-Z0-9#]+)(:.*:)(.*$)#\3\2\1#g' passwd
sed -r 's#(^[^:]+)(:.*:)(.*$)#\3\2\1#g' passwd

第14章 三劍客命令之AWK


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM