Shell腳本

ShellScript概念

程序

程序:算法+數據結構,數據是程序的核心javascript

  • 算法:處理數據的方式php

  • 數據結構:數據在計算機中的類型和組織方式html


分類

按程序編程風格:java

  • 過程式:以指令爲中心,數據服務於指令node

  • 對象式:以數據爲中心,指令服務於數據python


按程序運行方式:mysql

  • 編譯運行:高級語言-->編譯器-->機器代碼-->執行linux

    源代碼須要編譯器轉換爲程序文件,運行程序文件時不須要編譯器的參與,所以程序執行效率高ios

    好比:C,C++nginx

  • 解釋運行:高級語言-->執行-->解釋器-->機器代碼

    源代碼不須要事先編譯,運行時啓動解釋器然後由解釋器邊解釋邊運行,所以效率比較低

    好比:shell,python,php,JavaScript,perl


按編程實現是調用庫仍是調用外部的程序文件:

  • 非完整編程語言:利用系統上的命令及編程組件進行編程,shell腳本

  • 完整的編程語言:利用庫和編程組件進行編程,非shell腳本


按編程模型:

  • 面向過程編程語言

    以指令爲中心來組織代碼,以過程或函數爲基礎,數據服務於代碼,圍繞指令來組織數據;這種語言對底層硬件,內存等操做比較方便,可是寫代碼和調試維護等會很麻煩。 他們按照順序執行,選擇執行,循環執行 好比:C bash C++ python

  • 面向對象的編程語言

    以數據爲中心來組織代碼,以對象做爲基本程序結構單位的程序設計語言,指令服務於數據,圍繞數據來組織指令;指用於描述的設計是以對象爲核心,而對象是程序運行時刻的基本成分。語言中提供了類、繼承等成分。 對象:特定的數據類型 類class:實例化成爲對象 好比:Java C++ python


綜上所述可知:

shell腳本編程屬於解釋運行過程式編程語言且依賴於外部程序文件來運行


編程基本結構

  • 各類系統命令的組合

  • 數據存儲:變量、數組

  • 表達式: a+b

  • 語句: if

bash腳本

一種爲shell編寫的腳本程序;

是Linux命令的堆砌;

但因爲Linux中不少命令不具備冪等性,須要設定程序邏輯來判斷運行條件是否知足,以免其運行中發生錯誤

冪等性

即一個操做,不論執行多少次,產生的效果和返回的結果都是同樣的!


ShellScript做用

減小重複性的工做

  • 自動化經常使用命令

  • 執行系統管理和故障排除

  • 建立簡單的應用程序

  • 處理文本或文件

  1. 自動化安裝操做系統

    1. kickstart 底層shell腳本

    2. cobbler 底層shell腳本

  2. 初始化操做系統 SSH優化 關閉SElinux 防火牆放行須要的端口(80 443 22修改 10050) YUM源 時間同步 系統最大描述符 內核參數優化 字符集優化 禁止開機自動啓動 修改主機名稱 (修改公司網卡名稱)... 手動操做要注意 命令行安全 bash的命令歷史 寫入shell腳本(經常使用)

  3. 安裝服務 Nginx PHP MySQL Rsync等等... 針對不一樣的版本寫入shell腳本自動安裝

  4. 配置服務

  5. 啓動服務 全部的服務底層的啓動方式都是使用的shell腳本 公司本身研發的程序 nohup python3.5 test.py --redis --port --mysql --port -a xxxx & 複製一下 寫入腳本 sh start_test_py.sh 如何中止py程序 ps axu|grep test.py |grep -v grep|awk '{print $2}'|xargs kill -9 複製一下 寫入腳本 sh stop_test_py.sh 把py的進程的端口和PID取出來 來判斷是否運行

  6. 日誌統計 查看程序運行的狀況 統計咱們須要的數據 日誌切割 定時任務+腳本 統計數據 定時任務+腳本 ---> 經過郵件發送給管理員 ELK 日誌統計界面 py開發日誌界面 py界面----> 數據庫 <----數據 日誌展現

  7. 監控 監控服務 服務端口是否存在 服務是否存在 服務器的硬件資源使用狀況 狀態 日誌 網絡 Zabbix 經過腳本統計---> 測試---> 添加到zabbix服務 (cacti監控流量 Nagios寬帶運營商 IT公司)

ShellScript規範

腳本文件建立約定

  1. 腳本存放在固定的目錄/server/scripts統一管理

  2. 腳本使用.sh結尾,讓咱們能識別是shell腳本

  3. 腳本命名,見名知其意

  4. 腳本內的註釋最好不用中文(能夠用)

  5. 腳本內的成對的符號一次性寫完再寫內容

腳本代碼開頭約定

  1. 默認解析器: #!/usr/bin/env bash 會本身判斷使用的shell是什麼,並加載相應的環境變量

  2. 程序名,避免更改文件名爲沒法找到正確的文件

  3. 版本號

  4. 修改時間

  5. 做者相關信息

  6. 該程序的做用,及注意事項

  7. 最後是各版本的更新簡要說明

 #!/usr/bin/env bash
 # ------------------------------------------
 # Filename: hello.sh
 # Revision: 1.0
 # Date: 2020/10/22
 # Author: liupenghui
 # Email: 15094034633@163.com
 # Description: This is the first script
 # Copyright: 2020 liu
 # License: GPL
 # ------------------------------------------
 echo 「hello world」

註釋

 # 主函數 []<-()                   <-------函數註釋這樣寫
 function main(){
  local var="Hello World!!!"
   echo ${var}
 }
 # info級別的日誌 []<-(msg:String) <-------帶入參的函數註釋
 log_info(){
   echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: [info] $*" >&2
 }
 # error級別的日誌 []<-(msg:String) <-------帶入參的函數註釋
 log_error(){
   # todo [error]用紅色顯示         <------函數內註釋
  local msg=$1 # 將要輸出的日誌內容 <------變量的註釋緊跟在變量的後面
   if [[ x"${msg}" != x"" ]];then
     # 註釋                       <-------函數內註釋 `#` 與縮進格式對整齊
     echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]:[error] $*" >&2
   fi
 }

縮進

  1. 使用兩個空格進行縮進,不使用tab縮進

  2. 不在一行的時候使用 \ 進行換行,使用 \ 換行的原則是整齊美觀

 #!/usr/bin/env bash
 # 腳本使用幫助文檔 []<-()
 manual(){
   cat "$0"|grep -v "less \"\$0\"" \
          |grep -B1 "function "   \
          |grep -v "\\--"         \
          |sed "s/function //g"   \
          |sed "s/(){//g"         \
          |sed "s/#//g"           \
          |sed 'N;s/\n/ /'       \
          |column -t             \
          |awk '{print $1,$3,$2}' \
          |column -t
 }
 function search_user_info(){
  local result=$(httpclient_get --cookie "${cookie}" \
                                          "${url}/userName=${user_name}")
 }

執行方式

  1. 使用解釋器(sh或者bash)運行腳本,開啓一個子shell運行腳本內容

  2. 執行腳本絕對路徑或相對路徑,須要腳本有執行權限

  3. 使用. 或者source運行腳本,在當前父shell中運行裏面的內容

  4. 傳遞給|bash執行,不經常使用

能夠給腳本加上執行權限chmod +x /server/scripts/one.sh,並將腳本的絕對路徑添加到path變量中echo PATH=/server/scripts:$PATH > /etc/profile.d/shell.sh,使變量當即生效. /etc/profile.d/shell.sh,就能夠像運行普通命令同樣直接執行腳本了!

父進程和子進程

 [root@oldboyedu-lnb ~]# name=f;(echo $name;name=z;echo $name);echo $name # 小括號會開啓子進程,賦予的變量,只在小括號內有效,執行完命令後,就會退出子進程。
 f
 z
 f
 [root@oldboyedu-lnb ~]# name=f;{ echo $name;name=z;echo $name; };echo $name # 大括號不會開啓子進程,在當前進程有效,執行完命令後,留在當前進程。
 f
 z
 z

調試

 # 語法檢測
 bash -n /path/to/script
 # 調試執行
 bash -x /path/to/script

echo打印顏色字

 echo -e "\033[31malong\033[0m"       顯示紅色along
 echo -e "\033[1;31malong\033[0m"     高亮顯示紅色along
 echo -e "\033[41malong\033[0m"       顯示背景色爲紅色的along
 echo -e "\033[31;5malong\033[0m"     顯示閃爍的紅色along
 
 color=$[$[RANDOM%7]+31]
 echo -ne "\033[1;${color};5m*\033[0m" 顯示閃爍的隨機色along

ShellScript變量

命名法則

  1. 不能使程序中的保留字:例如if,for

  2. 只能使用數字、字母及下劃線,且不能以數字開頭

  3. 見名知義

  4. 統一命名規則:駝峯命名法

  • 建議:

  1. 全局變量大寫

  2. 局部變量小寫

  3. 函數名小寫

格式

  1. 變量賦值使用 = 等號,左右不能留有空格

  2. 使用變量時推薦使用 "${}" 雙引號和大括號包裹

 var1="Hello World"   # 正確,推薦使用雙引號
 var2=6.70            # 小數
 var3="${var1}"       # 推薦 雙引號和大括號 包裹

單引號裏的任何字符都會原樣輸出,單引號字符串中的變量是無效的,單引號字串中不能出現單引號(對單引號使用轉義符後也不行)。 雙引號中的普通字符都會原樣輸出,可使用$引用變量,雙引號中能夠出現單引號。

  1. 常量必定要定義成readonly

  2. 函數中的變量要用local修飾,定義成局部變量,這樣在外部遇到重名的變量也不會影響

 web="www.chen-shang.github.io"
 function main(){
  local name="chenshang" # 這裏使用local定義一個局部變量
  local web="${web}"     # 這裏${}內的web是全局變量,以後在函數中在使用web變量都是使用的局部變量
  local web2="${web}"    # 對於全局變量,雖然在使用的時候直接使用便可,但仍是推薦使用一個局部變量進行接收,而後使用局部變量,以防止在多線程操做的時候出現異常(至關於java中的靜態變量在多線程中的時候須要注意線程安全同樣,但常量除外)
 }
  1. 變量一經定義,不容許刪除(也就是禁用unset命令)

類型

強類型:

變量不通過強制轉換,它永遠是這個數據類型,不容許隱式的類型轉換。通常定義變量時必須指定類型、參與運算必須符合類型要求;調用未聲明變量會產生錯誤 如:java , c# ,python

弱類型:

語言的運行時會隱式作數據類型轉換。無須指定類型,默認均爲字符型;參與運算會自動進行隱式類型轉換;變量無須事先定義可直接調用 如:bash 不支持浮點數,php,javascript


shell中變量的基本類型就是String、數值(能夠本身看作Int、Double之類的)、Boolean。

Boolean 實際上是Int類型的變種, 在shell中0表明真、非0表明假,因此每每在shell腳本中用 readonly TURN=0 && readonly FALSE=1

根據變量的生效範圍等標準劃分下面變量類型:

局部變量:生效範圍爲當前shell進程;對當前shell以外的其它shell進程,包括當前shell的子shell進程均無效

環境變量:生效範圍爲當前shell進程及其子進程

本地變量:生效範圍爲當前shell進程中某代碼片段,一般指函數

位置變量:$1, $2, ...來表示,用於讓腳本在腳本代碼中調用經過命令行傳遞給它的參數

特殊變量:$?, $0, $*, $@, $#,$$

局部變量

  • 變量賦值:name=‘value’

  • 變量引用:${name} 或者 $name

(1) 能夠是直接字串:name=「root"
(2) 變量引用:name="$USER"
(3) 命令引用:name=`COMMAND`
            name=$(COMMAND)
  • " " 弱引用,其中的變量引用會被替換爲變量值

  • ' ' 強引用,其中的變量引用不會被替換爲變量值,而保持原字符串

  • 顯示已定義的全部變量:set

  • 刪除變量:unset name

環境變量

  • 變量聲明、賦值: export name=VALUE declare -x name=VALUE

  • 變量引用: $name, ${name}

  • 顯示全部環境變量: env print env export declare -x

  • 刪除變量:unset name

  • bash內建的環境變量 PATH SHELL USER UID HOME PWD SHLVL LANG MAIL HOSTNAME HISTSIZE _ 下劃線

只讀變量

只能聲明,但不能修改和刪除

  • 聲明只讀變量: readonly name declare -r name

  • 查看只讀變量: readonly -p

位置變量

在腳本代碼中調用經過命令行傳遞給腳本的參數

$1, $2, ... 對應第一、第2等參數,shift [n]換位置,從$9之後須要加{}表示總體
set --     清空全部位置變量

特殊變量

  $0         腳本文件名稱,若是全路徑執行則帶全路徑,可使用basename只獲取名字
  $#         傳遞給腳本的參數的個數
  $*         傳遞給腳本的全部參數
  $@         傳遞給腳本的全部參數
  "$*"       所有參數合爲一個字符串,可在循環中驗證
  "$@"       每一個參數爲獨立字符串,可在循環中驗證
  $$         運行腳本的PID
  $!         上一個運行腳本的PID
  $_         當前命令行的最後一個參數, 相似於ESC .

$$$BASHPID區別:二者都是當前進程的編號,可是$BASHPID更精確

basename

只輸出路徑的基名

[root@oldboyedu-lnb ~]#  basename /etc/passwd
passwd

進程使用退出狀態來報告成功或失敗

0       表明成功
1-255  表明失敗
$?      保存上一條命令的退出狀態

例如:

ping -c1 -W1 hostdown &> /dev/null
echo $?

exit [n]     自定義退出狀態碼

注意:

  • 腳本中一旦遇到exit命令,腳本會當即終止;終止退出狀態取決於exit命令後面的數字

  • 若是未給腳本指定退出狀態碼,整個腳本的退出狀態碼取決於腳本中執行的最後一條命令的狀態碼

子串

[root@shell ~]# test='I am oldboy'
[root@shell ~]# url='www.baidu.com'

${var:n:x}切片

[root@shell ~]# echo ${test:2:2}  # (2,2+2]從第二個字符開始向後兩位爲止
am
[root@shell ~]# echo $test|awk '{print $2}'
am
[root@shell ~]# echo $test|cut -c3-4
am

${#var}字符長度

[root@shell ~]# echo ${#test}
11
[root@shell ~]# echo $test|wc -L
11
[root@shell ~]# expr length "$test"
11
[root@shell ~]# echo $test|awk '{print length}'
11

統計出字符串小於3的單詞 筆試題
I am lzhenya teacher I am 18

[root@shell ~]# cat for.sh
for i in I am lzhenya teacher I am 18
do
		[ ${#i} -lt 3 ] && echo $i
done
[root@shell ~]# sh for.sh
I
am
I
am
18

[root@shell ~]# echo I am lzhenya teacher I am 18|xargs -n1|awk '{if(length<3)print}'
I
am
I
am
18

[root@shell ~]# echo I am lzhenya teacher I am 18|awk '{for(i=1;i<=NF;i++)if(length($i)<3)print $i}'
I
am
I
am
18

刪除匹配內容

支持通配符*

${var#}從前日後匹配,${var##}貪婪匹配

${var%}從後往前匹配,${var%%}貪婪匹配

若是要匹配#%,需使用\轉義

[root@shell ~]# echo ${url#www.}
baidu.com
[root@shell ~]# echo ${url#*.}
baidu.com
[root@shell ~]# echo ${url#*.*.}
com
[root@shell ~]# echo ${url##*.}
com

[root@shell ~]# echo ${url%.com}
www.baidu
[root@shell ~]# echo ${url%.*}
www.baidu
[root@shell ~]# echo ${url%.*.*}
www
[root@shell ~]# echo ${url%%.*}
www

${var/a/b}替換匹配內容

${var//a/b}貪婪匹配

[root@shell ~]# echo ${url/w/W}
Www.baidu.com
[root@shell ~]# echo ${url//w/W}
WWW.baidu.com
[root@shell ~]# echo ${url/baidu/sina}
www.sina.com
[root@shell ~]# echo $url|sed 's#www#WWW#g'
WWW.baidu.com

ShellScript算術運算

+, -, *, /, %取模(取餘), **(乘方),乘法符號有些場景中須要轉義

  1. 整數計算使用 expr或者 $[]或者$(())(運算最快)或者 let

  2. 小數計算使用 bc 計算器

  • 實現算術運算:

(1) var=$(expr arg1 arg2 arg3 ...)
(2) var=$[算術表達式]
(3) var=$((算術表達式))
(4) let var=算術表達式
(5) declare –i var = 數值
(6) echo ‘算術表達式’ | bc
  • 隨機數

bash有內建的隨機數生成器變量:$RANDOM(0-32767)
生成隨機數 echo $RANDOM
生成指定範圍隨機數

示例:
# 生成隨機7個數(0-6)
echo $[RANDOM%7]
# 生成隨機7個數(31-37)
echo $[$[RANDOM%7]+31]
生成隨機字符:cat /dev/urandom
# 生成8個隨機大小寫字母或數字
cat /dev/urandom |tr -dc [:alnum:] |head -c 8
tr -dc ‘a-zA-Z0-9’</dev/urandom|head -c8 
  • 加強型賦值:

+=, -=, *=, /=, %=

let var OPER value
例如:let count+=3   自加3後自賦值
自增,自減:
let var+=1
let var++
let var-=1
let var--
let var=i++   是賦值後加
let var=++i   是先加後賦值
# 取1-63的餘,其中 RANDOM%63 的值是0-62,加1就是1-63
echo $[RANDOM%63+1]
# 生成隨機顏色
echo -e  "\033[1;$[RANDOM%7+31]m 字符串\033[0m"
  • 邏輯運算

 true, false
 1, 0
 
 與 &
 1 與 1 = 1
 1 與 0 = 0
 0 與 1 = 0
 0 與 0 = 0
 
 或 |
 1 或 1 = 1
 1 或 0 = 1
 0 或 1 = 1
 0 或 0 = 0
 
 非 !
 ! 1 = 0 ! true
 ! 0 = 1 ! false
 
 短路與 &&
 第一個爲0,結果一定爲0
 第一個爲1,第二個必需要參與運算
 
 短路或 ||
 第一個爲1,結果一定爲1
 第一個爲0,第二個必需要參與運算
 
 異或:^ 異或的兩個值,相同爲假,不一樣爲真
 
 短路與和短路或
 [ $RANDOM%6 –eq 0 ] && rm –rf /* || echo 「click」
 # 數字互換
 A=10;B=20;A=$[A^B];B=$[A^B];A=$[A^B];echo A=$A B=$B
 
 A=01010=10
 B=10100=20
 A=$[A^B]=11110=30
 
 A=11110=30
 B=10100=20=10
 B=$[A^B]=01010
 
 A=11110=30
 B=01010=10
 A=$[A^B]=10100=20
 
 [root@oldboyedu-lnb ~]# A=10;B=20;A=$[A^B];B=$[A^B];A=$[A^B];echo A=$A B=$B
 A=20 B=10

ShellScript條件測試

非特別說明,則全部文件類操做都會追蹤到軟連接的源文件

 test EXPRESSION
 [ EXPRESSION ]
 (( EXPRESSION ))   算術表達式
 [[ EXPRESSION ]]   不會發生文件名擴展或者單詞分割,會發生參數擴展和命令替換
 注意:EXPRESSION 先後必須有空白字符
test -d "$HOME" ;echo $?
[ "abc" != "def" ];echo $?
test EXPRESSION && echo "exist" || echo "not exist"  更人性化地顯示結果
test EXPRESSION && echo true || echo false           更人性化地顯示結果

test -e file && echo "exist" || echo "not exist"
test 3 -gt 4 && echo true || echo false
  • bash的數值測試

  -v VAR   變量VAR是否設置
數值測試:
  -gt 是否大於
  -ge 是否大於等於
  -eq 是否等於
  -ne 是否不等於
  -lt 是否小於
  -le 是否小於等於
  • bash的字符串測試

=     是否等於
>     ascii碼是否大於ascii碼
<     是否小於
!=    是否不等於
=~    左側字符串是否可以被右側的 正則表達式 所匹配
注意: 此表達式通常用於[[ ]]中,[[ ]]中匹配正則表達式或通配符,不須要引號

[[ hello == hell? ]] && echo true || echo false
[[ 2\<3 ]] && echo true || echo false
[[ 0 < 1 ]] && echo true || echo false
[[ 2 -lt 3 ]] && echo true || echo false
[ 2 \< 3 ] && echo true || echo false
[ 1 = 1 ] && echo true || echo false

-z "STRING「 字符串是否爲空,空爲真,不空爲假
-n "STRING「 字符串是否不空,不空爲真,空爲假
注意:用於字符串比較時的用到的操做數都應該使用引號
  • bash的文件測試

存在性測試
    -a FILE:同 -e
    -e FILE: 文件存在性測試,存在爲真,不然爲假
存在性及類別測試
    -b FILE:是否存在且爲塊設備文件
    -c FILE:是否存在且爲字符設備文件
    -d FILE:是否存在且爲目錄文件
    -f FILE:是否存在且爲普通文件
    -h FILE 或 -L FILE:存在且爲符號連接文件
    -p FILE:是否存在且爲命名管道文件
    -S FILE:是否存在且爲套接字文件
  • bash的文件權限測試

文件權限測試:
    -r FILE:是否存在且可讀
    -w FILE: 是否存在且可寫
    -x FILE: 是否存在且可執行
文件特殊權限測試:
    -u FILE:是否存在且擁有suid權限
    -g FILE:是否存在且擁有sgid權限
    -k FILE:是否存在且擁有sticky權限
  • bash的文件屬性測試

文件大小測試:
    -s FILE: 是否存在且非空
文件是否打開:
    -t fd: fd 文件描述符是否在某終端已經打開
    -N FILE:文件自從上一次被讀取以後是否被修改過
    -O FILE:當前有效用戶是否爲文件屬主
    -G FILE:當前有效用戶是否爲文件屬組
雙目測試:
    FILE1 -ef FILE2: FILE1是不是FILE2的硬連接
    FILE1 -nt FILE2: FILE1是否新於FILE2(mtime)
    FILE1 -ot FILE2: FILE1是否舊於FILE2
  • bash的組合測試條件

第一種方式:
EXPRESSION1 -a EXPRESSION2 而且,只能在test或[]中使用
EXPRESSION1 -o EXPRESSION2 或者,只能在test或[]中使用
! EXPRESSION 非

第二種方式:
COMMAND1 && COMMAND2 而且,短路與,表明條件性的AND THEN,只能在[[]]中使用
COMMAND1 || COMMAND2 或者,短路或,表明條件性的OR ELSE,只能在[[]]中使用
! COMMAND 非
如:[ -f 「$FILE」 ] && [[ 「$FILE」=~ .*\.sh$ ]]
  • 條件性的執行操做符

示例:

grep -q no_such_user /etc/passwd || echo 'No such user'
No such user

ping -c1 -W2 station1 &> /dev/null \
> && echo "station1 is up" \
> || (echo 'station1 is unreachable'; exit 1)
station1 is up

test "$A" = "$B" && echo "Strings are equal"
test 「$A」-eq 「$B」 && echo "Integers are equal「
[ "$A" = "$B" ] && echo "Strings are equal"
[ "$A" -eq "$B" ] && echo "Integers are equal「
[ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab
# 判斷字符串爲空或者localhost.localdomain,則臨時修改主機名爲 www.magedu.com
[ -z 「$HOSTNAME」 -o $HOSTNAME "=="localhost.localdomain" ] && hostname www.magedu.com  

ShellScript相關命令

read

read           把輸入值分配給一個或多個shell變量
     -a        後跟一個變量,該變量會被認爲是個數組,而後給其賦值,默認是以空格爲分割符
     -e 	   在輸入的時候可使用命令補全功能
     -r 	   屏蔽\的轉義功能
     -u 	   後面跟fd,從文件描述符中讀入,該文件描述符能夠是exec新開啓的
     -p        指定輸入前打印提示信息
     -s        靜默輸入,輸入的字符不在屏幕上顯示,通常用於密碼
     -n N      指定輸入的字符長度最大爲N
     -d ‘字符’  以輸入的指定‘字符’做爲結束符
     -t N      TIMEOUT爲N秒,超時退出

read 從標準輸入中讀取值,給每一個單詞分配一個變量,全部剩餘單詞都被分配給最後一個變量
read -p 「Enter a filename: 「 FILE

示例: 
修改主機名稱爲shell,而且修改eth0網卡IP地址爲88
[root@shell ~]# cat hostname.sh 
#!/bin/bash
eth0_cfg='/etc/sysconfig/network-scripts/ifcfg-eth0'
old_ip=`ifconfig eth0|awk 'NR==2{print $2}'|awk -F. '{print $NF}'`
read -p "please input hostname: " name
read -p "please input New IP: " IP
hostnamectl set-hostname $name
sed -i "s#$old_ip#$IP#g" $eth0_cfg
grep $IP $eth0_cfg

[root@shell ~]# cat ping.sh 
read -p "Please Input URL: " url
ping -c2 -W1  $url &>/dev/null
[ $? -ne 0 ] && echo "ping不通" || echo "通了"

cat

cat <<EOFcat <<-EOF的區別

用於執行腳本的時候,須要往一個文件裏自動輸入N行內容。

  • cat用於顯示文本文件內容,所有輸出。

  • EOF是END Of File的縮寫,表示自定義終止符,Ctrl-D就表明EOF。

man說明:

If the redirection operator is <<-, then all leading tab characters are stripped from input lines and the line containing delimiter.

翻譯:

若是重定向的操做符是<<-,那麼分界符(EOF)所在行的開頭部分的製表符(Tab)都將被去除。

也就是說:

  • cat <<EOF中EOF必須頂行寫,前面不能用製表符或者空格。若是結束分解符EOF前有製表符或者空格,則EOF不會被當作結束分界符,只會繼續被當作stdin來輸入。

  • cat <<-EOF中就算最後的EOF前面有多個製表符和空格,但仍然會被當作結束分界符,表示stdin的結束。

trap

trap命令用於指定在接收到信號後將要採起的動做,常見的用途是在腳本程序被中斷時完成清理工做。當shell接收到sigspec指定的信號時,arg參數(命令)將會被讀取,並被執行,而不會執行原操做。

trap [-lp] [[arg] sigspec ...]
     -l    讓shell打印一個命令名稱和其相對應的編號的列表
     -p    若是有-p選項而沒有提供arg參數,則會打印全部與sigspec指定信號相關聯的的trap命令;
           若是沒有提供任何參數或者僅有-p選項,trap命令將會打印與每個信號有關聯的命令的列表;
     
[arg]參數缺省或者爲「-」,每一個接收到的sigspec信號都將會被重置爲它們進入shell時的值
[arg]參數是空字符串每個由sigspec指定的信號都會被shell和它所調用的命令忽略;
trap commands signals
# commands 能夠是任何有效的Linux命令,或一個用戶定義的函數,
# signals 能夠是任意數量的信號,或你想來捕獲的列表。
# 信號有3種表達方法:信號的數字二、全名SIGINT、縮寫INT

參考實例:

trap "rm -f $WORKDIR/work1$ $WORKDIR/dataout$; exit" 1 2  收到指定信號後清理臨時文件
trap '' 1 2 20                                            收到指定信號後忽略信號
trap '-' 1 2 20
trap 1 2 20                                               恢復信號的默認操做,重設陷阱

每一個sigspec信號都是是以名字或者編號的形式定義在signal.h頭文件中,信號的名字是不區分大小寫的,其前綴SIG是可選的,有如下狀況:

  1. 若是sigspec是EXIT(0),那麼arg指定的命令將會在shell上執行退出命令時執行

  2. 若是sigspec是DEBUG,那麼arg指定的命令將會在如下每一個命令執行以前執行:

    簡單命令,for語句,case語句,select命令,算法命令,在函數內的第一條命令。

  3. 若是sigspec是ERR,那麼arg指定的命令將會在任何簡單命名執行完後返回值爲非零值時執行,可是也有如下例外狀況,arg命令不會執行,這些規則一樣適用於errexit選項:

    若是執行失敗的命令是緊跟在while或者until關鍵字以後的一組命令中的一部分時

    若是執行失敗的命令是if測試語句的一部分時,是 && 和 ||鏈接的列表中的一部分時

    若是執行失敗的命令的返回值是被取反過的(經過!操做符)

  4. 若是sigspec是RETURN,那麼arg指定的命令在每次shell函數或者腳本用"."或者內置的命令執行完成後執行

注意:

  1. 在shell入口處被忽略的命令是無法被trap和reset的。

  2. 被trap的信號,在建立的子進程中使用時會在子進程被建立時被重置爲原始的值。

  3. 若是trap使用的sigspec信號是無效的信號,則trap命令返回false(失敗),不然返回true(成功)。

① 打印0-9,ctrl+c不能終止

執行腳本後,打印0-9,每秒一個數字,ctrl+c轉換爲echo press ctrl+c

#!/bin/bash
trap 'echo press ctrl+c' 2
for ((i=0;i<10;i++));do
        sleep 1
        echo $i
done

② 打印0-3,ctrl+c不能終止,3以後恢復,能終止

執行腳本後,打印0-3,每秒一個數字,ctrl+c不能終止,打印3以後解除捕獲2信號,能終止

#!/bin/bash
trap '' 2
trap -p
for ((i=0;i<3;i++));do
        sleep 1
        echo $i
done
trap '-' SIGINT
for ((i=3;i<10;i++));do
        sleep 1
        echo $i
done

信號

信號是一種進程間通訊機制,它給應用程序提供一種異步的軟件中斷,使應用程序有機會接受其餘程序活終端發送的命令(即信號)。

應用程序收到信號後,有三種處理方式:忽略,默認,或捕捉。

進程收到一個信號後,會檢查對該信號的處理機制:

  1. 若是是SIG_IGN,就忽略該信號;

  2. 若是是SIG_DFT,則會採用系統默認的處理動做,一般是終止進程或忽略該信號;

  3. 若是給該信號指定了一個處理函數(捕捉),則會中斷當前進程正在執行的任務,轉而去執行該信號的處理函數,返回後再繼續執行被中斷的任務。

在有些狀況下,咱們不但願本身的shell腳本在運行時刻被中斷,好比說咱們寫得shell腳本設爲某一用戶的默認shell,使這一用戶進入系統後只能做某一項工做,如數據庫備份,咱們不但願用戶使用Ctrl+c之類可以進入到shell狀態,作咱們不但願作的事情。這便用到了信號處理。

常見信號:

1) SIGHUP: 無須關閉進程而讓其重讀配置文件

2) SIGINT: 停止正在運行的進程;至關於Ctrl+c

3) SIGQUIT: 至關於ctrl+\

9) SIGKILL: 強制殺死正在運行的進程;本信號不能被阻塞,處理和忽略。

15) SIGTERM :終止正在運行的進程(默認爲15)

18) SIGCONT :繼續運行

19) SIGSTOP :後臺休眠

信號名稱 信號數 描述
SIGHUP 1 本信號在用戶終端鏈接(正常或非正常)結束時發出, 一般是在終端的控制進程結束時, 通知同一session內的各個做業, 這時它們與控制終端再也不關聯。登陸Linux時,系統會分配給登陸用戶一個終端(Session)。在這個終端運行的全部程序,包括前臺進程組和後臺進程組,通常都屬於這個Session。當用戶退出Linux登陸時,前臺進程組和後臺有對終端輸出的進程將會收到SIGHUP信號。這個信號的默認操做爲終止進程,所以前臺進程組和後臺有終端輸出的進程就會停止。對於與終端脫離關係的守護進程,這個信號用於通知它從新讀取配置文件。
SIGINT 2 程序終止(interrupt)信號, 在用戶鍵入INTR字符(一般是Ctrl+C)時發出。
SIGQUIT 3 和SIGINT相似, 但由QUIT字符(一般是Ctrl+/)來控制。進程在因收到SIGQUIT退出時會產生core文件,在這個意義上相似於一個程序錯誤信號。
SIGFPE 8 在發生致命的算術運算錯誤時發出。不只包括浮點運算錯誤,還包括溢出及除數爲0等其它全部的算術的錯誤。
SIGKILL 9 用來當即結束程序的運行。本信號不能被阻塞,處理和忽略。
SIGALRM 14 時鐘定時信號,計算的是實際的時間或時鐘時間。 alarm函數使用該信號。
SIGTERM 15 程序結束(terminate)信號,與SIGKILL不一樣的是該信號能夠被阻塞和處理,一般用來要求程序本身正常退出。shell命令kill缺省產生這個信號。
SIGHUP    1   /* Hangup (POSIX).  */                     終止進程  終端線路掛斷
SIGINT    2   /* Interrupt (ANSI).  */                   終止進程  中斷進程 Ctrl+C
SIGQUIT   3   /* Quit (POSIX).  */                       創建CORE文件終止進程,而且生成core文件 Ctrl+
SIGILL    4   /* Illegal instruction (ANSI).  */         創建CORE文件,非法指令
SIGTRAP   5   /* Trace trap (POSIX).  */                 創建CORE文件,跟蹤自陷
SIGABRT   6   /* Abort (ANSI).  */
SIGIOT    6   /* IOT trap (4.2 BSD).  */                 創建CORE文件,執行I/O自陷
SIGBUS    7   /* BUS error (4.2 BSD).  */                創建CORE文件,總線錯誤
SIGFPE    8   /* Floating-point exception (ANSI).  */    創建CORE文件,浮點異常
SIGKILL   9   /* Kill, unblockable (POSIX).  */          終止進程  殺死進程
SIGUSR1   10  /* User-defined signal 1 (POSIX).  */      終止進程  用戶定義信號1
SIGSEGV   11  /* Segmentation violation (ANSI).  */      創建CORE文件,段非法錯誤
SIGUSR2   12  /* User-defined signal 2 (POSIX).  */      終止進程  用戶定義信號2
SIGPIPE   13  /* Broken pipe (POSIX).  */                終止進程  向一個沒有讀進程的管道寫數據
SIGALARM  14  /* Alarm clock (POSIX).  */                終止進程  計時器到時
SIGTERM   15  /* Termination (ANSI).  */                 終止進程  軟件終止信號
SIGSTKFLT 16  /* Stack fault.  */
SIGCHLD   17  /* Child status has changed (POSIX).  */   忽略信號  當子進程中止或退出時通知父進程
SIGCONT   18  /* Continue (POSIX).  */                   忽略信號  繼續執行一箇中止的進程
SIGSTOP   19  /* Stop, unblockable (POSIX).  */          中止進程  非終端來的中止信號
SIGTSTP   20  /* Keyboard stop (POSIX).  */              中止進程  終端來的中止信號 Ctrl+Z
SIGTTIN   21  /* Background read from tty (POSIX).  */   中止進程  後臺進程讀終端
SIGTTOU   22  /* Background write to tty (POSIX).  */    中止進程  後臺進程寫終端
SIGURG    23  /* Urgent condition on socket (4.2 BSD).*/ 忽略信號  I/O緊急信號
SIGXCPU   24  /* CPU limit exceeded (4.2 BSD).  */       終止進程  CPU時限超時
SIGXFSZ   25  /* File size limit exceeded (4.2 BSD).  */ 終止進程  文件長度過長
SIGVTALRM 26  /* Virtual alarm clock (4.2 BSD).  */      終止進程  虛擬計時器到時
SIGPROF   27  /* Profiling alarm clock (4.2 BSD).  */    終止進程  統計分佈圖用計時器到時
SIGWINCH  28  /* Window size change (4.3 BSD, Sun).  */  忽略信號  窗口大小發生變化
SIGIO     29  /* I/O now possible (4.2 BSD).  */         忽略信號  描述符上能夠進行I/O
SIGPWR    30  /* Power failure restart (System V).  */
SIGSYS    31  /* Bad system call.  */

expect

安裝

yum install expect -y
expect [選項] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
       -c:從命令行執行expect腳本,默認expect是交互地執行的
       -d:能夠輸出輸出調試信息  

示例:
expect -c 'expect "\n" {send "pressed enter\n"}
expect -d ssh.exp

expect中相關命令 :

spawn:啓動新的進程
send:用於向進程發送字符串
expect:從進程接收字符串
interact:容許用戶交互,並停留在遠程鏈接的主機上
exp_continue:匹配多個字符串在執行動做後加此命令

expect最經常使用的語法(tcl語言:模式-動做)

單一分支模式語法:

匹配到hi後,會輸出「you said hi」,並換行

expect 「hi」 {send 「You said hi\n"}

多分支模式語法:

匹配hi,hello,bye任意字符串時,執行相應輸出。

expect "hi" { send "You said hi\n" } \  
       "hehe" { send "Hehe yourself\n" } \  
       "bye" { send "Good bye\n" }

等同以下:

expect {  
           "hi" { send "You said hi\n"}  
           "hehe" { send "Hehe yourself\n"}  
           "bye" { send  " Good bye\n"}
}

示例

① 用戶名密碼自動登陸系統

#!/usr/bin/expect
set ip 192.168.7.100
set user root
set password centos
set timeout 10
# 登陸 調用user和ip兩個變量的值
spawn ssh $user@$ip
expect {
# 有發現yes/no 輸入 yes\n
  "yes/no" { send "yes\n";exp_continue }
# 有發現password輸入$password的值
  "password" { send "$password\n" }               
}
# 容許用戶交互
interact

② shell調用expect腳本

#!/bin/bash
ip=$1 
user=$2
password=$3
expect <<EOF   # 開啓expect命令多行重定向
set timeout 20
spawn ssh $user@$ip
expect {        
        "yes/no" { send "yes\n";exp_continue }        
        "password" { send "$password\n" }
}
expect "]#" { send "useradd hehe\n" }
expect "]#" { send "echo centos |passwd --stdin hehe\n" }
expect "]#" { send "exit\n" }
expect eof   # 結束語
EOF

③ 多主機批量操縱:根據相同用戶名和密碼,批量建立用戶

一、建立IP地址清單

[root@oldboyedu-lnb ~]# cat >> iplist.txt << EOF
192.168.7.101
192.168.7.102
192.168.7.103
EOF

二、經過while實現批量讀取文件內容

#!/bin/bash
while read ip;do
user=root
password=centos
expect <<EOF
set timeout 20
spawn ssh $user@$ip
expect {        
        "yes/no" { send "yes\n";exp_continue }        
        "password" { send "$password\n" }
}
expect "]#" { send "useradd hehe\n" }   # 遠程ssh登陸後建立用戶名
expect "]#" { send "echo centos |passwd --stdin hehe\n" }  # 設置密碼
expect "]#" { send "exit\n" }
expect eof
EOF 
done < iplist.txt

④ 多主機批量操縱:根據不一樣用戶名和密碼傳遞公鑰,實現免密鑰登陸

一、建立IP地址,密碼清單

[root@oldboyedu-lnb ~]# cat >> iplist.txt << EOF
192.168.7.101  wangwang
192.168.7.102  centos
192.168.7.103  hahahaha
EOF

二、經過while實現批量讀取文件內容

#!/bin/bash
ssh-keygen -t rsa -P "" -f /root/.ssh/id_rsa
while read ip password;do
user=root
set timeout 10
expect << EOF
spawn ssh-copy-id -i /root/.ssh/id_rsa.pub $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect eof
EOF
done <iplist.txt

ShellScript邏輯語句

  • 順序執行

  • 選擇執行

  • 循環執行

條件語句

if

 選擇執行:可嵌套
 
 單分支
 if 判斷條件;then
    條件爲真的分支代碼
 fi
 
 雙分支
 if 判斷條件; then
    條件爲真的分支代碼
 else
    條件爲假的分支代碼
 fi
 
 多分支
 if 判斷條件1; then
    條件1爲真的分支代碼
 elif 判斷條件2; then
    條件2爲真的分支代碼
 elif 判斷條件3; then
    條件3爲真的分支代碼
 else
    以上條件都爲假的分支代碼
 fi
 
 逐條件進行判斷,第一次遇爲「真」條件時,執行其分支,然後結束整個if語句
 
 Example:
 根據命令的退出狀態來執行命令
 if ping -c1 -W2 station1 &> /dev/null; then
     echo 'Station1 is UP'
 elif grep "station1" ~/maintenance.txt &> /dev/null; then
     echo 'Station1 is undergoing maintenance'
 else
     echo 'Station1 is unexpectedly DOWN!'
 exit 1
 fi

① 判斷年紀

請輸入年紀,先判斷輸入的是否含有除數字之外的字符,有,輸出"please input a int";沒有,繼續判斷是否小於150,是否大於18。

#!/bin/bash
read -p "Please input your age: " age
if [[ $age =~ [^0-9] ]] ;then
    echo "please input a int"
    exit 10
elif [ $age -ge 150 ];then
    echo "your age is wrong"
    exit 20
elif [ $age -gt 18 ];then
    echo "good good work,day day up"
else
    echo "good good study,day day up"
fi

② 判斷分數

請輸入成績,先判斷輸入的是否含有除數字之外的字符,有,輸出"please input a int";沒有,繼續判斷是否大於100,是否大於85,是否大於60。

#!/bin/bash
read -p "Please input your score: " score
if [[ $score =~ [^0-9] ]] ;then
    echo "please input a int"
    exit 10
elif [ $score -gt 100 ];then
    echo "Your score is wrong"
    exit 20
elif [ $score -ge 85 ];then
    echo "Your score is very good"
elif [ $score -ge 60 ];then
    echo "Your score is soso"
else
    echo "You are loser"
fi

case

case 變量引用 in
PAT1)
     分支1
     ;;
PAT2)
     分支2
     ;;
*)
     默認分支
     ;;
esac

case支持glob風格的通配符:
*:   任意長度任意字符
?:   任意單個字符
[]: 指定範圍內的任意單個字符
a|b: a或b

① 判斷yes or no

請輸入yes or no,回答Y/y、yes各類大小寫組合爲yes;回答N/n、No各類大小寫組合爲no。

#!/bin/bash
read -p "Please input yes or no: " anw
case $anw in
[yY][eE][sS]|[yY])
    echo yes
    ;;
[nN][oO]|[nN])
    echo no
    ;;
*)
    echo false
    ;;
esac

循環語句

for

for (( i = 0; i < 10; i++ )); do
  循環體
done

for item in 列表; do
  循環體
done

執行機制:依次將列表中的元素賦值給「變量名」; 每次賦值後即執行一次循環體; 直到列表中的元素耗盡,循環結束

列表:

  • 支持glob通配符,如:{1..10}*.sh

  • 也能夠引用變量 ${array},如:seq 1 $1

① 求(1+2+...+n)的總和

sum初始值爲0,請輸入一個數,判斷輸入的值是否以1-9開頭,後面跟任意個0-9的數字,不是,就報錯;是,進入for循環,i的範圍爲1~輸入的數,每次的循環爲sum=sum+i,循環結束,最後輸出sum的值。

#!/bin/bash
sum=0
read -p "Please input a positive integer: " num
if [[ ! $num =~ ^[1-9][0-9]* ]] ;then
    echo "input error"
else
    for i in `seq 1 $num` ;do
        sum=$[$sum+$i]
    done
    echo $sum
fi

while

while [[ 循環控制條件 ]]; do
  循環體
done

while read -r item ;do
  循環體
done < 'file_name'

cat 'file_name' | while read line; do
  循環體
done

循環控制條件;進入循環以前,先作一次判斷;每一次循環以後會再次作判斷;條件爲「true」 ,則執行一次循環;直到條件測試狀態爲「false」 終止循環

遍歷文件的每一行:依次讀取file_name文件中的每一行,且將行賦值給變量line

① 100之內全部正奇數之和

sum初始值爲0,i的初始值爲1;當i<=100時,進入循環,判斷 i÷2取餘,不爲0時爲奇數,sum=sum+i,i+1;爲0時,i+1;循環結束,最後輸出sum的值。

#!/bin/bash
sum=0
i=1
while [ $i -le 100 ] ;do
if [ $[$i%2] -ne 0 ];then
    let sum+=i
    let i++
else
    let i++
fi
done
echo "sum is $sum"

② 菜單

#!/bin/bash
cat << EOF
             1)gongbaojiding
             2)kaoya
             3)fotiaoqiang
             4)haishen
             5)baoyu
             6)quit
EOF
while read -p "please choose the number: " num;do
case $num in
    1)
     echo "gongbaojiding price is 30"
     ;;
    2)
     echo "kaoya price price is 80"
     ;;
    3)  
     echo "fotiaoqiang price is 200"
     ;;
    4)
     echo "haishen price is \$20"
     ;;
    5)
     echo "baoyu price is \$10"
     ;;
    6)
     break
     ;;
    *)
    echo "please input again"
esac
done

③ 統計日誌訪問IP狀況

#!/bin/bash
# 其中access_log爲訪問日誌,統計訪問IP和次數,導出到文件iplist.txt中
sed -rn 's/^([^[:space:]]+).*/\1/p' access_log |sort |uniq -c > iplist.txt
# while read 逐行處理
while read count ip;do
    if [ $count -gt 100 ];then
        # 將統計後的日誌導出到文件crack.log中
        echo from $ip access $count  >> crack.log
    fi
# while read 支持重定向,能夠將要統計的文件導入到循環中
done < iplist.txt

④ 統計磁盤使用率大於指定值的信息

#!/bin/bash
# 定義報警的磁盤使用率
WARNING=10
df | awk '{if($5>$WARNING)print $0}'
#!/bin/bash
# 定義報警的磁盤使用率
WARNING=10
df |sed -rn  '/^\/dev\/sd/s#^([^[:space:]]+).* ([[:digit:]]+)%.*$#\1 \2#p' | while read part use; do
    if [ $use -gt $WARNING ]; then
        echo $part will be full,use:$use
    fi
done
#!/bin/bash
# 定義報警的磁盤使用率
WARNING=10
df |awk -F"[[:space:]]+|%" '/dev\/sd/{print $1,$(NF-2)}' > disk.txt
while read part use; do
        if  [ $use -gt $WARNING ]; then
            echo $part will be full,use:$use
        fi
done < disk.txt

until

until [[ 循環控制條件 ]]; do
  循環體
done

進入條件:循環條件爲false ;

退出條件:循環條件爲true;

恰好和while相反,因此不經常使用,用while就行。

① 監控test用戶,登陸就殺死

#!/bin/bash
# 發現test用戶登陸,條件爲true,退出循環
until pgrep -u test &> /dev/null ;do
    # 每隔0.5秒掃描
    sleep 0.5
done
# 殺死test用戶相關進程
pkill -9 -u test

select

select variable in list
do
  循環體
done

① select 循環主要用於建立菜單,按數字順序排列的示菜單項將顯示在標準錯誤上,並顯示PS3提示符,等待用戶輸入

② 用戶輸入菜單列表中的某個數字,執行相應的命令

③ 用戶輸入被保存在內置變量 REPLY 中

④ select 是個無限循環,所以要記住用 break 命令退出循環,或用 exit 命令終止腳本。也能夠按 ctrl+c退出循環

⑤ select 常常和 case 聯合使用

⑥ 與for循環相似,能夠省略 in list, 此時使用位置參量

示例: 生成菜單,並顯示選中的價錢

#!/bin/bash
PS3="Please choose the menu: "
select menu in mifan huimian jiaozi babaozhou quit
do
        case $REPLY in
        1|4)
                echo "the price is 15"
                ;;
        2|3)
                echo "the price is 20"
                ;;
        5)
                break
                ;;
        *)
                echo "no the option"
        esac
done

注意:PS3是select的提示符,自動生成菜單,選擇5退出循環。

循環控制語句

continue [N]:提早結束第N層的本輪循環,而直接進入下一輪判斷;最內層爲第1層

break [N]:提早結束第N層循環,最內側爲第1層

例:

while CONDTITON1; do
  CMD1
if CONDITION2; then
  continue / break
fi
  CMD2
done

(2)案例:

① 求(1+3+...+49+53+...+100)的和

#!/bin/bash
sum=0
for i in {1..100} ;do
        [ $i -eq 51 ] && continue
        [ $[$i%2] -eq 1 ] && { let sum+=i;let i++; }
done
echo sum=$sum

分析:作1+3+...+99的循環,當i=51時,跳過此次循環,可是繼續整個循環,結果爲:sum=2449

② 求(1+3+...+49)的和

#!/bin/bash
sum=0
for i in {1..100} ;do
        [ $i -eq 51 ] && break
        [ $[$i%2] -eq 1 ] && { let sum+=i;let i++; }
done
echo sum=$sum

分析:作1+3+...+99的循環,當i=51時,跳出整個循環,結果爲:sum=625

shift

shift n    用於將參數列表list左移指定次數,最左端的那個參數就從列表中刪除,其後邊的參數繼續進入循環,n是整數

1.依次讀取輸入的參數並打印參數個數:
$ cat run.sh
#!/bin/bash
while [ $# != 0 ];do
echo "第一個參數爲:$1,參數個數爲:$#"
shift
done 
$ sh run.sh a b c d e f 
第一個參數爲:a,參數個數爲:6
第一個參數爲:b,參數個數爲:5
第一個參數爲:c,參數個數爲:4
第一個參數爲:d,參數個數爲:3
第一個參數爲:e,參數個數爲:2
第一個參數爲:f,參數個數爲:1 

2.把參數進行左移3個:
$ cat t.sh
#!/bin/bash
echo -e "./t.sh arg1 arg2 arg3 arg4 arg5 arg6"
str1="${1},${2},${3}"
echo "str1=$str1"
shift 3
str2=$@
echo "str2=$str2"
$ sh t.sh 1 2 3 4 5 6 7
str1=1,2,3

3.將參數從左到右逐個移動:
$ cat shift.sh
#!/bin/bash
while [ $# -ne 0 ]
do
echo "第一個參數爲: $1 參數個數爲: $#"
shift
done
$ sh shift.sh Lily Lucy Jake Mike
第一個參數爲: Lily 參數個數爲: 4
第一個參數爲: Lucy 參數個數爲: 3
第一個參數爲: Jake 參數個數爲: 2
第一個參數爲: Mike 參數個數爲: 1

① 建立指定的多個用戶

#!/bin/bash
if [ $# -eq 0 ] ;then
        echo "Please input a arg(eg:`basename $0` arg1)"
        exit 1
else
        while [ -n "$1" ];do
                useradd $1 &> /dev/null
                echo "User:$1 is created"
                shift
        done
fi

分析:若是沒有輸入參數(參數的總數爲0),提示錯誤並退出;反之,進入循環;若第一個參數不爲空字符,則建立以第一個參數爲名的用戶,並移除第一個參數,將緊跟的參數左移做爲第一個參數,直到沒有第一個參數,退出。

② 打印直角三角形的字符

 #!/bin/bash
 while (( $# > 0 ))
 do
     echo "$*"
    shift
 done
 [root@oldboyedu-lnb ~]# sh trian.sh 1 2 3
 1 2 3
 2 3
 3

Boolean

true

永遠成功

false

永遠錯誤


無限循環

while true ;do
  循環體
done
# 或者
until false ;do
  循環體
done

並行循環

每次循環將循環體放入後臺執行. 繼續下一次循環. 最後等待全部線程執行完畢再退出腳本

for name in 列表 ;do
  {
  循環體
  }&
done
wait

① 搜尋指定ip(子網掩碼爲24的)的網段中,UP的ip地址

read -p "Please input network (eg:192.168.0.0): " net
echo $net |egrep -o "\<(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>"
[ $? -eq 0 ] || ( echo "input error";exit 10 )
IP=`echo $net |egrep -o "^([0-9]{1,3}\.){3}"`
for i in {1..254};do
        {
        ping -c 1 -w 1 $IP$i &> /dev/null && \
        echo "$IP$i is up"
        }&
done
wait

分析:請輸入一個IP地址,例如192.168.37.234,若是格式不是0.0.0.0 則報錯退出;正確則進入循環,IP變量的值爲192.168.37. i的範圍爲1-254,並行ping 192.168.37.1-154,ping通就輸出此IP爲UP。

函數遞歸示例

函數遞歸: 函數直接或間接調用自身 注意遞歸層數 遞歸實例: 階乘是基斯頓·卡曼於 1808 年發明的運算符號,是數學術語,一個正整數的階乘(factorial)是全部小於及等於該數的正整數的積,而且有0的階乘爲1,天然數n的階乘寫做n!

n!=1×2×3×...×n
階乘亦能夠遞歸方式定義:0!=1,n!=(n-1)!×n
n!=n(n-1)(n-2)...1
n(n-1)! = n(n-1)(n-2)!

函數遞歸示例
示例:fact.sh
#!/bin/bash

fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}
fact $1

展開命令行

  • 把命令行分紅單個命令詞

  • 展開別名

  • 展開大括號的聲明({})

  • 展開波浪符聲明(~)

  • 命令替換$()和``

  • 再次把命令行分紅命令詞

  • 展開文件通配(*、?、[abc]等等)

  • 準備I/0重導向(<、>)

  • 運行命令

防止擴展

  1. 反斜線\會使隨後的一個字符按原意解釋

  2. 單引號'防止全部擴展

  3. 雙引號"除了如下狀況,防止全部擴展:

  $   (美圓符號)   變量擴展(注意:"$" 輸出 $,仍有特殊含義)
  `   (反引號)      命令替換
  \   (反斜線)     禁止單個字符擴展
  !   (歎號)       歷史命令替換

分支

HEAD_KEYWORD parameters; BODY_BEGIN
  BODY_COMMANDS
BODY_END
  • 將HEAD_KEYWORD和初始化命令或者參數放在第一行;

  • 將BODY_BEGIN一樣放在第一行;

  • 複合命令中的BODY_COMMANDS部分以2個空格縮進;

  • BODY_END部分獨立一行放在最後;

  1. if

if [[ condition ]]; then
  # statements
fi

if [[ condition ]]; then
  # statements
else
  # statements
fi

if [[ condition ]]; then
  # statements
elif [[ condition ]]; then
  # statements
else
  # statements
fi
  • if 後面的判斷 使用 雙中括號[[]]

  • if [[ condition ]]; then 寫在一行

  1. while

while [[ condition ]]; do
  # statements
done

while read -r item ;do
  # statements
done < 'file_name'
  1. until

until [[ condition ]]; do
  # statements
done
  1. for

for (( i = 0; i < 10; i++ )); do
  # statements
done

for item in ${array}; do
  # statements
done
  1. case

case $var in
  pattern )
    #statements
    ;;
    *)
    #statements
    ;;
esac

ShellScript函數

function用法

  1. 函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程。

  2. 它與shell程序形式上是類似的,不一樣的是它不是一個單獨的進程,不能獨立運 行,而是shell程序的一部分,定義函數只對當前的會話窗口有效,若是再打開一個窗口再定義另一個函數,就對另外一個窗口有效,二者互不影響。

  3. 函數和shell程序比較類似,區別在於如下兩種:

(1)Shell程序在子Shell中運行。

(2)而Shell函數在當前Shell中運行。所以在當前Shell中,函數能夠對shell中變量進行修改。

定義函數

function main(){
  #函數執行的操做
  #函數的返回結果
}

main(){
  #函數執行的操做
  #函數的返回結果
}

function main {
  #函數執行的操做
  #函數的返回結果
}
  1. 使用關鍵字 function 顯示定義的函數爲 public 的函數,能夠供外部腳本以 sh 腳本 函數 函數入參 的形式調用

  2. 未使用關鍵字 function 顯示定義的函數爲 privat 的函數, 僅供本腳本內部調用,注意這種privat是人爲規定的,並非shell的語法,不推薦以 sh 腳本 函數 函數入參 的形式調用,注意是不推薦而不是不能。

本shell規約這樣作的目的就在於使腳本具備必定的封裝性,看到 function 修飾的就知道這個函數能被外部調用, 沒有被修飾的函數就僅供內部調用。你就知道若是你修改了改函數的影響範圍. 若是是被function修飾的函數, 修改後可能影響到外部調用他的腳本, 而修改未被function修飾的函數的時候,僅僅影響本文件中其餘函數。

如 core.sh 腳本內容以下是

# 從新設置DNS地址 []<-()
function set_name_server(){
  > /etc/resolv.conf
  echo nameserver 114.114.114.114 >> /etc/resolv.conf
  echo nameserver 8.8.8.8 >> /etc/resolv.conf
  cat /etc/resolv.conf
}
# info級別的日誌 []<-(msg:String)
log_info(){
  echo -e "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: \033[32m [info] \033[0m $*" >&2
}
# error級別的日誌 []<-(msg:String)
log_error(){
  # todo [error]用紅色顯示
  echo -e "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: \033[31m [error] \033[0m $*" >&2
}

則我可使用 sh core.sh set_name_server 的形式調用 set_name_server 函數,但就不推薦使用 sh core.sh log_info "Hello World" 的形式使用 log_infolog_error 函數,注意是不推薦不是不能。


(1)可在交互式環境下定義函數

(2)可將函數放在腳本文件中做爲它的一部分

#!/bin/bash
# 定義function func_os_version,在大括號裏邊定義命令,取出操做系統的版本號,相似於定義別名同樣
func_os_version () {  
sed -nr 's/.* ([0-9]+)\..*/\1/p' /etc/redhat-release   
}
# 直接寫上函數名,或者用echo加反引號輸出結果
echo OS version is `func_os_version`  

若是命令過多,不太方便

(3)可放在只包含函數的單獨文件中

  • 而後將函數文件載入shell

  • 文件名可任意選取,但最好與相關任務有某種聯繫。例如:functions.main

  • 一旦函數文件載入shell,就能夠在命令行或腳本中調用函數。可使用set查看全部定義的函數,其輸出列表包括已經載入shell的全部函數

  • 若要改動函數,首先用unset function_name從shell中刪除函數。改動完畢後,再從新載入此文件

 # 將定義的函數放到functions文件中
 [root@centos-7 ~]# cat functions
 func_os_version () {
 sed -nr 's/.* ([0-9]+)\..*/\1/p' /etc/redhat-release
 }
 
 [root@centos-7 ~]# cat osversion.sh  
 #!/bin/bash
 # 在當前腳本加載 functions
 source functions
 # 調用函數
 func_os_version
 
 # 對腳本osversion.sh加上執行權限
 [root@centos-7 ~]# chmod +x osversion.sh
 # 運行腳本
 [root@centos-7 ~]# ./osversion.sh
 7

可使用declare -F 查看全部定義的函數

定義環境函數

使子進程也可以使用

(1)聲明:export -f function_name

(2)查看:export -fdeclare -xf

調用函數

調用:給定函數名

函數名出現的地方,會被自動替換爲函數代碼

函數的生命週期:被調用時建立,返回時終止


使用腳本單獨調用函數中的某個函數

#!/usr/bin/env bash
# shellcheck disable=SC1091,SC2155
readonly local TRUE=0 && readonly local FALSE=1
# 腳本使用幫助文檔
manual(){
  cat "$0"|grep -v "less \"\$0\"" \
          |grep -B1 "function "   \
          |grep -v "\\--"         \
          |sed "s/function //g"   \
          |sed "s/(){//g"         \
          |sed "s/#//g"           \
          |sed 'N;s/\n/ /'        \
          |column -t              \
          |awk '{print $1,$3,$2}' \
          |column -t
}
######################################################################
# 主函數
main(){
  (manual)
}
######################################################################
# 執行函數 [Any]<-(function_name:String,function_parameters:List<Any>)
execute(){
  function_name=$1
  shift # 參數列表以空格爲分割左移一位,至關於丟棄掉第一個參數
  function_parameters=$*
  (${function_name} "${function_parameters}")
}
case $1 in
  "-h" | "--help" | "?") (manual);;
  "") (main) ;;
  *) (execute "$@") ;;
esac

使用如上的框架,只須要在 兩個 ###################################################################### 之間寫函數,就可使用 sh 腳本名稱 腳本中的某個函數 腳本中的某個函數的入參 的形式調用函數了。 使用 sh 腳本名稱 ? 或者 sh 腳本名稱 -h/--help 就能夠查看這個腳本中的函數說明了。

img

  1. 在函數內部首先使用有意義的變量名接受參數,而後在使用這些變量進行操做,禁止直接操做$1,$2 等,除非這些變量只用一次

  2. 函數的註釋 函數類型的概念是從函數編程語言中的概念偷過來的,shell函數的函數類型指的是函數的輸入到函數的輸入的映射關係

# 主函數 []<-()                  <-------函數註釋這樣寫
function main(){
  local var="Hello World!!!"
  echo ${var}
}
# info級別的日誌 []<-(msg:String)  <-------帶入參的函數註釋
log_info(){
  echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: [info] $*" >&2
}

main函數的函數類型是 []<-() , <- 左側表的是函數的返回值類型用[]包裹, 右側是函數的參數類型用()包裹,多個參數用 ',' 分隔, 參數的描述是從 Scala 語言中偷過來, 先是參數名稱, 而後是參數類型, 中間用:分隔

對於main函數的註釋來講, # 頂格寫,後面緊跟一個空格,其實這樣寫是遵循的markdown的語法, 後面再跟一個空格,而後是 []<-(),表明這個函數沒有入參也沒有返回值,這個函數的目的就是執行這個這個函數中的命令,但我不關心這個函數的返回值。也就是利用函數的反作用來完成咱們想要的操做。

對於log_info也是同樣不過最後的函數類型是 []<-(msg:String) 表明入參是一個string類型的信息,而後也沒有返回值。 關於函數的返回值,我理解的函數的返回值有兩種形式,一種是顯示的return一種是隱式的echo

如下是幾種常見的寫法

[]<-()
[String]<-(var1:String,var2:String)
[Boolean]<-(var1:String,var2:Int)
[]<-(var1:String)

返回值

一、函數的執行結果返回值:

(1) 使用echo等命令進行輸出

(2) 函數體中調用命令的輸出結果

二、函數的退出狀態碼:

(1) 默認取決於函數中執行的最後一條命令的退出狀態碼

(2) 自定義退出狀態碼,其格式爲:

return 從函數中返回,用最後狀態命令決定返回值:

  (1)return 0 無錯誤返回。

  (2)return 1-255 有錯誤返回


執行一條命令的時候, 好比 pwd 正常狀況下它輸出的結果是 當前所處的目錄

$ pwd
/Users/chenshang

shell中必然有一種狀態來標識一條命令是否執行成功,也就是命令執行結果的狀態。

0表明真、成功的含義。

非零表明假、失敗的含義。

因此 pwd 這條命令若是執行成功的話,命令的執行結果狀態必定是0,而後返回值纔是當前目錄。若是這條命令執行失敗的話,命令的執行結果狀態必定不是0,有多是1 表明命令不存在,而後輸出 not found,也有可能執行結果狀態是2表明超時,而後什麼也不輸出。那怎麼獲取這個命令的執行結果和執行結果的狀態呢?

function main(){
  pwd
}

執行main函數就會在控制檯輸出當前目錄 若是想要將pwd的內容獲取到變量中以供後續使用呢

function main(){
  local dir=$(pwd)
  echo "dir is ${dir}"
}

若是想要獲取pwd的執行結果的狀態呢

function main(){
  local dir=$(pwd)
  local status=$?
  echo "pwd run status is ${status}" #這個stauts必定有值,且是int類型,取值範圍在0-255之間
  echo "dir is ${dir}"
}

顯示return

return 用來顯示的返回函數的返回結果,例如

# 檢查當前系統版本 [Integer]<-()
function check_version(){
  (log_info "check_version ...") # log_info是我寫的工具類中的一個函數
  local version # 這裏是先定義變量,在對變量進行賦值,咱們每每是直接初始化,而不是像這樣先定義在賦值,這裏只是告訴你們能夠這麼用
  version=$(sed -r 's/.* ([0-9]+)\..*/\1/' /etc/redhat-release)
  (log_info "centos version is ${version}")
  return "${version}"
}

這樣這個函數的返回值是一個數值類型,我在腳本的任何地方調用check_version這個函數後,使用 $? 獲取返回值

check_version
local version=$?
echo "${version}"

注意這裏不用 local version=$(check_version) 這種形式獲取結果,這樣也是獲取不到結果的,由於顯示的return結果,返回值只能是[0-255]的數值,這對於咱們通常的函數來講就足夠了,由於咱們使用顯示return的時候每每是知道返回結果必定是數字且在[0-255]之間的,經常用在狀態判斷的時候。

本shell規約規定:

  1. 明確返回結果是在[0-255]之間的數值類型的時候使用顯示 reuturn 返回結果

  2. 返回結果類型是Boolean類型,也就是說函數的功能是起判斷做用,返回結果是真或者假的時候使用顯示 return 返回結果

# 檢查網絡 [Boolean]<-()
function check_network(){
  (log_info "check_network ...")
  for((i=1;i<=3;i++));do
    http_code=$(curl -I -m 10 -o /dev/null -s -w %\{http_code\}  www.baidu.com)
    if [[ ${http_code} -eq 200 ]];then
      (log_info "network is ok")
      return ${TRUE}
    fi
  done
  (log_error "network is not ok")
  return ${FALSE}
}
# 獲取數組中指定元素的下標 [int]<-(TABLE:Array,target:String)
function get_index_of(){
  readonly local array=($1)
  local target=$2
  local index=-1 # -1實際上是255
  local size=${#array[@]}
  for ((i=0;i<${size};i++));do
    if [[ ${array[i]} == ${target} ]];then
      return ${i}
    fi
  done
  return ${index}
}

隱式echo

return 用來顯示的返回函數的返回結果,例如

# 將json字符串格式化樹形結構 [String]<-(json_string:String)
function json_format(){
  local json_string=$1
  echo "${json_string}"|jq . #jq是shell中處理json的一個工具
}

函數中全部的echo照理都應該輸出到控制檯上 例如

json_format "{\"1\":\"one\"}"

你會在控制檯上看到以下輸出

{
  "1": "one"
}

可是一旦你用變量接收函數的返回值,這些本該輸出到控制檯的結果就都會存儲到你定義的變量中 例如

json=$(json_format "{\"1\":\"one\"}")
echo "${json}" # 若是沒有這句,上面的語句執行完成後,不會在控制檯有任何的輸出

咱們把 json_format 改造一下

# 將json字符串格式化樹形結構 [String]<-(json_string:String)
function json_format(){
  local json_string=$1
  echo "爲格式化以前:${json_string}" # 其實新添加的只一句只是用來記錄一行日誌的
  echo "${json_string}"|jq . # jq是shell中處理json的一個工具
}

echo "爲格式化以前:${json_string}" 其實新添加的只一句只是用來記錄一行日誌的,可是json=$(json_format "{"1":"one"}") json 也會將這句話做爲返回結果進行接收,但這是我不想要看到的。

子shell能夠捕獲父shell的變量,但不能改變父shell的變量,使用()將代碼塊包裹,包裹的代碼塊將在子shell中運行,子shell至關於獨立的一個環境,不會影響到父shell的結果

因此若是我不想讓 echo "爲格式化以前:${json_string}" 這句話也做爲結果的話,我就只須要用()將代碼塊包裹便可

# 將json字符串格式化樹形結構 [String]<-(json_string:String)
function json_format(){
  local json_string=$1
  (echo "爲格式化以前:${json_string}") # 其實新添加的只一句只是用來記錄一行日誌的
  echo "${json_string}"|jq . # jq是shell中處理json的一個工具
}

示例

① 對不一樣的成績分段進行判斷

[root@oldboyedu-lnb ~]# cat functions
func_is_digit(){
    # 判斷參數$1不是空,就爲真,取反,空爲真
    if [ ! "$1" ];then
        # 請輸入數字
        echo "Usage:func_is_digit number"
        return  10
    # 若是輸入是數字,返回0
    elif [[ $1 =~ ^[[:digit:]]+$ ]];then
        return 0
    else
    # 不然提醒不是數字
        echo "Not a  digit"  
        return 1
    fi
}
 [root@oldboyedu-lnb ~]# cat score.sh
 #!/bin/bash
 source /data/functions
 read -p "Input your score:" SCORE
 func_is_digit $SCORE
 #判斷上面的命令執行結果不等於0就退出
 if [ $? -ne 0 ];then
     exit
 else
     # 若是成功了,對成績的三種判斷以下。
     if [ $SCORE -lt 60 ];then
         echo "You are loser"
     elif [ $SCORE -lt 80 ];then
         echo "soso"
     else
         echo "very good"
     fi
 fi

② function配合case:代碼發佈與回滾與檢驗

 #!/bin/bash
 # Author: liupengju
 # date:   2020-06-22
 # TEL:     xxxxxxxxxx
 # 代碼發佈與回滾
 set -e
 set -u
 
 # adx代碼部署 定義變量
 ADX_DIR=/gnome/adx
 adx_new_version="gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar"
 ADX_NEW_MD5=`md5sum $adx_new_version | awk '{ print $1 }'`
 
 # adx代碼部署 cf平臺的md5碼
 ADX_CHK_MD5="43bcfe7594f083a8653126e0896b93ac"
 
 # directAd代碼部署 定義變量
 direct_DIR=/gnome/directAd/
 direct_version="direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar"
 direct_MD5=`md5sum $direct_version | awk '{ print $1 }'`
 #direct_old_version=$(ls -l |tail -n1 | awk '{print $9}')
 
 # directAd代碼部署 cf平臺的md5碼
 direct_CHK_MD5="03c3c2fc62b2edfc92e548351010ee9f"
 
 ##########部署directAd代碼#############################
 fun_copy_direct_code(){
     mv $direct_DIR/$direct_version  $direct_DIR/bak/${direct_version}_$(date +"%F-%T")
         echo "-----上一個版本已經移動到備份目錄"
     cp /data/$direct_version  $direct_DIR && echo "-----代碼複製成功!!!"
 }
 
 fun_chk_direct_code(){
       if [[ "$direct_MD5" == "$direct_CHK_MD5" ]];then
            echo "-----代碼校驗成功" && echo "代碼部署成功後MD5值爲:$direct_MD5"
       else
            echo "-----代碼校驗失敗" && exit
       fi
 }
 
 fun_deploy_direct_restart(){
     #$direct_DIR/restart.sh
    systemctl restart httpd
    systemctl restart nginx
     echo "後端服務重啓成功!!!"
 }
 
 # 驗證端口存活狀態
 fun_chk_direct_port1(){
     PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
     PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
      for port in $PORT1 $PORT2;do
         echo "The port is:$port------監聽端口正常"
      done
 }
 
 #############回滾directAd代碼###################################
 fun_rollback_direct_code(){
     cd $direct_DIR/bak
 # 提取上一個版本的jar包
     direct_old_version=$(ls -l |tail -n1 | awk '{print $9}')  
     mv $direct_DIR/${direct_version} $direct_DIR/bak/${direct_version}_$(date +"%F-%T")
     mv $direct_DIR/bak/${direct_old_version} $direct_DIR/${direct_version}
     echo "------舊版本代碼移動成功"
     direct_old_MD5=$(md5sum $direct_DIR/${direct_version} | awk '{print $1}')
     echo "代碼回滾後MD5值爲:$direct_old_MD5"
 }
 
 fun_rollback_direct_restart(){
     #$direct_DIR/restart.sh
    systemctl restart httpd
    systemctl restart nginx
     echo "--------後端服務重啓成功"
 }
 
 # 驗證端口存活狀態
 fun_chk_direct_port2(){
     PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
     PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
     for port in $PORT1 $PORT2;do
         echo "The port is:$port------端口監聽正常"
     done
 }
 
 #####################adx代碼部署########################################
 fun_copy_adx__code(){
     mv $ADX_DIR/$adx_new_version  $ADX_DIR/bak/${adx_new_version}_$(date +"%F-%T")
         echo "-----上一個版本已經移動到備份目錄"
     cp /data/$adx_new_version  $ADX_DIR && echo "-----代碼複製成功!!!"
 }
 
 fun_chk_adx_code(){
     if [[ "$ADX_NEW_MD5" == "$ADX_CHK_MD5" ]];then
         echo "-----代碼校驗成功" && echo "代碼部署成功後MD5值爲:$ADX_NEW_MD5"
     else
             echo "-----代碼校驗失敗" && exit
     fi
 }
 
 fun_deploy_adx_restart(){
     #$ADX_DIR/restart.sh
    systemctl restart httpd
    systemctl restart nginx
     echo "後端服務已經啓動!!!"
 }
 
 # 驗證端口存活狀態
 fun_chk_adx_port1(){
     PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
     PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
       for port in $PORT1 $PORT2;do
           echo "The port is:$port------監聽的端口正常啓動"
       done
 }
 
 ###################################adx代碼回滾###########################
 fun_rollback_adx_code(){
     cd $ADX_DIR/bak
     adx_old_version=$(ls -l |tail -n1 | awk '{print $9}')
     mv $ADX_DIR/${adx_new_version} $ADX_DIR/bak/${adx_new_version}_$(date +"%F-%T")
     mv $ADX_DIR/bak/${adx_old_version} $ADX_DIR/${adx_new_version}
     echo "------舊版本代碼移動成功"
     adx_old_MD5=$(md5sum $ADX_DIR/${adx_new_version} | awk '{print $1}')
     echo "代碼回滾後MD5值爲:$adx_old_MD5"
 }
 
 fun_rollback_adx_restart(){
     #$ADX_DIR/restart.sh
    systemctl restart httpd
    systemctl restart nginx
     echo "--------後端服務已經啓動"
 }
 
 # 驗證端口存活狀態
 fun_chk_adx_port2(){
     PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
     PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
     for port in $PORT1 $PORT2;do
         echo "The port is:$port-------端口監聽正常"
     done
 }
 
 case $1 in
  direct_deploy)
      fun_copy_direct_code
      fun_chk_direct_code
      fun_deploy_direct_restart
      fun_chk_direct_port1
      ;;
  direct_rollback)
      fun_rollback_direct_code
      fun_rollback_direct_restart
      fun_chk_direct_port2
      ;;
  adx_deploy)
      fun_copy_adx__code
      fun_chk_adx_code
      fun_deploy_adx_restart
      fun_chk_adx_port1
      ;;
  adx_rollback)
    fun_rollback_adx_code
    fun_rollback_adx_restart
    fun_chk_adx_port2
      ;;
 esac
#!/bin/bash
# Auth:   liupenghui
# date:   2020-10-22
# TEL:    xxxxx
# 部署完成校驗

####驗證adserver版本號#############
fun_chk_adx_version(){
    ansible adx -m shell -a 'md5sum   /gnome/adx/gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar' |awk '{print $1}'|sort |head -n62 |tee version_adx |cat -n
    adx_version=$(ansible adx -m shell -a 'md5sum /gnome/adx/gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar' |awk '{print $1}'|sort |tail -n62 |uniq -c|awk '{print $2}')
    echo -e "\e[1;32m新發布的版本號爲:$adx_version\e[0m"
    version1=$(diff metadata  version_adx)
        if [ -z $version1 ];then
           echo -e "\e[1;32m代碼部署成功 \e[0m"
    else
           echo -e "\e[1;31m請檢查錯誤  \e[0m"
    fi
}
 
####驗證directAd版本號############
fun_chk_direct_version(){  
        ansible adx -m shell -a ' md5sum   /gnome/directAd/direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar'|awk '{print $1}'|sort |head -n62 |tee version_direct |cat -n
        direct_version=$(ansible adx -m shell -a 'md5sum /gnome/directAd/direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar'|awk '{print $1}'|sort |tail -n62 |uniq -c|awk '{print $2}')
        echo -e "\e[1;32m新發布的版本號爲:$direct_version\e[0m"
        version2=$(diff metadata  version_direct)
        if [ -z $version2 ];then
           echo -e "\e[1;32m代碼部署成功 \e[0m"
        else
           echo -e "\e[1;31m請檢查錯誤  \e[0m"
        fi
}
 
###驗證8080端口狀態###############
fun_chk_8080_port(){
    chk_ip_8080=$(ansible adx -m shell -a ' netstat -ntulp |grep 8080' |awk '{print $1}' |egrep "[0-9]+\.*" |sort | tee data_8080.bak |cat -n)
    DIR_8080=$(diff metadata  data_8080.bak)
    if [ -z $DIR_8080 ];then
           echo -e "\e[1;32m端口檢查成功,端口號:8080 \e[0m"
    else
           echo -e "\e[1;31m請檢查錯誤  \e[0m"
    fi
}
 
####驗證8182端口狀態#############
fun_chk_8182_port(){
    chk_ip_8182=$(ansible adx -m shell -a ' netstat -ntulp |grep 8182' |awk '{print $1}' |egrep "[0-9]+\.*" |sort |tee data_8182.bak |cat -n)
    DIR_8182=$(diff metadata  data_8182.bak)
    if [ -z $DIR_8182 ];then
        echo -e "\e[1;32m端口檢查成功,端口號:8182 \e[0m"
    else
        echo -e "\e[1;31m請檢查錯誤  \e[0m"
    fi
}
 
case $1 in
     adx)
       fun_chk_adx_version
       fun_chk_8080_port
       fun_chk_8182_port
       ;;
     direct)
       fun_chk_direct_version
       fun_chk_8080_port
       fun_chk_8182_port
       ;;
esac

環境變量

bash配置文件

  • 按生效範圍劃分,存在兩類:

全局配置:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
我的配置:
~/.bash_profile
~/.bashrc
交互式登陸:
(1)直接經過終端輸入帳號密碼登陸
(2)使用「su - UserName」 切換的用戶
執行順序:/etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile --> ~/.bashrc --> /etc/bashrc
非交互式登陸:
(1)su UserName
(2)圖形界面下打開的終端
(3)執行腳本
(4)任何其它的bash實例
執行順序: /etc/profile.d/*.sh --> /etc/bashrc --> ~/.bashrc

按功能劃分,存在兩類:

profile類:
爲交互式登陸的shell提供配置
全局:/etc/profile, /etc/profile.d/*.sh
我的:~/.bash_profile
功用:
(1) 用於定義環境變量
(2) 運行命令或腳本
bashrc類:
爲非交互式和交互式登陸的shell提供配置
全局:/etc/bashrc
我的:~/.bashrc
功用:
(1) 定義命令別名和函數
(2) 定義本地變量

注意:

(1)命令中定義的特性,譬如變量和別名,僅對當前shell進程有效

(2)配置文件中定義的特性,只對隨後新啓動的shell進程有效,想讓經過配置文件定義的特性當即生效,能夠在命令行重複定義一次,或者讓shell進程重讀配置文件

  • source /PATH/FROM/CONF_FILE

  • . /PATH/FROM/CONF_FILE

  • 用戶退出任務~/.bash_logout

    在退出登陸shell時運行。

    用於

    • 建立自動備份

    • 清除臨時文件

示例:當退出登陸時,刪除文件/data/test

[root@centos7 ~]# vim ~/.bash_logout
# ~/.bash_logout
rm -rf /data/test

環境變量配置文件

系統開機啓動,環境變量配置文件的執行順序爲: /etc/profile -> (~/.bash_profile | ~/.bash_login | ~/.profile) -> ~/.bashrc -> /etc/bashrc -> ~/.bash_logout

 (1)/etc/profile:
 此文件爲系統的每一個用戶設置環境信息,當用戶第一次登陸時,該文件被執行.
 並從/etc/profile.d目錄的配置文件中搜集shell的設置(*.sh文件)。
 (2)~/.bash_profile:
 每一個用戶均可使用該文件輸入專用於本身使用的shell信息,當用戶登陸時,該文件僅僅執行一次!
 默認狀況下,他設置一些環境變量,執行用戶的.bashrc文件。
 (3)~/.bashrc:
 該文件包含專用於你的bash shell的bash信息,當登陸時以及每次打開新的shell時,
 該文件被讀取,調用/etc/bashrc。
 (4)/etc/bashrc:
 爲每個運行bash shell的用戶執行此文件.當bash shell被打開時,該文件被讀取。
 (5)~/.bash_logout:
 當每次退出系統(退出bash shell)時,執行該文件。可用於 建立自動備份 和 清除臨時文件
 注意:/etc/profile 中設定的變量(全局)的能夠做用於任何用戶,
      而~/.bashrc 等中設定的變量(局部)只能繼承/etc/profile中的變量,他們是"父子"關係.
  ~/.bash_profile 是交互式 login 方式進入 bash 時運行的;
      ~/.bashrc 是交互式 non-login 方式進入 bash 時運行的;
  一般兩者設置大體相同,一般前者會調用後者,以便統一配置用戶環境。

其餘配置文件

~/.bash_history 
是bash shell的歷史記錄文件,裏面記錄了你在bash shell中輸入的全部命令。
環境變量:
HISSIZE  設置在歷史記錄文件裏保存記錄的條數。
/etc/enviroment 是系統的環境變量,與登陸用戶無關,例如登陸提示語言,不必修改
/etc/profile    是全部用戶的環境變量,與登陸用戶有關,變量衝突以用戶的環境變量爲準
系統應用程序的執行與用戶環境能夠是無關的,但與系統環境是相關的.

系統開機時,讀取的順序是:?
/etc/enviroment -> /etc/profile
登錄系統時,用戶shell創建環境,讀取的順序是:
/etc/profile -> /etc/enviroment 

永久修改環境變量(編輯環境變量配置文件)

示例: 在文件末尾添加環境變量: export PATH=$PATH:/opt/lamp/mysql/bin export CLASSPATH=./JAVA_HOME/lib;$JAVA_HOME/jre/lib

使環境變量生效

source /etc/profile   通知bash進程從新讀取配置文件
. /etc/profile        執行配置文件
註銷系統

(同名的環境變量,後寫入的起做用)

查看環境變量

set                  顯示當前shell的變量,包括當前用戶的變量(內部命令)
export               顯示當前導出成用戶變量的shell變量(內部命令)
env                  顯示當前用戶的環境變量(外部命令)
declare -x
printenv

echo $PATH  顯示當前導出成用戶變量shell的變量,包括當前用戶的變量中的PATH

臨時設置或顯示環境變量

注意:使用export或declare設置的變量都是臨時變量,也就是說退出當前的shell,爲該變量定義的值便不會生效

export [-fnp] [NAME]=[變量設置值]    當前導出成用戶變量的shell變量
       -f                 表明[NAME]中爲函數名稱。
       -n                 刪除指定的變量。變量實際上並未刪除,只是不會輸出到後續指令的執行環境中。
       -p                 列出全部的shell賦予程序的環境變量。
Examples:
export MYENV=7             # 定義環境變量並賦值
export LANG=zh_CN.UTF-8    # 臨時修改語言環境字符集中文
declare [-fFirx] [-p] [name[=value]]    顯示全部shell變量(與 typeset 相同)
	    +/-       "-"可用來指定變量的屬性,"+"則是取消變量所設的屬性;
		  -p      將顯示每一個[name]的屬性和值    
          -f      僅顯示函數;
          -r      將[name]設置爲只讀;
          -x      指定的變量會成爲用戶環境變量,可供shell之外的程序來使用;env |grep [name]
          -i      [value]能夠是數值,字符串或運算式。
		  -F      包含-f,禁止顯示函數定義;只有函數名和屬性會被顯示
Examples:
declare -x MYENV=7                   # 定義環境變量並賦值
declare -i number=$RANDOM*100/32767  # 設置0~100之間的隨機數變量number

顯示和設置shell中的行爲選項:shopt(向下兼容set)

shopt    列出當前shell中只能由shopt設置的選項
         cdspell       自動改正cd命令參數中的小錯誤
         hostcomplete  以@開頭時,按tab鍵可進行主機名的自動完成
         dotgblob      以點開始的文件名被包含在路徑名擴展中
         mailwarn      顯示郵件警告信息
shopt -o 列出可由set命令設置的選項
         emacs         進入emacs編輯模式
         vi            進入vi編輯模式
         ignoreeof     不容許單獨使用Ctrl+D退出的用法,要使用exit。與IGNOREEOF=10等價
         noclobber     不容許重定向覆蓋已存在文件
         noglob        不容許擴展文件名通配符
         nounset       使用未定義的變量時給出錯誤
      -p 顯示可設置選項及當前取值
      -s 設置每一選項爲on
      -u 設置每一選項爲off
      -q 不輸出信息

臨時設置shell變量:set

set               顯示全部shell變量
    -a            標示已修改的變量,導出至環境變量。echo $[name]
    -b            使被停止的後臺程序馬上回報執行狀態。
    -C            重定向所產生的文件沒法覆蓋已存在的文件。
    -d            Shell預設會用雜湊表記憶使用過的指令,以加速指令的執行。使用-d參數可取消。
    -e            若指令傳回值不等於0,則當即退出shell。等同 set –o errexit
    -f            取消使用通配符。
    -h            自動記錄函數命令的所在位置。hash下來
    -H,--history  可利用"!"加<指令編號>的方式來執行history中記錄的指令。
    -k            指令所給的參數都會被視爲此指令的環境變量。
    -l            記錄for循環的變量名稱。
    -m            使用監視模式。能夠經過Job control來控制進程的中止、繼續,後臺或者前臺執行等。
    -n            只讀取指令,而不實際執行。
    -p            啓動優先順序模式。
    -P            啓動-P參數後,執行指令時,會以實際的文件或目錄來取代符號鏈接。
    -t            執行完隨後的指令,即退出shell。
    -u            當執行時使用到未定義過的變量,則顯示錯誤信息。等同 set –o nounset
    -v            顯示shell所讀取的輸入值。
    -x            執行指令後,會先顯示該指令及所下的參數
    -i,--interactive-comments  交互式 shell ,shell 腳本中默認關閉
    -B,braceexpand             大括號擴展命令,默認開啓

刪除shell變量:unset

unset [-f] [-v] [name ...]  刪除shell變量 name
      -f                    僅刪除函數
      -v                    僅刪除變量

經常使用的環境變量

PATH

決定了shell將到哪些目錄中尋找命令或程序,預設可執行文件或命令的搜索路徑。
$PATH=路徑1:路徑2:...:路徑n

RANDOM

0~32767之間的隨機整數

HISTSIZE

設置在歷史記錄文件裏保存記錄的條數。

HISTFILE

指定歷史文件,默認爲~/.bash_history

HISTFILESIZE

命令歷史文件記錄的條數

HISTTIMEFORMAT

HISTTIMEFORMAT=「%F %T 「 顯示時間

HISTIGNORE

HISTIGNORE=「str1:str2*:… 「 忽略str1str2開頭的歷史

HISTCONTROL

HISTCONTROL=            記錄方式
            ignoredups  默認,忽略重複的命令,連續且相同爲「重複」
            ignorespace 忽略全部以空白開頭的命令
            ignoreboth  至關於 ignoredups,ignorespace 的組合
            erasedups   刪除重複命令

CDPATH

cd 相對路徑進入目錄時,尋找匹配目錄的目錄

PWD

當前目錄路徑,自動更新

OLDPWD

上一次目錄路徑,自動更新

LANG

系統語言

LANG="en_US.UTF-8"
LANG="zh_CN.UTF-8"
Cent OS 6.x 配置文件
/etc/sysconfig/i18n
Cent OS 7.x 配置文件:
/etc/locale.conf
localectl set-locale LANG='en_US.UTF-8'

PS1

提示符變量,用於設置提示符格式,設置一級shell提示符環境變量,個性化終端命令行提示符的信息或格式。

PS1 基本提示符 例如:export PS1="[\u@\h \w]\$ "
    \d :表明日期,格式爲weekday month date,例如:Wed Dec 12
    \H :完整的主機名稱。例如:hostname是debian.linux
    \h :僅取主機的第一個名字,如上例,則爲debian,.linux則被省略
    \t :顯示時間爲24小時格式,如:HH:MM:SS
    \T :顯示時間爲12小時格式
    \A :顯示時間爲24小時格式:HH:MM
    \u :當前用戶的帳號名稱 如:root
    \v :BASH的版本信息  如:3.2
    \w :完整的工做目錄名稱。家目錄會以~代替 如顯示/etc/default/
    \W :利用basename取得工做目錄名稱,只會列出最後一個目錄。如上例則只顯示default
    \# :下達的第幾個命令
    \$ :提示字符,若是是root時,提示符爲:# ,普通用戶則爲:$ 

PS2

用於設置二級shell提示符環境變量。

終端命令行的相關設置:stty

stty     顯示終端命令行的相關設置
     -a  打印終端全部當前設置
	    iuclc              禁止輸出大寫,前加-開啓
		olcuc              禁止輸出小寫,前加-開啓
		size               打印出終端的行數和列數
		stty eof "string"  修改Ctrl+D快捷鍵
		echo               打開回顯,前加-關閉
		igncr              忽略回車符,前加-關閉

LOGNAME:當前用戶的登陸名 LANGUGE:語言相關的環境變量,多語言能夠修改此環境變量 MAIL:當前用戶的郵件存放目錄 BASH:記錄當前bash shell的路徑。 BASH_SUBSHELL:記錄當前子shell的層次。BASH_SUBSHELL是從0開始計數的整數。 BASH_VERSINFO:是一個數組包含六個元素,這六個元素顯示bash的版本信息。 BASH_VERSION:顯示shell版本的信息。 DIRSTACK:記錄了棧頂的目錄值,初值爲空。 GLOBLGNORE:是由冒號分割的模式列表,表示通配時忽略的文件名集合。 GROUPS:記錄當前用戶所屬的組。 HOME:記錄當前用戶的家目錄,由/etc/passwd的倒數第二個域決定。 HOSTNAME:記錄主機名。 HOSTTYPE和MACHTYPE:都是記錄系統的硬件架構。 IFS:用於設置指定shell域分隔符,默認狀況下爲空格。 OSTYPE:記錄操做系統類型。 PPID:是建立當前進程的進程號,即當前進程的父進程號 REPLY:REPLY變量與read和select有關。 SECONDS:記錄腳本從開始到結束耗費的時間。 SHELL:顯示當前所用的shell SHELLOPTS:記錄了處於「開」狀態的shell選項列表,它只是一個只讀變量。 SHLVL:記錄了bash嵌套的層次,通常來講,咱們啓動第一個Shell時。$SHLVL=1。若是在這個Shell中執行腳本,腳本中的$SHLVL=2。 TMOUT:用來設置腳本過時的時間,好比TMOUT=3,表示該腳本3秒後過時。 UID: 已登用戶的ID USER:顯示當前用戶名字

用於查詢與修改系統的本地化(locale)與鍵盤佈局的設置:localectl

localectl list-locales      列出全部可用的 locale

設置系統的本地化環境變量(能夠一次設置多個)
例如 "LANG=zh_CN.utf8", "LC_MESSAGES=en_US.utf8" 等等。

localectl set-locale LANG=zh_CN.UTF-8   --- centos7修改字符集信息

自定義環境變量

export PS1='\[\e[36;1m\][\u@\h \W]\$ \[\e[1;37m\]'

腳本示例

bc.sh

四則運算計算器

#!/bin/bash

# 1
if [[ $# == 2 ]];then
  echo $1+$2=$(($1+$2))
  echo $1-$2=$(($1-$2))
  echo $1*$2=$(($1*$2))
  echo $1/$2=$(($1/$2))
  exit
fi
# 2
if [[ $# == 0 ]];then
  read -p "請輸入參數1: " var1
  read -p "請輸入參數2: " var2
  if [[ $var1 == "" || $var2 == "" ]];then
# 3
    var1=10
    var2=10
  fi
  echo $var1+$var2=$(($var1+$var2))
  echo $var1-$var2=$(($var1-$var2))
  echo $var1*$var2=$(($var1*$var2))
  echo $var1/$var2=$(($var1/$var2))
  exit
fi

echo 請輸入2個參數!
rabbit_chook.sh

雞兔同籠腳本:

輸入頭數:35 輸入腳個數:94

輸出x只兔、y只雞

算法:

x+y=35(頭數) 2x+4y=94(腳數)

94/2=47-35=12(兔子個數)35-12=23(雞個數)

let

 #!/bin/bash
 read -p "please input head num: " head
 read -p "please input floot num: " floot
 let x=$floot/2-$head
 let y=$head-$x
 echo "rabbit=$x;chook=$y"

expr

 #!/bin/bash
 read -p "please input head num: " head
 read -p "please input floot num: " floot
 x=`expr $floot / 2 - $head`
 y=`expr $head - $x`
 echo "rabbit=$x;chook=$y"

$[ ]

#!/bin/bash
read -p "please input head num: " head
read -p "please input floot num: " floot
rabbit=$[(floot-2*head)/2]
chook=$[head-rabbit]
echo "rabbit=$rabbit;chook=$chook"

$(())

#!/bin/bash
read -p "please input head num: " head
read -p "please input floot num: " floot
rabbit=$((floot/2-head))
chook=$((head-rabbit))
echo "rabbit=$rabbit;chook=$chook"
9x9.sh
#!/bin/bash
for a in {1..9};do
    for b in `seq 1 $a`;do
    let c=$a*$b ;echo -e "${a}x${b}=$c\t\c"
    done
    echo   
done
[root@oldboyedu-lnb ~]# sh 9x9.sh
1x1=1	
2x1=2	2x2=4	
3x1=3	3x2=6	3x3=9	
4x1=4	4x2=8	4x3=12	4x4=16	
5x1=5	5x2=10	5x3=15	5x4=20	5x5=25	
6x1=6	6x2=12	6x3=18	6x4=24	6x5=30	6x6=36	
7x1=7	7x2=14	7x3=21	7x4=28	7x5=35	7x6=42	7x7=49	
8x1=8	8x2=16	8x3=24	8x4=32	8x5=40	8x6=48	8x7=56	8x8=64	
9x1=9	9x2=18	9x3=27	9x4=36	9x5=45	9x6=54	9x7=63	9x8=72	9x9=81
chess_board.sh
#!/bin/bash
red="\033[1;41m  \033[0m"
yellow="\033[1;43m  \033[0m"
 
for i in {1..8};do
        if [ $[i%2] -eq 0 ];then
                for i in {1..4};do
                        echo -e -n "$red$yellow";
                done
                echo
        else
                for i in {1..4};do
                        echo -e -n "$yellow$red";
                done
                echo
        fi
done
color_isosceles_triangle.sh
#!/bin/bash
read -p "Please input a num: " num
if [[ $num =~ [^0-9] ]];then
        echo "input error"
else
        for i in `seq 1 $num` ;do
                xing=$[2*$i-1]
                for j in `seq 1 $[$num-$i]`;do
                        echo -ne " "
                done
                for k in `seq 1 $xing`;do
                        color=$[$[RANDOM%7]+31]
                        echo -ne "\033[1;${color};5m*\033[0m"
                done
                echo
        done
fi
[root@oldboyedu-lnb ~]# sh color_isosceles_triangle.sh
Please input a num: 5
    *
   ***
  *****
 *******
*********
systeminfo.sh

顯示當前主機系統信息,包括主機名,IPv4地址,操做系統版本,內核版本,CPU型號,內存大小,硬盤大小

#! /usr/bin/sh
hn=`uname -nr | awk '{print $1}'`
ipv4=`ip route | grep -o 'src.*$' | awk '{print $2}'`
os=`cat /etc/redhat-release`
ke=`uname -nr | awk '{print $2}'`
cpu=`lscpu | grep 'Ven.*' | awk '{print $3}'`
me=`free -h | tail -1 | awk '{print $2}'`
sda=`fdisk -l | head -2 | awk '{print $3}' | tail -1`
echo "hostname:$hn"
echo "IPv4:$ipv4"
echo "OS version:$os"
echo "Kernel version:$ke"
echo "CPU model:$cpu"
echo "SWAP size:$me"
echo "Disk size:$sda G"
sysinfo.sh

帶顏色顯示當前主機系統信息,包括系統版本,內核版本,硬盤使用率,主機名

#!/usr/bin/env bash
# ------------------------------------------
# Filename: hello.sh
# Revision: 1.0
# Date: 2020/10/21
# Author: wu
# Email: wu@gmail.com
# Description: This is the first script
# Copyright (C): 2020 All rights reserved
# License: GPL
# ------------------------------------------
# RED is content color
# REDD is content color
RED="\033[1;31m"                                                     
REDD="\033[0m"
echo -e OS Version is $RED`cat /etc/centos-release`$REDD
echo -e Disk used is $RED`df | grep /dev/sd | tr -s " " |cut -d" " -f5 | sort -nr | head -n1`$REDD
echo -e Kernel is $RED`uname -r`$REDD
echo -e "Host name  is  $RED`hostname`"$REDD
create_sh.sh

編寫生成腳本基本格式的腳本,包括做者,聯繫方式,版本,時間,描述等

#!/usr/bin/env bash
# ------------------------------------------
# Filename: hello.sh
# Revision: 1.0
# Date: 2020/10/21
# Author: wu
# Email: wu@gmail.com
# Description: create script and start
# Copyright (C): 2020 All rights reserved
# License: GPL
# ------------------------------------------
if [ $# -lt 1 ]; then
     echo '至少應該給一個參數!'
     exit 1
fi
cat > $1 <<EOF
#!/usr/bin/env bash
# ------------------------------------------
# Filename: $1
# Revision: 1.0
# Date: `date +%F`
# Author: wu
# Email: wu@gmail.com
# Description: This is the $1 script
# Copyright (C): 2020 All rights reserved
# License: GPL
# ------------------------------------------

EOF
chmod +x $1
vim + $1
backup.sh

每日將/etc/目錄備份到/backup/etcYYYYmm-dd中

#! /usr/bin/sh
if [[ ! -d '/backup' ]]; then
    mkdir /backup
fi
cp -af /etc/ /backup/etc$(date -d 'today' +'%Y%m-%d')
echo "/etc/已備份"
chmod u+x backup.sh
echo '00 0 9 * * * root /root/backup.sh #天天早上九點執行backup.sh該文件' >> /etc/crontab
user_no_nologin.sh

統計出/etc/passwd文件中,默認shell爲非/sbin/nologin的用戶個數,並將用戶都顯示出來

#! /usr/bin/sh
getent passwd | grep -v /sbin/nologin
user_max_uid.sh

查出用戶UID最大值的用戶名、UID及shell類型

#! /usr/bin/sh
getent passwd | sort -t: -k3 -nr | head -n1
memory

顯示佔用系統內存最多的進程

#! /usr/bin/sh
ps aux | sort -k4 -nr | head -n1
disk.sh

顯示當前硬盤分區中空間利用率最大的值

#! /usr/bin/sh
echo `df|egrep -o '[0-9]{1,3}%'|sort -nr|head -1`
links.sh

顯示正鏈接本主機的每一個遠程主機的IPv4地址和鏈接數,並按鏈接數從大到小排序

#! /usr/bin/sh
echo `netstat -nt | awk '{print $4}' | egrep '[0-9.]' | cut -d: -f1 | sort | uniq -c | sort -nr`
sumid.sh

計算/etc/passwd文件中的第10個用戶和第20用戶的UID之和

#! /usr/bin/sh
echo `cut -d: -f3 /etc/passwd | sed -n '10p;20p' | awk '{sum += $1};END {print sum}'`
sumspace.sh

傳遞兩個文件路徑做爲參數給腳本,計算這兩個文件中全部空白行之和

#! /usr/bin/sh
echo `cat $1 $2 | sed -n '/^$/p' | wc -l`
sumfile.sh

統計/etc, /var, /usr 目錄中共有多少個一級子目錄和文件

#! /usr/bin/sh
# c is Number of parameters
c=3
echo "`ll /etc /var /usr | wc -l` -($c*2+($c-1))" | bc
argsnum.sh

接受一個文件路徑做爲參數;若是參數個數小於1,則提示用戶「至少應該給一個參數」,並當即退出;若是參數個數不小於1,則顯示第一個參數所指向的文件中的空白行數

#! /usr/bin/sh
if [ $# -lt 1 ]; then
     echo '至少應該給一個參數!'
     exit 1
fi
cat $1 | sed -n '/^$/p' | wc -l
hostping.sh

接受一個主機的IPv4地址作爲參數,測試是否可連通。若是能ping通,則提示用戶「該IP地址可訪問」;若是不可ping通,則提示用戶「該IP地址不可訪問」

#! /usr/bin/sh
if [ $# -lt 1 ]; then
     echo '至少應該給一個參數!'
     exit 1
fi
if ping -c1 -W2 $1; then
     echo '該IP地址可訪問'
else
     echo '該IP地址不可訪問'
fi
#! /usr/bin/sh
ping -c1 -W2 $1
[ $? -eq 0 ] && echo '該IP地址可訪問' || echo '該IP地址不可訪問'
hostsping.sh

使用for和while分別實現:

測試192.168.0.0/24網段內,地址是否可以ping通,若ping通則輸出"success!",若ping不通則輸出"fail!"

for循環實現

#!/bin/bash
NUM=`seq 1 254`

for IP in ${NUM};do
   HOST_IP="192.168.0.${IP}"
   ping -c 2 -w 3 ${HOST_IP} &> /dev/null
   if [ $? -eq 0 ];then
      echo "success!" && echo ${HOST_IP} >> /tmp/ip_success.txt
   else
      echo "fail!"  && echo ${HOST_IP} >> /tmp/ip_fail.txt
   fi
done

while循環實現

#!/bin/bash
IP=192.168.0
NUM=1

while [ $NUM -lt 255 ];do
  ping -c2 -W2 $IP.$NUM &> /dev/null
  if [ $? -eq 0 ];then
     echo "$IP.$NUM success!"
  else
     echo "$IP.$NUM fail!"
  fi
  let NUM++
done
checkdisk.sh

工做日,每10分鐘執行一次,檢查磁盤分區空間和inode使用率,若是超過80%,就發廣播警告空間將滿

#! /usr/bin/sh
sd=`df | egrep -o '[0-9]{1,3}%' | sort -nr | head -1 | egrep -o '[0-9]{1,3}'`
inode=`df -ih | egrep -o '[0-9]{1,3}%' | sort -nr | head -1 | egrep -o '[0-9]{1,3}'`
if [ $sd -gt 80 -o $inode -gt 80 ]; then
     wall 'Disk space will be full!'
else
     echo '磁盤分區空間和inode使用率正常'
fi
echo "*/10 * * * 1-5 /usr/bin/bash /server/script/checkdisk.sh" >> /var/spool/cron/root

每10分鐘執行一次,檢查磁盤分區空間、CPU和內存使用率,若是超過80%,就發廣播警告空間將滿

#! /usr/bin/sh
sd=`df | egrep -o '[0-9]{1,3}%' | sort -nr | head -1`
cpu=`cat /proc/stat|grep '^cpu[0-9]'|awk '{used+=$2+$3+$4;tolused+=$2+$3+$4+$5+$6+$7+$8} END{printf ("%.0f",used/tolused*100)}'`
mem=`free -t | tail -1 | awk '{printf ("%.0f",$3/$2*100)}'`

if [ ${sd%\%} -gt 80 ]; then
    wall 'Disk space will be full!'
elif [ $inode -gt 80 ]; then
    wall 'CPU space will be full!'
elif [ $mem -gt 80 ]; then
    wall 'Mem space will be full!'
fi
echo "*/10 * * * * /usr/bin/bash /server/script/checkdisk.sh" >> /var/spool/cron/root
checkip.sh

編寫腳本/server/script/checkip.sh,每5分鐘檢查一次,若是發現經過ssh登陸失敗次數超過10次,自動將此遠程IP放入Tcp Wrapper的黑名單中予以禁止防問

寫法一:

#!/bin/bash

while true;do
  awk '/Failed/{ip[$(NF-3)]++}
  END {
        for(i in ip)
        {
          if(ip[i]>=2)
          {
            system("echo sshd:"i" >> /etc/hosts.deny")
          }
        }
      }' /var/log/ssh.log
  sleep 5m
done

寫法二:

#!/bin/bash

while true;do
# 收集IP出現失敗的次數
  ipnum=`awk '/Failed/{print $(NF-3)}'  /var/log/ssh.log  |uniq -c |awk '{print $1}'` 
# 統計IP地址
  ip=`awk '/Failed/{print $(NF-3)}'  /var/log/ssh.log  |uniq -c |awk '{print $2}'`
# 當IP地址出現次數大於等於3,將IP地址拉入黑名單。
  if [ $ipnum -ge 3 ] ;then
     echo "sshd:$ip" >> /etc/hosts.deny
  fi
  sleep 5m
done

執行方式:sleep 5分鐘或者加入計劃任務,每5分鐘執行一次

$ crontab -l
*/5 * * * *  /server/script/checkip.sh
checkDOS.sh

解決DOS攻擊生產案例: 根據web日誌或者或者網絡鏈接數,監控當某個IP併發鏈接數或者短時內PV達到100,調用防火牆命令封掉對應的IP,監控頻率每隔5分鐘。防火牆命令爲: iptables -A INPUT -s IP -j REJECT

#!/bin/bash
# environment variable 
source /etc/profile 
iplist=$(ss -tan | awk -F "[[:space:]]+|:" '/ESTAB/{ip[$(NF-2)]++}END{
   for(i in ip)
   {print i,ip[i]}
  }' | awk '{if($2>100)print $1}')
for ip in $iplist
do
  iptables -I INPUT -s $ip -j DROP
  echo "$ip is drop!"
done

加入計劃任務,每5分鐘執行一次

$ crontab -l
*/5 * * * *  /server/script/checkDOS.sh
per.sh

判斷當前用戶對指定參數文件,是否不可讀而且不可寫

#! /usr/bin/sh
if [ -r $1 -a -w $1 ]; then
    echo "$1並不是不可讀而且不可寫"
else
    echo "$1不可讀而且不可寫"
fi
excute.sh

判斷參數文件是否爲sh後綴的普通文件,若是是,添加全部人可執行權限,不然提示用戶非腳本文件

#! /usr/bin/sh
if [[ -f $1 && `echo "$1" | cut -d. -f2` = sh ]]; then
    chmod a+x $1
else
    echo "$1非腳本文件"
fi
nologin.sh

禁止普通用戶登陸系統

#! /usr/bin/sh
echo "System maintenance, no login." > /etc/nologin
login.sh

容許普通用戶登陸系統

#! /usr/bin/sh
\mv /etc/nologin /root/nologin
createuser.sh

使用一個用戶名作爲參數,若是指定參數的用戶存在,就顯示其存在,不然添加之;顯示添加的用戶的id號等信息

#! /usr/bin/sh
if id $1 &> /dev/null; then
    id $1
else
    useradd $1
    id $1
fi

create_user_home.sh

接受二個位置參數,magedu和/www,判斷系統是否有magedu,若是沒有則自動建立magedu用戶,並自動設置家目錄爲/www

#!/bin/bash
id $1 &> /dev/null
if [ $? -eq 0 ]; then
    id $1
else
    useradd -d $2 $1
    id $1
fi
yesorno.sh

提示用戶輸入yes或no,並判斷用戶輸入的是yes仍是no,或是其它信息

#! /usr/bin/sh
read -p "Please input:yes/no: " name
if [ $name = yes ]; then
    echo 'You input: yes'
elif [ $name = no ]; then
    echo 'You input: no'
else
    echo 'You input: other info'
fi
filetype.sh

判斷用戶輸入文件路徑,顯示其文件類型(普通,目錄,連接,其它文件類型)

#! /usr/bin/sh
if [ $# -lt 1 ]; then
    echo '至少應該給一個參數!'
    exit 1
elif [ -L $1 ]; then
    echo '$1 is a Symbolic link'
elif [ -d $1 ]; then
    echo '$1 is a directory'
elif [ -f $1 ]; then
    echo '$1 is a normal file'
else
    echo '$1 is a other file type'
fi
checkint.sh

判斷用戶輸入的參數是否爲正整數

#! /usr/bin/sh
if [ $# -lt 1 ]; then
    echo '至少應該給一個參數!'
    exit 1
fi
if expr $1 + 1 &> /dev/null; then
    if [ $1 -ge 0 ]; then
    	echo "$1是正整數"
    fi
else
    echo "$1不是正整數"
fi
#! /bin/bash
if [ $# -gt 1  ];then
  echo "只能判斷一個參數!"
  exit
fi
if [[ -z $1 ]];then
  echo "輸入爲空"
  exit
fi
if [[ $1 =~ ^[0-9-]?[0-9]+$ ]];then
  echo "輸入爲整數"
  exit
fi
echo "輸入不是整數"
pathadd.sh

讓全部用戶的PATH環境變量的值多出一個路徑,例如:/usr/local/apache/bin

#! /usr/bin/sh
echo "export PATH=$PATH:/usr/local/apache/bin" >> /etc/profile
source /etc/profile
rootlogin.sh

用戶 root 登陸時,將命令指示符變成紅色,並自動啓用以下別名: rm='rm –i' cdnet='cd /etc/sysconfig/network-scripts/' editnet='vim /etc/sysconfig/network-scripts/ifcfg-eth0' editnet='vim /etc/sysconfig/network-scripts/ifcfg-eno16777736 或 ifcfg-ens33' (若是系統是CentOS7)

#! /usr/bin/sh
cat <<EOF>> /etc/profile
export PS1="\e[31m[\u@\h \W]\$ \e[0m"
alias rm='rm –i'
alias cdnet='cd /etc/sysconfig/network-scripts/'
alias editnet='vim /etc/sysconfig/network-scripts/ifcfg-eth0'
EOF
loginissue.sh

任意用戶登陸系統時,顯示紅色字體的警示提醒信息「Hi,dangerous!」

#! /usr/bin/sh
echo 'echo -e "\e[1;31mHi,dangerous!\e[0m"' >> /etc/profile
reset.sh

用戶的環境初始化:包括別名,登陸提示符,vim的設置,環境變量等

#! /usr/bin/sh
selinux.sh

開啓或禁用SELinux

#!/bin/bash

if [[ $1 =~ ^[Oo][Nn]$ ]]; then
  sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config &>/dev/null
  setenforce 1 &>  /dev/null 
elif [[ $1 =~ ^[Oo][Ff][Ff]$ ]]; then
  sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config &>/dev/null 
  setenforce 0 &>/dev/null
else
  echo "you are error,please input on or off !!!"
fi
checkganglia.sh

當客戶機數量不爲10的時候,進行報錯;當客戶機ganglia服務沒有啓動時,進行報錯,而且篩選出全部沒有啓動ganglia的客戶機

#!/bin/bash
DATE=`date +%Y%m%d`
filename="ganglia-${DATE}.log"
prefix="ganglia-${DATE}"
hosts=`grep test@ /tmp/log/ganglia/${filename} | wc -l`
pids=`grep gmond.pid /tmp/log/ganglia/${filename} | wc -l`
if [ ${hosts} != 10 ]
then
    echo "Some hosts are offline!" >> /tmp/log/ganglia/error-${DATE}.log
fi

if [ ${hosts} != ${pids} ]
then
    echo "Some ganglia services have stopped!" >> /tmp/log/ganglia/error-${DATE}.log
    cd /tmp/log/ganglia/
    csplit /tmp/log/ganglia/${filename} /test@/ -n2 -s {*} -f ${prefix} -b ".log.%02d"
    rm ${prefix}.log.00
    for file in /tmp/log/ganglia/${prefix}.log.*
    do 
    if [ -f "${file}" ]
    then
        #echo "${file} is file"
        if [ `grep gmond.pid ${file} | wc -l` == 0 ]
        then
            echo `grep test@ ${file}` >> /tmp/log/ganglia/error-${DATE}.log
        fi
    fi
    done
fi
[root@linuxcool ~]# crontab -l 
# 天天查看一下ganglia的狀態,並保存到/tmp/log/ganglia目錄
0 0 * * * /usr/bin/parallel-ssh -h /home/test/hosts.txt -t 30 -i 'ps aux | grep gmond' > /tmp/log/ganglia/ganglia-`date +\%Y\%m\%d`.log
# 自動篩選報錯
10 0 * * * /bin/bash /home/test/checkganglia.sh

返回頁首

Linux基礎

CentOS7

tr命令

tr [OPTION]... SET1 [SET2] 轉換和刪除字符
   -c –C --complement      取字符集的補集
   -d --delete             刪除SET1的字符
   -s --squeeze-repeats    把連續重複的字符以單獨一個字符表示
   -t --truncate-set1      將SET1轉化爲SET2
tr -d a < filename         刪除文件filename標準輸出中的a
tr -s '\n' < filename      刪除文件filename標準輸出中的空行
cat filename |tr a-z A-Z       將文件filename中標準輸出的小寫字母所有轉換成大寫字母
or
cat filename |tr [:lower:] [:upper:] 	

tr -cd '0-9a-zA-Z' < /dev/urandom | head -c8  # 生成8位隨機數

收集文本統計數據wc

計數單詞總數、行總數、字節總數和字符總數
能夠對文件或STDIN中的數據運行
wc story.txt
39 237 1901 story.txt
行數 字數 字節數
經常使用選項
-l 只計數行數
-w 只計數單詞總數
-c 只計數字節總數
-m 只計數字符總數
-L 顯示文件中最長行的長度

uniq

uniq命令:從輸入中刪除先後相接的重複的行
uniq [OPTION]... [FILE]...
-c: 顯示每行重複出現的次數
-d: 僅顯示重複過的行
-u: 僅顯示未曾重複的行
注:連續且徹底相同方爲重複
常和sort 命令一塊兒配合使用:
sort userlist.txt | uniq -c

文本排序sort

把整理過的文本顯示在STDOUT,不改變原始文件
sort [options] file(s)
經常使用選項
-r 執行反方向(由上至下)整理
-R 隨機排序
-n 執行按數字大小整理
-f 選項忽略(fold)字符串中的字符大小寫
-u 選項(獨特,unique)刪除輸出中的重複行
-t c 選項使用c作爲字段界定符
-k X 選項按照使用c字符分隔的X列來整理可以使用屢次

bash如何展開命令行  

  • 把命令行分紅單個命令詞

  • 展開別名

  • 展開大括號的聲明({})

  • 展開波浪符聲明(~)

  • 命令替換$()和``)

  • 再次把命令行分紅命令詞

  • 展開文件通配(*、?、[abc]等等)

  • 準備I/0重導向(<、>)

  • 運行命令

防止擴展 

反斜線\會使隨後的字符按原意解釋

$echoYourcost:\$5.00
Yourcost:$5.00

加引號來防止擴展 •單引號'防止全部擴展 •雙引號"也防止全部擴展,可是如下狀況例外:

$(美圓符號)-變量擴展
(反引號)-命令替換
\(反斜線)-禁止單個字符擴展,轉義單個字符
!(歎號)-歷史命令替換

1.簡單的介紹shell 1) 爲何要使用shell shell的做用 a. 安裝操做系統 手工方式安裝

   自動化安裝操做系統 
	     kickstart  底層shell腳本
		   cobbler    底層shell腳本

b. 初始化操做系統

	   SSH優化      關閉SElinux 防火牆放行須要的端口(80 443 22修改 10050) YUM源 時間同步 系統最大描述符 
	   內核參數優化 字符集優化 禁止開機自動啓動 修改主機名稱 (修改公司網卡名稱)...
	   手動  命令行安全
	   寫入shell腳本(經常使用)
  c. 安裝服務 Nginx PHP MySQL Rsync等等...  針對不一樣的版本寫入shell腳本自動安裝
	d. 配置服務
	e. 啓動服務 全部的服務底層的啓動方式都是使用的shell腳本

公司本身研發的程序

 nohup python3.5 test.py --redis --port --mysql --port -a xxxx &

複製一下 寫入腳本 sh start_test_py.sh 如何中止py程序

  ps axu|grep test.py |grep -v grep|awk '{print $2}'|xargs kill -9

複製一下 寫入腳本 sh stop_test_py.sh 把py的進程的端口和PID取出來 來判斷是否運行

	f. 日誌統計 查看程序運行的狀況 統計咱們須要的數據
	   日誌切割 定時任務+腳本 
	   統計數據 定時任務+腳本  ---> 經過郵件發送給管理員
	   ELK 日誌統計界面  py開發日誌界面 py界面----> 數據庫 <----數據   日誌展現
g. 監控 監控服務 服務端口是否存在 服務是否存在 服務器的硬件資源使用狀況 狀態 日誌 網絡
   Zabbix	經過腳本統計---> 測試---> 添加到zabbix服務 (cacti監控流量 Nagios寬帶運營商 IT公司)  
減小重複性的工做

2) 學習shell編程所用到的知識 必須熟練掌握

   a. vim編輯器 課下快捷鍵
   b. xshell crt
   c. shell基礎命令60個左右  回顧基礎命令
   d. 三劍客命令 grep sed awk

3) 如何學習shell編程

   a. 能讀懂shell腳本---> 模仿---> 本身修改 ---> 本身寫 ---> 熟練運用(基礎命令) 重複性
   b. 有編程能力(寫的越多 編程能力越高)
   c. 找一本適合本身的教材 或者 完善的文檔 (腳本功能框架)
   e. 多練多操做
   f. 切記拿來直接使用,語句理解後在使用
  1. shell初步入門 1)什麼是shell

      shell是命令解釋器 負責翻譯咱們輸入的命令 輸入的ls pwd命令都是shell解釋器給咱們運行的
     交互式   咱們輸入命令,shell負責解釋執行咱們輸入的命令,而且把結果輸出到屏幕的過程稱爲交互式
    		  用戶簽退exit 關閉xshell shell終止
     非交互式 不和咱們交互,讀取存在到文本中的shell命令,讀取到文件結尾,shell結束

    2)什麼是shell腳本

      把可執行的命令統一放入到一個文本中,稱爲shell腳本 包含了 判斷語句 循環語句 數組等

    3)腳本語言的種類

      編譯型 c c++
     解釋型 python ruby
     腳本型 python shell javascript
     其餘語言: PHP html go
     問: 編譯型語法是最快的嗎 看優化
     面試題: Linux中默認的shell解釋器是 bash
     shell和py的區別
     shell處理底層的能力較強 全部的服務都是shell編寫 一鍵優化 一鍵安裝 一鍵統計(awk)
     py主要做用能夠寫界面 自動化管理平臺CMDB 功能需求

    4)書寫腳本的規範

     a. 腳本存放固定的目錄 統一管理 /server/scripts
     b. 腳本使用.sh結尾    讓咱們能識別是shell腳本
     c. 腳本命名 見名知其意 start_nginx.sh stop_nginx.sh
     d. 腳本內的開頭使用解釋器  #!/bin/bash 
     e. 腳本內的註釋最好不用中文(能夠用)
     f. 腳本內的成對的符號一次性書寫完畢 語法書寫完在寫內容

    5)寫第一個腳本 使用vim編輯test.sh 在屏幕上輸出 Hellow World!

     [root@shell ~]# cat test.sh
     #!/bin/sh
     echo "Hello World!"

    執行腳本經常使用的三種方式: 父shell和子shell的區別 子shell能夠繼承父shell的變量 /etc/profile 變量 全部的子shell均可使用 使用bash或sh執行shell腳本 都是在子shell中運行的

    1. 使用sh或者bash方式運行 開啓了一個子shell運行裏面的內容

    [root@shell ~]# sh test.sh
      Hello World!
    1. 使用全路徑方式執行腳本

    [root@shell ~]# /root/test.sh
    -bash: /root/test.sh: Permission denied
    [root@shell ~]# ./test.sh
    -bash: ./test.sh: Permission denied
     增長執行權限chmod
     [root@shell ~]# chmod +x test.sh 
     [root@shell ~]# ll
      total 4
      -rwxr-xr-x 1 root root 123 Oct 15 10:49 test.sh
      [root@shell ~]# /root/test.sh 
      Hello World!
      /bin/bash
      [root@shell ~]# ./test.sh
      Hello World!
      /bin/bash
    1. 使用. 或者source 在父進程中執行shell腳本

    [root@shell ~]# . test.sh 
    Hello World!
    /bin/bash
    [root@lb02 ~]# name=oldboy
    [root@shell ~]# echo $name
    oldboy

    不經常使用的執行方式

     [root@shell ~]# echo pwd|bash
     /root
     [root@shell ~]# sh < test.sh
     Hello World!
     /bin/bash
  2. shell經常使用的基礎變量 1) 什麼是變量

    系統中的變量: $PATH $LANG $BASH $PS1
      x=1 y=x+1 y=2
      x和y都是變量的名稱 等號後面的是變量的值
    name=oldboy
    使用一個固定的值表明不固定的值(數字 字符串 命令)

    2) 環境變量分類

    環境變量 (全局變量)  國法  全部的shell都生效
    普通變量 (局部變量)  家規  只針對當前的shell生效  自定義的變量
    查看系統環境變量 env

    3) 按照生命週期劃分

      臨時生效 只是在當前shell中生效 關閉則失效 使用export直接定義便可
       永久生效 對當前系統全部shell生效 寫入/etc/profile
       不加export 只對當前的shell生效
       加export   對當前的窗口的父shell和子shell生效

    4) 環境變量配置文件執行的順序

    /etc/profile
    讀取家目錄下全部的環境變量文件
    .bash_profile 
    .bashrc
    /etc/bashrc

5) 定義環境變量 環境變量的名稱定義: 字母數字下劃線開頭的組合 見名知其意 等號兩端不能有空格 不容許數字開頭

  oldboy_age=18    # 全小寫
  OLDBOY_AGE=25	   # 全大寫 系統環境變量都大寫
  oldboy_Age=30    # 小駝峯語法
  Oldboy_Age=35    # 大駝峯語法
  變量值的定義:  數字 字符串 和命令

三種值: 字符串定義 變量值中間不容許有空格 若是有須要加引號

  [root@shell ~]# test='I am lizhenya'	
  [root@shell ~]# oldboy_age=45
  [root@shell ~]# test=`pwd`		# 定義命令
  [root@shell ~]# echo $test		
  /root
  [root@shell ~]# test=$(ls)		# 定義命令
  [root@shell ~]# echo $test
  [root@shell ~]# ls
  test.sh

PS: 注意賦值反引號仍是雙引號

  [root@shell ~]# date
	Thu Oct 15 11:46:55 CST 2020
	[root@shell ~]# time=`date`
	[root@shell ~]# echo $time
	Thu Oct 15 11:47:00 CST 2020
	[root@shell ~]# echo $time
	Thu Oct 15 11:47:00 CST 2020
  [root@shell ~]# time="date +%F+%M+%S"

6) shell腳本中特殊的位置變量

$0    # 表示腳本的名稱 若是全路徑執行則帶全路徑 可使用basename只獲取名字
  $0
   [root@shell ~]# cat test.sh
	#!/bin/sh
	#print   hello world
	#Author  oldboy
	#date    20202020
	#version v1.0
	echo "Hello World!"
	echo $0
	[root@shell ~]# sh test.sh 
	Hello World!
test.sh
-------------------
$n 		# 表明了腳本的第n的參數 n爲數字 若是是0呢 則爲腳本名稱 從1開始 從$9之後須要加{} 表示總體
-gt 是否大於
-ge 是否大於等於
-eq 是否等於
-ne 是否不等於
-lt 是否小於
-le 是否小於等於

	$#      # 表示腳本傳參的個數
	使用方法 判斷傳參的個數
	[root@shell ~]# cat test.sh 
	[ $# -ne 2 ] && echo "請輸入兩個參數" && exit
	expr $1 + $2
	[ $# -ne 2 ] && echo "請輸入兩個參數" && exit
	echo $1
	echo $2

$? # 判斷上一條命令的結果是否正確 0爲正確 非0失敗

[root@shell ~]# cat ping.sh 
ping -c2 -W1  www.baiduaaaaa.com &>/dev/null
[ $? -ne 0 ] && echo "ping不通" || echo "通了"
$$      # 獲取當前腳本的PID
	[root@shell ~]# cat test.sh 
	#!/bin/sh
	#print   hello world
	#Author  oldboy
	#date    20202020
	#version v1.0
	echo $$ > /tmp/nging.pid
	sleep 300

$$      # 獲取當前腳本的PID
$#      # 表示腳本傳參的個數
$n 		  # 表明了腳本的第n的參數 n爲數字 若是是0呢 則爲腳本名稱 從1開始 從$9之後須要加{} 表示總體
$0      #腳本名稱
$?      # 判斷上一條命令的結果是否正確 0爲正確 非0失敗
$!      # 獲取上一個在後臺運行腳本的PID號 排錯使用
$_		  # 獲取當前命令行的最後一個參數 相似於esc .
$*		  # 獲取腳本傳參的全部參數 在循環體中 不加雙引號和$@相同 加雙引號 把全部的參數當作一個參數	
$@      # 獲取腳本傳參的素有參數 在循環體中 不加雙引號和$*相同 加雙引號 把全部的參數當作獨立的
小結: 經常使用的位置參數
$0  $# $n $?
  1. 腳本傳參的三種方式 1) 直接傳參

     [root@shell ~]# cat test.sh 
    echo name=$1
    echo age=$2
    [root@shell ~]# sh test.sh  oldboy 18
    name=oldboy
    age=18

    2) 賦值傳參

    [root@shell ~]# cat test.sh
    #!/bin/sh
    name=$1
    age=$2
    echo -e "$name\n$age"
    [root@shell ~]# sh test.sh oldboy 20
    oldboy
    20

    3) read 傳參 read 變量名稱 進行賦值 read -p "用戶提示" 變量名稱

[root@shell ~]# cat read.sh 
#!/bin/bash
read -p "請輸入你的姓名: " name
echo "你輸入的姓名是 $name"
[root@shell ~]# cat read.sh 
#!/bin/bash
read -p "請輸入你的姓名和年齡: " name age
echo "你輸入的姓名是: $name 年齡是: $age"
[root@shell ~]# sh read.sh
請輸入你的姓名和年齡: oldboy 20
你輸入的姓名是: oldboy 年齡是: 20
[root@shell ~]# cat read.sh
#!/bin/bash
read -p "請輸入你的姓名: " name 
read -p "請輸入你的年齡: " age
echo "你輸入的姓名是: $name 年齡是: $age"
[root@shell ~]# sh read.sh
請輸入你的姓名: oldboy
請輸入你的年齡: 20
你輸入的姓名是: oldboy 年齡是: 20
案例: 使用read傳參的方式 修改主機名稱爲shell 而且修改eth0網卡IP地址爲88
[root@shell ~]# cat hostname.sh 
#!/bin/bash
eth0_cfg='/etc/sysconfig/network-scripts/ifcfg-eth0'
old_ip=`ifconfig eth0|awk 'NR==2{print $2}'|awk -F. '{print $NF}'`
read -p "please input hostname: " name
read -p "please input New IP: " IP
hostnamectl set-hostname $name
sed -i "s#$old_ip#$IP#g" $eth0_cfg
grep $IP $eth0_cfg
[root@shell ~]# cat ping.sh 
read -p "Please Input URL: " url
ping -c2 -W1  $url &>/dev/null
[ $? -ne 0 ] && echo "ping不通" || echo "通了"
重複賦值
[root@shell ~]# cat dir.sh
#!/bin/sh
dir1=/etc
dir2=/tmp
read -p "請輸入須要備份的目錄: " dir2
echo $dir1
echo $dir2
[root@shell ~]# sh dir.sh
請輸入須要備份的目錄: /opt
/etc
/opt
  1. 變量的子串及刪除替換

    [root@shell ~]# test='I am oldboy'
    [root@shell ~]# echo $test
    I am oldboy
    [root@shell ~]# echo $test|awk '{print $2}'
    am
    [root@shell ~]# echo $test|cut -c3-4
    am

變量切片

nginx
[root@shell ~]# echo ${test:2:2}
am
[root@shell ~]# echo ${#test}
11
統計字符的長度
nginx
	[root@shell ~]# echo $test|wc -L
	11
	[root@shell ~]# expr length "$test"
	11
	[root@shell ~]# echo $test|awk '{print length}'
	11
統計出字符串小於3的單詞 筆試題
I am lzhenya teacher I am 18
[root@shell ~]# cat for.sh
for i in I am lzhenya teacher I am 18
do
		[ ${#i} -lt 3 ] && echo $i
done	
	[root@shell ~]# sh for.sh
	I
	am
	I
	am
	18
[root@shell ~]# echo I am lzhenya teacher I am 18|xargs -n1|awk '{if(length<3)print}'
	I
	am
	I
	am
	18
	[root@shell ~]# echo I am lzhenya teacher I am 18|awk '{for(i=1;i<=NF;i++)if(length($i)<3)print $i}'
  I
	am
	I
	am
	18
子串的刪除替換
[root@shell ~]# echo ${url}
www.baidu.com	
[root@shell ~]# echo ${url}|awk -F "w." '{print $3 }'
baidu.com
[root@shell ~]# echo ${url}
www.baidu.com
[root@shell ~]# echo ${url#www.}
baidu.com
[root@shell ~]# echo ${url#*.}
baidu.com
[root@shell ~]# echo ${url#*.*.}
com
貪婪匹配
[root@shell ~]# echo ${url##*.}
com
[root@shell ~]# echo ${url%.com}
www.baidu
[root@shell ~]# echo ${url%.*}
www.baidu
[root@shell ~]# echo ${url%.*.*}
www
[root@shell ~]# echo ${url%%.*}
www
[root@shell ~]# test=%66
[root@shell ~]# echo $test
%66
[root@shell ~]# echo ${test#%}
66
[root@shell ~]# echo ${test##}
#66
[root@shell ~]# echo ${test#\#}
66
變量替換
nginx
[root@shell ~]# echo $url
www.baidu.com
[root@shell ~]# echo $url|sed 's#www#WWW#g'
WWW.baidu.com
[root@shell ~]# echo $url
www.baidu.com
[root@shell ~]# echo ${url/w/W}
Www.baidu.com
[root@shell ~]# echo ${url//w/W}
WWW.baidu.com	
[root@shell ~]# echo ${url/baidu/sina}
www.sina.com
知識點回顧:
$0 $n $# $? $$ $! $* $@ $_
1.shell數值運算
  1) expr 只能作整數運算 不支持小數運算
  命令格式:
		   expr 整數1 空格 運算符 空格 整數2
[root@shell ~]# expr 1 +1
expr: syntax error
[root@shell ~]# expr 10 - 5
5
[root@shell ~]# expr 10 * 5
expr: syntax error
[root@shell ~]# expr 10 \* 5
50
[root@shell ~]# expr 10 / 5
2
[root@shell ~]# expr length oldboy	# 統計字符串長度
6

2) $[] 只支持整數運算 不支持小數運算

[root@shell ~]# echo $[1+1]
2
[root@shell ~]# echo $[1+1.5]
-bash: 1+1.5: syntax error: invalid arithmetic operator (error token is ".5")
[root@shell ~]# echo $[1+10]
11
[root@shell ~]# echo $[1-10]
-9
[root@shell ~]# echo $[100*10]
1000
[root@shell ~]# echo $[100/10]
10
[root@shell ~]# #100*10=1000
[root@shell ~]# echo 100*10=1000
100*10=1000
[root@shell ~]# echo 100*10=$[100*10]
100*10=1000
[root@shell ~]# num1=100
[root@shell ~]# num2=10
[root@shell ~]# echo $num1*$num2
100*10
[root@shell ~]# echo $num1*$num2=$[$num1*$num2]
100*10=1000

3) $(()) 只支持整數運算 不支持小數運算 PS: $()執行命令 $(()) 數值運算 執行效率最高

  [root@shell ~]# echo $((1+1))
	2
	[root@shell ~]# echo $((1+1.5))
	-bash: 1+1.5: syntax error: invalid arithmetic operator (error token is ".5")
	[root@shell ~]# echo $((100-100))
	0
	[root@shell ~]# echo $((100*100))
	10000
	[root@shell ~]# echo $((100/100))
	1

4) let 只支持整數運算 不支持小數運算 語法格式:

		let sum=1+1
輸入結果: echo $sum
[root@shell ~]# let sum=1+1
[root@shell ~]# echo $sum
2
[root@shell ~]# let sum=100-100
[root@shell ~]# echo $sum
0
[root@shell ~]# let sum=100*100
[root@shell ~]# echo $sum
10000
[root@shell ~]# let sum=100/100
[root@shell ~]# echo $sum
1
[root@shell ~]# let sum=$num1+$num2
[root@shell ~]# echo $sum
110
[root@shell ~]# let i++
[root@shell ~]# echo $i
1
[root@shell ~]# let i++
[root@shell ~]# echo $i
2
[root@shell ~]# let i++
[root@shell ~]# echo $i
3
[root@shell ~]# echo i++ ===== i=i+1
i++ ===== i=i+1
[root@shell ~]# unset i
[root@shell ~]# let i=i+1
[root@shell ~]# echo $i
1
[root@shell ~]# let i=i+1
[root@shell ~]# echo $i
2
[root@shell ~]# let i++
[root@shell ~]# let i++
[root@shell ~]# let i++

5) bc 使用yum安裝 yum -y install bc 支持整數和小數運算 語法格式: echo 10*10|bc

[root@shell ~]# echo 10*10|bc
100
[root@shell ~]# echo 10/10|bc
1
[root@shell ~]# echo 10-10|bc
0
[root@shell ~]# echo 10+10|bc
20
[root@shell ~]# echo 10+10.2|bc
20.2
[root@shell ~]# echo 10.5+10.2|bc
20.7
[root@shell ~]# echo 10.5*10.2|bc
107.1

其餘運算 支持整數和小數運算 awk python awk語法: awk 'BEGIN{print 10*10}'

[root@shell ~]# awk 'BEGIN{print 10*10}'
100
[root@shell ~]# awk 'BEGIN{print 10-10}'
0
[root@shell ~]# awk 'BEGIN{print 10/10}'
1
[root@shell ~]# awk 'BEGIN{print 10+10}'
20
[root@shell ~]# awk 'BEGIN{print 10^10}'
10000000000
[root@shell ~]# echo 10 10|awk '{print $1*$2}' 
100
[root@shell ~]# echo 10 10|awk '{print $1^$2}' 
10000000000

案例: 作一個加減乘除的計算器 要求用戶輸入兩個輸入 顯示加減乘除後的結果 使用三種傳參方式 sh count.sh 10 10 輸入的結果

  10+10=20
	  10-10=
	  10*10=
	  10/10=
[root@shell ~]# cat count.sh 
#!/bin/sh
[ $# -ne 2 ] && echo "輸入兩個參數多了不行" && exit
#1.直接傳參
echo $1+$2=$[$1+$2]
echo $1-$2=$[$1-$2]
echo $1*$2=$[$1*$2]
echo $1/$2=$[$1/$2]
#2 賦值傳參
num1=$1
num2=$2
echo $num1+$num2=$[$num1+$num2]
echo $num1-$num2=$[$num1-$num2]
echo $num1*$num2=$[$num1*$num2]
echo $num1/$num2=$[$num1/$num2]
#3 read傳參
read -p "輸入兩個參數: " num1 num2
echo $num1+$num2=$[$num1+$num2]
echo $num1-$num2=$[$num1-$num2]
echo $num1*$num2=$[$num1*$num2]
echo $num1/$num2=$[$num1/$num2]
  1. 條件表達式之文件判斷 語法格式: 方法1 test 表達式 文件|目錄 例如: test -e /etc/passwd 方法2 [ -e file ] 經常使用表達式:

    [ -e file|dir ]   # 文件存在則爲真 執行&& 不然執行||
    [ -f file ]		    # 判斷文件是否存在而且爲普通文件
    [ -d dir  ]       # 判斷文件存在而且爲目錄
    [ -r file ]       # 文件存在而且可讀
    [ -w file ]		    # 文件存在而且有寫入權限
    [ -x file ]		    # 文件存在而且可執行
    [root@shell ~]# test -e /etc/passwd
    [root@shell ~]# echo $?
    0
    [root@shell ~]# test -e /etc/passwd;echo $?
    0
    [root@shell ~]# test -e /etc/passwdddddddd;echo $?
    1
    [root@shell ~]# test -e /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件存在
    [root@shell ~]# test -f /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件存在
    [root@shell ~]# test -d /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件不存在
    [root@shell ~]# test -x /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件不存在
    [root@shell ~]# test -r /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件存在
    [root@shell ~]# test -w /etc/passwd && echo "文件存在" || echo "文件不存在"
    文件存在
[root@shell ~]# [ -e /etc/hosts ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -f /etc/hosts ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -d /etc/hosts ] && echo "文件存在" || echo "文件不存在"
文件不存在
[root@shell ~]# [ -d /etc/ ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -r /etc/ ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -w /etc/hosts ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -x /etc/hosts ] && echo "文件存在" || echo "文件不存在"
文件不存在
[root@shell ~]# [ -x test.sh ] && echo "文件存在" || echo "文件不存在"
文件存在
判斷中可使用命令的方式進行取值判斷
[root@shell ~]# [ -f `ls test.sh` ]
使用and和or判斷多個文件
[root@shell ~]# [ -f /etc/hosts -a -f /etc/passwd ]
[root@shell ~]# [ -f /etc/hosts -a -f /etc/passwd ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -f /etc/hostsss -a -f /etc/passwd ] && echo "文件存在" || echo "文件不存在"
文件不存在
[root@shell ~]# [ -f /etc/hostsss -o -f /etc/passwd ] && echo "文件存在" || echo "文件不存在"
文件存在
[root@shell ~]# [ -f /etc/hostsss -o -f /etc/passwddd ] && echo "文件存在" || echo "文件不存在"
文件不存在
案例1:
使用變量賦值一個目錄,判斷目錄是否存在,若是存在則打包壓縮,若是不存在則提示目錄不存在
[root@shell ~]# dir=/etc/;[ -d $dir ] && tar zcvf test.tar.gz $dir || echo "文件不存在"
案例2:
判斷一個目錄是否存在 若是不存在則建立
[root@shell ~]# [ -d /hehe ] && mkdir hehe
[root@shell ~]# ll hehe
ls: cannot access hehe: No such file or directory
[root@shell ~]# ll /hehe
ls: cannot access /hehe: No such file or directory
[root@shell ~]# 
[root@shell ~]# [ -d hehe ] && mkdir hehe
[root@shell ~]# ll hehe
ls: cannot access hehe: No such file or directory
[root@shell ~]# [ -d hehe ] || mkdir hehe
[root@shell ~]# ll hehe
total 0
案例3:
判斷環境變量文件函數庫是否存在 存在則執行.
[root@shell ~]# cat test.sh 
#!/bin/sh
[ -f /etc/init.d/functions ] && . /etc/init.d/functions
ping -c2 $1 &>/dev/null
[ $? -eq 0 ] && action "ping $1 is ok" true || action "ping $1 is error" false
  1. 數值比較 整數 格式: test 整數1 表達式 整數2 [ 整數1 表達式 整數2 ]

    表達式:

    -eq   等於
    -ne   不等於
    -gt   大於
    -ge   大於等於
    -lt   小於
    -le   小於等於

    [] 判斷支持正則 必須使用[[]] 在正則中使用如下符號

    =
    !=
    >=
    ><
    ><=
    [root@shell ~]# [ 10 -eq 10 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 10 -ne 10 ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ 10 -ne 100 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 10 -gt 100 ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ 10 -lt 100 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 100 -le 100 ] && echo "成立" || echo "不成立"
    成立

    使用變量的方式比較

    [root@shell ~]# echo $num1
    100
    [root@shell ~]# [ $num1 -le $num2 ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# echo $num2
    10
    [root@shell ~]# [ $num1 -gt $num2 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ $num1 -ne $num2 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ $num1 -ne $num2 ] && echo "成立" || echo "不成立"
    [root@shell ~]# name='I am lizhenya'
    [root@shell ~]# echo $name
    I am lizhenya
    [root@shell ~]# for i in $name;do echo $i;done
    I
    am
    lizhenya
    [root@shell ~]# for i in $name;do echo ${#i};done
    1
    2
    8
    [root@shell ~]# for i in $name;do [ ${#i} -lt 3 ];done
    [root@shell ~]# for i in $name;do [ ${#i} -lt 3 ]&& echo $i;done
    I
    am
    [root@shell ~]# for i in $name;do [ ${#i} -gt 3 ]&& echo $i;done
    lizhenya

    [root@shell ~]# [ 10 != 10 ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ 100 > 10 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 100 >= 10 ] && echo "成立" || echo "不成立"
    -bash: [: 100: unary operator expected
    不成立
    [root@shell ~]# [ 100 < 10 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [[ 100 < 10 ]] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [[ 100 > 10 ]] && echo "成立" || echo "不成立"
    成立

    案例1: 使用read或直接傳參方式寫一個數值比較腳本(使用數值表達式) 要求 輸入兩個數字 兩個數字須要加判斷 輸入結果

       sh count.sh 100 10
    	   100>10
    
    [root@shell ~]# cat count.sh 
    #!/bin/sh
    [ $1 -gt $2 ] && echo "$1>$2"
    [ $1 -lt $2 ] && echo "$1<$2"
    [ $1 -eq $2 ] && echo "$1=$2"

    案例2: 統計磁盤使用率 若是超過3 則發送郵件通知管理員,正常則輸出當前使用率 第一個步驟: 取出磁盤使用率的數值

    [root@shell ~]# df -h|awk 'NR==2{print $(NF-1)}'
    4%

    第二個步驟: 比較 優化取到的值

    [root@shell ~]#df -h|awk 'NR==2{print $(NF-1)}'|awk -F% '{print $1}'

    進行比較

    [root@shell ~]# [ `df -h|awk 'NR==2{print $(NF-1)}'|awk -F% '{print $1}'` -gt 3 ] && echo mail..... || echo ok
    mail.....

    第三個步驟: 寫入腳本

    [root@shell ~]# cat disk.sh 
    #!/bin/bash
    use_disk=`df -h|awk 'NR==2{print $(NF-1)}'`
    [ ${use_disk%\%} -gt 80 ] && echo sendmail........ || echo "當前的磁盤使用率爲 $use_disk"
    [root@shell ~]# sh disk.sh 
    當前的磁盤使用率爲 4%

    案例3: 統計內存使用率 若是超過3 則發送通知管理員,不然輸出當前的內存使用率 內存使用率的計算方式 使用的除以總共的乘以100 統計內存使用率:

    [root@shell ~]# free|awk 'NR==2{print $3/$2*100}'
    		5.46261
    數值比較 
        [ `free|awk 'NR==2{print $3/$2*100}'|awk -F. '{print $1}'` -gt 3 ] && echo sendmail.... || echo ok 
    寫入腳本
    [root@shell ~]# cat use_mem.sh
    #!/bin/sh
    use_mem=`free|awk 'NR==2{print $3/$2*100}'`
    [ ${use_mem%.*} -gt 80 ] && echo sendmail...... || echo "當前內存使用率 ${use_mem}%"
    [root@shell ~]# sh use_mem.sh
    當前內存使用率 5.4916%
    [root@shell ~]# cat use_mem.sh
    #!/bin/sh
    . /etc/init.d/functions
    use_mem=`free|awk 'NR==2{print $3/$2*100}'`
    [ ${use_mem%.*} -lt 3 ] && action "sendmail......" false || action "當前內存使用率 ${use_mem}%" true

    案例4: 統計當前的負載 負載大於1 則發送通知 不然輸出當前的負載使用狀況

    yum -y install httpd
    systemctl start httpd
    ab -n800000 -c400 http://10.0.0.61/index.html
    [root@shell ~]# cat use_load.sh 
    #!/bin/sh
    use_load=`uptime|awk '{print $(NF-2)}'`
    [ ${use_load%.*} -gt 1 ] && echo sendmail............. || echo "當前系統的負載爲 ${use_load%,}"

    案例5: 統計系統的主機名稱 虛擬平臺 系統版本 內核版本 eth0 IP地址 外網網卡IP地址 當前內存和磁盤的使用率 輸出的結果: 當前系統主機名稱爲: shell 當前系統內存使用率: 20%

    [root@lb02 ~]# cat static.sh 
    #!/bin/bash
    eth0_cfg='/etc/sysconfig/network-scripts/ifcfg-eth0'
    old_ip=`ifconfig eth0|awk 'NR==2{print $2}'|awk -F. '{print $NF}'`
    read -p "please input hostname: " name
    read -p "please input New IP: " IP
    hostnamectl set-hostname $name
    sed -i "s#$old_ip#$IP#g" $eth0_cfg
    grep $IP $eth0_cfg
    
    use_virtualization=`hostnamectl|awk 'NR==6{print $NF}'`
    [ ${use_virtualization} ] && echo "當前系統的虛擬平臺爲  ${use_virtualization}"
    use_oprete=`hostnamectl|awk 'NR==7{print $(NF-1)}'`
    [ ${use_oprete} ] && echo "當前系統版本爲 ${use_oprete}"
    use_kernal=`hostnamectl|awk 'NR==9{print $NF}'`
    [ ${use_kernal} ] && echo "當前系統內核版本爲 ${use_kernal}"
    use_mem=`free|awk 'NR==2{print $3/$2*100}'`
    [ ${use_mem%.*} -gt 80 ] && echo sendmail...... || echo "當前內存使用率 ${use_mem}%"
    use_disk=`df -h|awk 'NR==2{print $(NF-1)}'`
    [ ${use_disk%\%} -gt 80 ] && echo sendmail........ || echo "當前的磁盤使用率爲 $use_disk"
  2. 多整數比較

    -a and
    -o or
     [ 10 -eq 10 -a 100 -gt 50 -o 100 -ne 100 ]
     [root@shell ~]# [ 10 -eq 10 ]
    [root@shell ~]# [ 10 -eq 10 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# 
    [root@shell ~]# [ 10 -eq 10 -a 100 -gt 50 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 10 -ne 10 -a 100 -gt 50 ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# 
    [root@shell ~]# [ 10 -eq 10 -a 100 -gt 50 -o 100 -ne 100 ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ 10 -eq 10 -o 100 -ne 100 ] && echo "成立" || echo "不成立"
    成立
    在正則中使用 && ||
     [root@shell ~]# [[ 10 -eq 10 && 100 -gt 20 ]] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [[ 10 -eq 10 || 100 -gt 20 ]] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [[ 10 -eq 11 || 100 -gt 20 ]] && echo "成立" || echo "不成立"
    成立
  3. 字符串比對

    [root@shell ~]# [ user = user ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# [ user = userrr ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ $USER = userrr ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ $USER = user ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# echo $USER
    root
    [root@shell ~]# [ $USER = root ] && echo "成立" || echo "不成立"
    成立
    -n //nozero 字符串長度不爲0時爲真
    -z //zero 字符串長度爲0時爲真
    [root@shell ~]# test=""
    [root@shell ~]# [ -z $test ] && echo "成立" || echo "不成立"
    成立
    [root@shell ~]# test="aaa"
    [root@shell ~]# [ -z $test ] && echo "成立" || echo "不成立"
    不成立
    [root@shell ~]# [ -n $test ] && echo "成立" || echo "不成立"
    成立

    案例: 傳參的時候不容許爲空

    [root@shell ~]# cat test.sh
    #!/bin/sh
    [ -f /etc/init.d/functions ] && . /etc/init.d/functions
    [ -z $1 ] && echo "請輸入url" && exit
    ping -c2 $1 &>/dev/null
    [ $? -eq 0 ] && action "ping $1 is ok" true || action "ping $1 is error" false
    [root@shell ~]# cat count.sh 
    #!/bin/sh
    [ $# -ne 2 ] && echo "請輸入兩個參數" && exit
    [ -z $1  ] && echo "請輸入兩個參數" && exit
    [ -z $2  ] && echo "請輸入兩個參數" && exit
    [ $1 -gt $2 ] && echo "$1>$2"
    [ $1 -lt $2 ] && echo "$1<$2"
    [ $1 -eq $2 ] && echo "$1=$2"
    read -p "請輸入你的姓名: " name
    [ -z $name ] && echo "請輸入姓名" && exit
    echo $name

    案例: 判斷輸入的是否爲數字

    [root@shell ~]# expr 1 + 1.5
    expr: non-integer argument
    [root@shell ~]# echo $?
    2
    [root@shell ~]# expr 1 + qq
    expr: non-integer argument
    [root@shell ~]# echo $?
    2
    [root@shell ~]# expr 1 + 234
    235
    [root@shell ~]# echo $?
    0
    [root@shell ~]# cat count.sh 
    #!/bin/sh
    [ $# -ne 2 ] && echo "請輸入兩個參數" && exit
    expr $1 + $2 &>/dev/null
    [ $? -ne 0 ] && echo "請輸入兩個整數" && exit
    [ $1 -gt $2 ] && echo "$1>$2"
    [ $1 -lt $2 ] && echo "$1<$2"
    [ $1 -eq $2 ] && echo "$1=$2"
  4. 正則比對

  [root@shell ~]# [[ $user =~ ^r ]] && echo "成立" || echo "不成立"
  成立
  [root@shell ~]# [[ user =~ ^r ]] && echo "成立" || echo "不成立"
  不成立
  [root@shell ~]# [[ user =~ user ]] && echo "成立" || echo "不成立"
  成立
  [root@shell ~]# [[ user =~ r$ ]] && echo "成立" || echo "不成立"
  成立
  [root@shell ~]# [[ user =~ r$ && oldboy =~ ^o ]] && echo "成立" || echo "不成立"
  成立
  [root@shell ~]# [[ user =~ r$ && oldboy =~ ^a ]] && echo "成立" || echo "不成立"
  不成立
  [root@shell ~]# [[ user =~ r$ || oldboy =~ ^a ]] && echo "成立" || echo "不成立"
  成立
  [root@shell ~]# [[ $num1 =~ ^[0-9]+$ ]] && echo "成立" || echo "不成立"
  不成立
  [root@shell ~]# cat count.sh 
  #!/bin/sh
  [ $# -ne 2 ] && echo "請輸入兩個參數" && exit
  [[ ! $1 =~ ^[0-9]+$ ]] && echo "請輸入整數" && exit 
  [ $1 -gt $2 ] && echo "$1>$2"
  [ $1 -lt $2 ] && echo "$1<$2"
  [ $1 -eq $2 ] && echo "$1=$2"

練習 判斷輸入的是否爲整數 判斷輸入的是否爲空 expr -z [[ 正則判斷 ]]

  1. if判斷 語法格式:

    [ 10 -eq 10 ] && echo ok || echo error
    
    if [ 10 -eq 10 ];then
       echo ok
    fi

    單分支語法: 一個條件 一個結果 [ 你有錢 ] && 我就嫁給你

    if [ 你有錢 ];then 		
      我就嫁給你
    fi
    if [ 你有錢 ]
    then
        我就嫁給你
    fi
    雙分支語法: 一個條件 兩個結果
    if [ 你有錢 ];then			 [ 你有錢 ] && 我就嫁給你 || 拜拜
      我就嫁給你
    else
       拜拜
    fi
    多分支語法: 多個條件 多個結果
    if [ 你有錢 ];then
       我就嫁給你
    elif [ 你爸是李剛 ];then
       我也嫁給你
    elif [ 你活好 ];then
       我倒貼
    elif [ 你在老男孩學習 ];then
       先談戀愛吧
    else
       拜拜
    fi

    案例: expr 判斷輸入的是否爲整數

     [root@shell ~]# cat count.sh 
    	#!/bin/sh
    	expr $1 + $2 &>/dev/null
    	if [ $? -ne 0 ];then
    	   echo "請輸入兩個整數"
    	   exit
    	fi
    使用$# 判斷傳參的總個數
    	if [ $# -ne 2 ]
    	then
    	   echo "請輸入兩個參數"
    	   exit
    	fi
    使用正則匹配判斷輸入的是否爲整數
    	if [[ ! $1$2 =~ ^[0-9]+$  ]];then
    	echo "請輸入整數"
    	exit
    	fi
    使用-z判斷是否爲空
    read -p "please input hostname: " name
    if [ -z $name ];then
       echo "請輸入姓名"
       exit
    fi

    案例: 不一樣的操做系統版本安裝不用的YUM源 獲取操做系統版本號:

    [root@shell ~]# awk '{print $(NF-1)}' /etc/redhat-release 
    7.6.1810
    [root@oldboyedu-c6 ~]# awk '{print $(NF-1)}' /etc/redhat-release
    6.9
    [root@shell ~]# cat yum.sh
    #!/bin/sh
    #判斷網絡是否正常
    #ping的返回結果使用if判斷
    os_version=`awk '{print $(NF-1)}' /etc/redhat-release`
    if [[ $os_version =~ ^7 ]];then
    判斷wget是否存在
    which wget &>/dev/null
       if [ $? -ne 0 ];then
    		   yum -y install wget &>/dev/null
    			  if [ $? -eq 0 ];then
    			 echo "install wget is ok.............."
    			  else
    			echo "請檢查你的網絡或者源"
    			  fi
       fi
    	  mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
    	  wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
    elif
       [[ $os_version =~ ^6 ]];then
       echo yum6..................
    else
       echo "請檢查你的系統版本"
    fi

if案例: 根據需求安裝不一樣版本的PHP

菜單: 列出當前腳本的功能

	 [root@shell ~]# cat menu.sh 
	#!/bin/sh
	while true
	do
		echo -e "\t\t\t##################"
		echo -e "\t\t\t#1. INSTLL PHP7.1#"
		echo -e "\t\t\t#2. INSTLL PHP7.2#"
		echo -e "\t\t\t#3. INSTLL PHP7.3#"
		echo -e "\t\t\t#4. INSTLL PHP7.4#"
		echo -e "\t\t\t#5. INSTLL MySQL #"
		echo -e "\t\t\t#6. exit         #"
		echo -e "\t\t\t##################"
		read -p "請輸入須要安裝的PHP的序號[1|2|3]: " num
		if [ $num -eq 1 ];then
		   echo yum7.1....................
		elif [ $num -eq 2 ];then
		   echo yum7.2...................
		elif [ $num -eq 3 ];then
		   echo yum7.3...................
		elif [ $num -eq 4 ];then
		   echo yum7.4...................
	elif [ $num -eq 5 ];then
	   echo MySQL...................
	elif [ $num -eq 6 ];then
	   exit
	fi
done

案例: 使用菜單的方式和if判斷 顯示當前系統的 內存 CPU負載 登陸系統用戶 當前的磁盤使用 退出 菜單: echo cat

 [root@shell ~]# cat os_menu.sh 
	#!/bin/sh
	menu(){
	cat<<EOF
							1 f.  查看系統內存	
							2 u.  查看CPU負載
							3 w.  查看當前用戶
							4 d.  查看當前磁盤
							5 e.  exit
							6 h.  顯示當前菜單
	EOF
	}
	menu
	while true
	do
		read -p "請輸入要查看系統信息的編號或字母[1|f]: " num
		if [ $num = 1 -o $num = "f" ];then
		   free -h
		elif [ $num = 2 -o $num = u ];then 
		   uptime
		elif [ $num = 3 -o $num = w ];then
		   w
		elif [ $num = 4 -o $num = d ];then
		   df -h
		elif [ $num = 6 -o $num = h ];then
		   menu
		else
		   exit
		fi
	done

案例:猜數字遊戲 系統隨機生成一個數字, 讓用戶輸入這個數字,若是大了 則提示大 小了則提示小了 相等則提示用戶猜對了.

  1. 生成隨機數

    [root@shell ~]# echo $((RANDOM%100+1))
    29
    [root@shell ~]# cat ran.sh 
    #!/bin/sh
    ran=`echo $((RANDOM%100+1))`
    while true
    do
    	let i++
    	read -p "請輸入你要猜的數字1-100: " num
    	if [ $num -gt $ran ];then
    	   echo "你輸入的大了"
    	elif [ $num -lt $ran ];then
    	   echo "你輸入的小了"
    	else
    	   echo "恭喜你猜對了"
    	   echo "總共猜了 $i 次"
    		   exit
    	fi
    done   

case語句 語法格式:

case 變量 in
	  匹配變量1)
	     執行命令序列
		 ;;
      匹配變量2)
	     執行命令序列
		 ;;
	  匹配變量3)
	     執行命令序列
		 ;;
	  *)
	     執行命令序列
esac

案例: 查看系統信息

 [root@shell ~]# cat case.sh 
	#!/bin/bash
	menu(){
	cat<<EOF
					d. 查看磁盤
					f. 查看內存
					w. 查看用戶
					u. 查看負載
					h. 顯示菜單
					e. 退出腳本
	EOF
	}
	menu
	while true
	do
		read -p "請輸入內容[d|f|w]: " test
		case $test in
			   d)
				 df -h
			   ;;
			   f)
				 free -h
			   ;;
			   w)
				 w
			   ;;
			   h)
				 clear
				 menu
			   ;;
			   *)
				echo "請輸入菜單顯示的系統項"
		esac
	done
 案例: shell跳板機
 [root@shell ~]# cat jumpserver.sh
 #!/bin/sh
 . /etc/init.d/functions
 WEB01=10.0.0.7
 WEB02=10.0.0.8
 LB01=172.16.1.5
 LB02=10.0.0.6
 MySQL=10.0.0.51
 NFS=10.0.0.31
 BACKUP=10.0.0.41
 menu1(){
 cat<<EOF
  1. 運維
  2. 開發
  3. 測試
  4. 菜單
 EOF
 }
 menu1
 menu(){
 cat<<EOF
  1. WEB01-10.0.0.7
  2. WEB02-10.0.0.8
  3. LB01-10.0.0.5
  4. LB02-10.0.0.6
  5. MySQL-10.0.0.51
  6. NFS-10.0.0.31
  7. 顯示菜單
  8. 返回上一級菜單
 EOF
 }
 menu2(){
 cat<<EOF
  1.MySQL-10.0.0.51
  2.LB01-10.0.0.5
  3.顯示菜單
  4.返回上一級菜單
 EOF
 }
 trap "" INT TSTP HUP#禁止ctrl+c ctrl+d等
 while true
 do
 read -p "選擇你的崗位:[1|2|3]: " jd
  if [[ ! $jd =~ ^[0-9]+$ ]];then
  echo "必須輸入數字"
  continue
  fi
 if [ $jd -eq 1 ];then
  while true
  do
  let i++
  read -p "請輸入運維的口令: " op
  [ -z $op ] && echo "請輸入正確的口令,不容許爲空" && continue
  if [ $op = "woxiangjinqu" ];then
  action "口令正確歡迎運維大神歸來" /bin/true
  break
  else
  action "口令不對從新輸入!!!" /bin/false
  if [ $i -ge 3 ];then
  /root/game.sh
  fi
  fi
  done
  menu
 while true
 do
 read -p "請輸入你要鏈接的服務器的編號[1|2|3|4菜單]: " num
 
 case $num in
   1)
  ssh $WEB01
  ;;
   2)
  ssh $WEB02
  ;;
   3)
  ssh $LB01
  ;;
   4)
  ssh $LB02
  ;;
   5)
  ssh $MySQL
  ;;
   7)
  clear
  menu
  ;;
   houmen)
  exit
  ;;
   8)
   menu1    
  break
  ;;
  *)
  echo "請輸入正確的服務器編號"
 esac
 done
  elif [ $jd = 2 ];then
  while true
  do
  read -p "請輸入開發的口令: " dev
  [ -z $dev ] && echo "請輸入正確的口令,不容許爲空" && continue
  if [ $dev = "wos" ];then
  action "口令正確歡迎代碼搬運工" /bin/true
  break
  else
  action "口令不對從新輸入!!!" /bin/false
  fi
  done  
  menu2
  while true
  do
  read -p "請輸入鏈接的服務器編號:[1|2]: " num2 
case $num2 in 
1) 
ssh $MySQL 
;; 
2) 
ssh $LB01 
;; 
3)  
 clear 
 menu2 
;; 
4) 
 men1 
 break 
;; 
houmen) 
 exit 
;; 
*
echo "請輸入正確的編號: [1|2]" 
esac 
done 
elif [ $jd = 4 ];then 
menu1 
fi 
done
------------------------------精簡版----------------------------
[root@shell ~]# cat jump.sh
#!/bin/sh
. /etc/init.d/functions
WEB01=10.0.0.7
WEB02=10.0.0.8
LB01=172.16.1.5
LB02=10.0.0.6
MySQL=10.0.0.51
NFS=10.0.0.31
BACKUP=10.0.0.41
menu(){
cat<<EOF
						1. WEB01-10.0.0.7
						2. WEB02-10.0.0.8
						3. LB01-10.0.0.5
						4. LB02-10.0.0.6
						5. MySQL-10.0.0.51
						6. NFS-10.0.0.31
						7. 顯示菜單
EOF
}
menu
trap "" INT TSTP HUP
while true
do
read -p "請輸入你要鏈接的服務器的編號[1|2|3|4菜單]: " num
case $num in
	  1|web01)
		ssh $WEB01
	  ;;
	  2|web02)
		ssh $WEB02
	  ;;
	  3|lb01)
		ssh $LB01
	  ;;
	  4)
		ssh $LB02
	  ;;
	  5)
		ssh $MySQL
	  ;;
	  7)
		clear
		menu
	  ;;
		  houmen)
		exit
	  ;;
	  *) 
		echo "請輸入正確的服務器編號"
esac
done
-------------------------------------------------------------

案例: nginx啓動腳本

[root@shell ~]# /usr/sbin/nginx           # nginx啓動方式
[root@shell ~]# /usr/sbin/nginx -s stop   # Nginx中止
[root@shell ~]# /usr/sbin/nginx -s reload # 重載
[root@shell ~]# /usr/sbin/nginx -s stop && /usr/sbin/nginx  # 先中止後啓動
[root@shell ~]# cat start_nginx.sh
#!/bin/sh
. /etc/init.d/functions
case $1 in
	start)
		if [ `ps axu|grep nginx|grep master|wc -l` -eq 0 ];then
		 
			/usr/sbin/nginx
				if [ $? -eq 0 ];then
				action "Nginx $1 is"  /bin/true
				else
				action "Nginx $1 is"  /bin/false
				fi
	   else
			action "Nginx $1 is 已經在運行" /bin/true
	   fi
	;;
	stop)
	   /usr/sbin/nginx -s stop
	;;	
	reload)
	  /usr/sbin/nginx -s reload
	;;
	restart)
	 /usr/sbin/nginx -s stop
	sleep 1
	/usr/sbin/nginx
	;;
	status)  
		if [ `ps axu|grep nginx|grep master|wc -l` -eq 0 ];then
		echo "Nginx is not running......"
		else
		pid=`ps axu|grep nginx|grep master|awk '{print $2}'`
		port=`netstat -tnulp|grep nginx|grep master|grep 80|awk '{print $4}'`
		echo "當前的Nginx端口號爲 $port"
		echo "當前的NgnxPID號爲: $pid"
		fi
	;;
	*)
	 echo "USAGE: $0 [start|stop|restart|reload|status]"
esac

交互腳本expect

1.簡單實現免交互登錄
expect是一款自動化的腳本解釋型的工具
腳本開頭
expect腳本通常以#!/usr/bin/expect 開頭,相似bash腳本。
經常使用後綴
expect腳本經常以.exp或者.ex結束。
expect主要命令
spawn 新建一個進程,這個進程的交互由expect控制
expect 等待接受進程返回的字符串,直到超時時間,根據規則決定下一步操做
send 發送字符串給expect控制的進程
set 設定變量爲某個值      set user oldboy
exp_continue 從新執行expect命令分支
[lindex $argv 0] 獲取expect腳本的第1個參數
[lindex $argv 1] 獲取expect腳本的第2個參數
set timeout -1 設置超時方式爲永遠等待
set timeout 30 設置超時時間爲30秒
interact 將腳本的控制權交給用戶,用戶可繼續輸入命令
expect eof 等待spawn進程結束後退出信號eof

安裝expect

yum -y install expect
#!/usr/bin/expect
spawn ssh root@10.0.0.5
expect {
    "yes/no" { send "yes\r"; exp_continue }
    "password:" { send "centos\r" };
} 
interact

2.expect定義變量實現交互方式

#!/usr/bin/expect
set ip 10.0.0.5
set user root
set password centos
set timeout 5

spawn ssh $user@$ip

expect {
    "yes/no" { send "yes\r"; exp_continue }
    "password:" { send "$password\r" };
} 

#交互方式 interact 案例

批量獲取在線主機, 進行祕鑰批量分發

cat for_ip.sh 
#!/usr/bin/bash
#setup1 拿到IP地址
>ip.txt
for i in {1..10}
do
ip=10.0.0.$i
{
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip" >> ip.txt
 fi
 }&
done
#2.生成對應的密鑰
if [ ! -f ~/.ssh/id_rsa ];then
ssh-keygen -P "" -f ~/.ssh/id_rsa
fi

#3.批量分發密鑰

 while read line
        do
/usr/bin/expect << EOF
                        set pass 1
                        set timeout 2
                        spawn ssh-copy-id  $line -f 
                        expect {
                                "yes/no" { send "yes\r"; exp_continue}
                                "password:" { send "1\r"}
                        }
                        expect eof 
EOF
done<ip.txt

for 循環

語法結構: for 變量 in 變量值 變量值能夠是數字 字母 變量 命令 do 執行的命令序列 具體的執行動做 能夠和變量無關 done

for.sh

for i in 框子(蘋果 梨子 香蕉 黃瓜)
do
	 echo $i 
	第一個循環:
	i=蘋果
	echo 蘋果
	第二次循環:
	i=梨子
	echo 梨子
	第四次循環
	i=黃瓜
	echo 黃瓜
done
[root@shell scripts]# cat for.sh 
for i in 1 10 20 30
do
	   echo $i
done
[root@shell scripts]# cat for.sh 
for i in `seq 10`
do
	   echo $i
done
[root@shell scripts]# cat for.sh
for i in `seq 5`
do
	   echo ok
done
案例: 數字從1加到100  筆試題
[root@shell scripts]# cat for.sh 
for i in `seq 100`
do
	   sum=$[$sum+$i]
done
echo $sum
[root@shell scripts]# cat for.sh
for i in $(seq 100)
do 
         sum=$(($sum+$i))  or   sum=$((sum+i))
done
echo $sum
[root@shell scripts]# seq -s + 100|bc
5050
擴展使用awk
[root@shell scripts]# seq 100|awk '{i=i+$1}END{print i}'
5050
[root@shell scripts]# echo {1..100}| awk -F " " '{for(i=1;i<NF+1;i++){j=j+i;}print j;}'
5050

案例: 批量pin 獲取在線的IP地址 能ping通說明在線 
10.0.0.0/24  10.0.0.1-10.0.0.254

[root@shell scripts]# cat ping.sh 
#!/bin/sh
for i in {1..254}
do
	{
	IP=10.0.0.$i
	ping -c2 -W1 $IP &>/dev/null
	if [ $? -eq 0 ];then
	   echo "當前 $IP 在線"
	fi
	} &
done
wait
echo "完成在線取IP.........."
案例: 批量建立10個用戶 用戶前綴名oldboy oldboy1 oldboy2 oldboy3 ... oldboy10
要求判斷用戶是否存在 不存在則建立 存在提示用戶已經存在  爲每一個用戶建立123456密碼 而且判斷密碼是否建立成功
使用隨機密碼 而且把用戶名和密碼統一輸出到文件中
[root@shell scripts]# vim user.sh 
#!/bin/sh
for i in {1..10}
do
		useradd oldboy$i
done
[root@shell scripts]# cat user.sh
#!/bin/bash
for i in {1..10}
do
      userdel -r oldboy$i
done

[root@shell scripts]# cat user1.sh
#!/bin/sh
for i in {1..10}
do
	id oldboy$i &>/dev/null
		if [ $? -eq 0 ];then
	   echo "useradd: user oldboy$i already exists"
	else
	useradd   oldboy$i
	   if [ $? -eq 0 ];then
	   echo "Create oldboy$i is ok"
	   else
	   echo "Create oldboy$i is error"
	   fi
	fi
done

[root@shell scripts]# cat user2.sh
#!/bin/bash
for i in {1..10}
do 
id oldboy$i &>/dev/null
 if [ $? -eq 0 ];then
    echo "用戶已經存在"
    else 
     useradd  oldboy$i
     if [ $? -eq 0 ];then
     echo "Create oldboy$i is ok"
     else
      echo "Create oldboy$i is error"
      fi
fi 
done
--------------------------
[root@shell scripts]# cat user.sh 
#!/bin/sh
> passwd.txt
for i in {1..10}
do
	pass=`date +%N|md5sum|cut -c1-8`
	id oldboy$i &>/dev/null
		if [ $? -eq 0 ];then
	   echo "useradd: user oldboy$i already exists"
	else
	useradd   oldboy$i
	   if [ $? -eq 0 ];then
	   echo "Create oldboy$i is ok"
			echo $pass|passwd --stdin oldboy$i &>/dev/null
			if [ $? -eq 0 ];then
			echo "設置密碼成功"
			else
			echo "設置密碼失敗"
			fi
		   echo  "oldboy$i $pass" >>passwd.txt
	   else
	   echo "Create oldboy$i is error"
	   fi
	fi
done
用戶的前綴手動輸入 建立的個數手動輸入 for循環 結合case語句方式批量建立用戶
[root@shell scripts]# cat user1.sh 
#!/bin/sh
read -p "請輸入用戶的前綴名稱: " prefix
[ -z $prefix ] && echo "請輸入用戶前綴" && exit
read -p "請輸入建立用戶的個數: " num
if [[ ! $num =~ ^[0-9]+$ ]];then
	echo "請輸入整數"
fi
for i in `seq $num`
do
	echo $prefix$i
done
read -p "你肯定要建立以上的用戶嗎?[y|Y|yes|n|N|no]: " re
for a in `seq $num`
do
		  user=$prefix$a
case $re in
	  y|Y|yes)
			 id $user &>/dev/null
		 if [ $? -eq 0 ];then
			echo "用戶存在"
		 else
			useradd $user &>/dev/null
			if [ $? -eq 0 ];then
			   echo "用戶建立成功"
			else
			   echo "用戶建立失敗"
			fi
		 fi
		 ;;
	   n|N|no)
		  exit
		;;
		   *)
		  echo "USAGE: $0 [請輸入前綴個數....]"

esac
done

while循環

語法格式: while 條件表達式 爲真則執行 不然不執行 do 命令序列 done

[root@shell scripts]# cat while.sh
while true
do
	 echo ok
	 sleep 2
done
[root@shell scripts]# cat while.sh 
a=0
while [ $a -le 3 ]
do
	 let a++
	 echo ok
done
[root@shell scripts]# sh while1.sh 
ok
ok
ok
ok
[root@shell scripts]# cat while1.sh
a=1
while [ $a -le 3 ]
do 
  let a++
       echo ok
done
[root@shell scripts]# sh while1.sh 
ok
ok
ok
使用while循環從1加到100
[root@shell scripts]# cat while.sh 
a=1
while [ $a -le 100 ]
do
	 sum=$[$sum+$a]
	 let a++
done
echo $sum
while讀取文件
	  for循環取值方式: 按照空格取值
		  [root@shell scripts]# cat test.sh
			#!/bin/sh
			for i in `cat /etc/hosts`
			do
				echo $i
			done
	  while循環取值方式: 按照行取值
		  while read line
			do
				echo $line
			done</etc/hosts
案例: 經過讀取文件的方式建立用戶名
    user.txt 
	zs1
	old2
	tes1
	test2
	lisi
[root@shell scripts]# cat while.sh 
#!/bin/sh
while read line
do
		user=`echo $line|awk '{print $1}'`
	pass=`echo $line|awk '{print $2}'`
	useradd $user
	echo $pass|passwd --stdin $user
done<user.txt

流程控制語句 exit 退出當前的腳本 break 跳出本層循環 continue 省略當前循環的後面的代碼.從頭開始執行

#!/bin/sh
while true
do
		echo ok
		continue
		echo hehe
done
echo done...............

exit 退出當前整個腳本
[root@shell scripts]# cat while2.sh
#!/bin/sh
for i in `seq 10`
do
	useradd oldboy$i &>/dev/null
		if [ $? -eq 0 ];then
		echo "Create oldboy$i success"
		else
		exit
	fi
done
break 跳出本層循環 
[root@shell scripts]# cat while2.sh
#!/bin/sh
for i in `seq 10`
do
	useradd oldboy$i &>/dev/null
		if [ $? -eq 0 ];then
		echo "Create oldboy$i success"
		else
		break
	fi
done
echo done................
[root@shell scripts]# sh while2.sh 
Create oldboy1 success
Create oldboy2 success
Create oldboy3 success
Create oldboy4 success
done................
 continue
   [root@shell scripts]# cat while2.sh 
	#!/bin/sh
	for i in `seq 10`
	do
		useradd oldboy$i &>/dev/null
			if [ $? -eq 0 ];then
			echo "Create oldboy$i success"
			else
			continue
		fi
	done
	echo done................
  [root@shell scripts]# sh while2.sh 
	Create oldboy1 success
	Create oldboy2 success
	Create oldboy3 success
	Create oldboy4 success
	Create oldboy6 success
	Create oldboy7 success
	Create oldboy8 success
	Create oldboy9 success
	Create oldboy10 success
	done................

fun函數

1.命令的集合,完成特定功能的代碼塊 ​ 2.便於代碼複用 3.和變量相似 只能先定義 在調用,只定義不調用 不執行 函數的定義: [root@shell scripts]# cat fun.sh #!/bin/sh fun(){ echo "函數的第一種定義方式" }

function fun1(){
	echo "函數的第二種定義方式"
}

function fun2 {
	echo "函數的第三種定義方式"
}
fun
fun1
fun2
[root@shell scripts]# sh fun.sh 
函數的第一種定義方式
函數的第二種定義方式
函數的第三種定義方式

函數的傳參 函數的傳參第一種方式 在函數的後面調用

[root@shell scripts]# cat fun1.sh
	#!/bin/sh
	fun(){
		if [ -f $1 ];then
		  echo "文件存在"
		else
		  echo "文件不存在"
		fi
	}
	fun /etc/hosts
	[root@shell scripts]# sh -x fun1.sh /etc/passwd 
	+ fun /etc/hosts
	+ '[' -f /etc/hosts ']'
	+ echo 文件存在
	文件存在
[root@shell scripts]# cat fun1.sh
#!/bin/sh
fun(){
	if [ -f $1 ];then
	  echo "$1 文件存在"
	else
	  echo "$1 文件不存在"
	fi
}
fun $2
[root@shell scripts]# sh fun1.sh /etc/passwd /etc/hosts
/etc/hosts 文件存在

-------------------------
[root@shell scripts]# cat fun1.sh
#!/bin/sh
fun(){
	if [ -f $1 ];then
	  echo "$1 文件存在"
	else
	  echo "$1 文件不存在"
	fi
}
fun $2 $1
[root@shell scripts]# sh fun1.sh /etc/passwd /etc/hosts
/etc/hosts 文件存在

[root@shell scripts]# cat fun1.sh
#!/bin/sh
fun(){
	if [ -f $2 ];then
	  echo "$2 文件存在"
	else
	  echo "$2 文件不存在"
	fi
}
fun $2 $1
[root@shell scripts]# sh fun1.sh /etc/passwd /etc/hosts
/etc/passwd 文件存在
函數傳參的第二種方式:
    調用當前shell中的變量
[root@shell scripts]# cat fun1.sh
#!/bin/sh
file=$1
fun(){
	if [ -f $file ];then
	  echo "$file 文件存在"
	else
	  echo "$file 文件不存在"
	fi
}
fun

函數的本地變量: 只能在函數體中生效 在其餘的shell語句中不生效
local file=/etc/hosts
[root@shell scripts]# vim fun1.sh 
#!/bin/sh
fun(){
	local file=/etc/hosts
	if [ -f $file ];then
	  echo "$file 文件存在"
	else
	  echo "$file 文件不存在"
	fi
}
fun
echo $file
案例:
[root@shell scripts]# cat fun2.sh 
#!/bin/sh
num1=10
fun(){
	for i in `seq 10`
	do
	total=$[$i+$num1]
	done
}
fun
echo $total
[root@shell scripts]# sh fun2.sh
20	

注意變量位置
[root@shell scripts]# cat fun2.sh
#!/bin/sh
fun(){
	for i in `seq 10`
	do
	total=$[$i+$num1]
	done
}
num1=10
fun
echo $total
[root@shell scripts]# sh fun2.sh
20
[root@shell scripts]# cat fun2.sh
#!/bin/sh
fun(){
	for i in `seq 10`
	do
	total=$[$i+$1]
	done
	echo $total
}
fun $1
fun $2
fun $3
[root@shell scripts]# sh fun2.sh 10 20 30
20
30
40
 [root@shell scripts]# cat fun2.sh 
  #!/bin/sh
  fun(){
  for i in `seq 10`
  do
  total=$[$i+$1]
  done
  echo $total
  }
  fun $2 $1 $3
  [root@shell scripts]# sh fun2.sh 20 30 10
  40
 
 函數的返回值: return
    [root@shell scripts]# cat fun1.sh
  #!/bin/sh
  file=$1
  fun(){
  if [ -f $file ];then
   return 50
  else
   return 100
  fi
  }
  fun
  echo $?
  [root@shell scripts]# sh fun1.sh /etc/hostssss
  100
  [root@shell scripts]# sh fun1.sh /etc/hosts
  50
 
    [root@shell scripts]# cat fun1.sh
  #!/bin/sh
  file=$1
  fun(){
  if [ -f $file ];then
   return 50
  else
   return 100
  fi
  }
  fun
  if [ $? -eq 50 ];then
  echo "文件存在"
  else
  echo "文件不存在"
  fi
  [root@shell scripts]# cat fun1.sh
  #!/bin/sh
  file=$1
  fun(){
   echo 50
   return 100
  }
  num=`fun`
  echo "當前函數的返回值爲: $?"
  echo "當前函數的執行結果爲: $num"
  [root@shell scripts]# sh fun1.sh /etc/hosts
  當前函數的返回值爲: 100
  當前函數的執行結果爲: 50
  [root@shell scripts]# cat fun1.sh
  #!/bin/sh
  file=$1
  fun(){
   echo 50
   return 100
  }
  num=`fun`
  name=oldboy
  echo "當前函數的返回值爲: $?"
  echo "當前函數的執行結果爲: $num"
  [root@shell scripts]# sh fun1.sh /etc/hosts
  當前函數的返回值爲: 0
  當前函數的執行結果爲: 50
 
 函數在其餘shell中調用 相似變量
    [root@shell scripts]# cat fun.sh
  /bin/sh
  fun(){
  echo "函數的第一種定義方式"
  }
 function fun1(){
  echo "函數的第二種定義方式"
 }
 
 function fun2 {
  echo "函數的第三種定義方式"
 }
 [root@shell scripts]# fun
 -bash: fun: command not found
 [root@shell scripts]# . fun.sh
 [root@shell scripts]# fun
 函數的第一種定義方式
 [root@shell scripts]# fun1
 函數的第二種定義方式
 [root@shell scripts]# fun2
 函數的第三種定義方式

數組

1.數組的分類

普通數組

只能使用整數來定義

關聯數組

可以使用數字和字符來定義

變量:存儲單個元素的內存空間
數組:存儲多個元素的連續的內存空間,至關於多個變量的集合
數組名和索引
索引:編號從0開始,屬於數值索引
注意:索引可支持使用自定義的格式,而不只是數值格式,即爲關聯索引,bash4.0版本以後開始支持
bash的數組支持稀疏格式(索引不連續)
聲明數組:
declare -a ARRAY_NAME
declare -A ARRAY_NAME 關聯數組
注意:二者不可相互轉換
高級變量用法-有類型變量
Shell變量通常是無類型的,可是bash Shell提供了declare和typeset兩個命令用於指定變量的類型,兩個命令是等價的
declare [選項] 變量名
-r 聲明或顯示只讀變量
-i 將變量定義爲整型數
-a 將變量定義爲數組
-A 將變量定義爲關聯數組
-f 顯示已定義的全部函數名及其內容
-F 僅顯示已定義的全部函數名
-x 聲明或顯示環境變量和函數
-l 聲明變量爲小寫字母 declare –l var=UPPER
-u 聲明變量爲大寫字母 declare –u var=lower

間接變量引用

bash Shell提供了兩種格式實現間接變量引用
eval tempvar=\$$variable1
tempvar=${!variable1}
示例:
[root@server ~]# N=NAME
[root@server ~]# NAME=wangxiaochun
[root@server ~]# N1=${!N}
[root@server ~]# echo $N1
wangxiaochun
[root@server ~]# eval N2=\$$N
[root@server ~]# echo $N2
wangxiaochun

建立臨時文件

mktemp命令:建立並顯示臨時文件,可避免衝突
mktemp [OPTION]... [TEMPLATE]
TEMPLATE: filenameXXX
X至少要出現三個
OPTION:
-d: 建立臨時目錄
-p DIR或--tmpdir=DIR:指明臨時文件所存放目錄位置
示例:
mktemp /tmp/testXXX
tmpdir=`mktemp –d /tmp/testdirXXX`
mktemp --tmpdir=/testdir testXXXXXX

安裝複製文件

install命令:
install [OPTION]... [-T] SOURCE DEST 單文件
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY...建立空目錄
選項:
-m MODE,默認755
-o OWNER
-g GROUP
示例:
install -m 700 -o wang -g admins srcfile desfile
install –m 770 –d /testdir/installdir

2.數組的格式

變量名稱=變量的值 數組名稱[索引]=(元素的值)

3.定義數組

第一種定義方式

索引定義

默認索引從0開始

  [root@shell scripts]# array[1]=cm
	[root@shell scripts]# array[5]=zs
	[root@shell scripts]# array[6]=ls

第二種定義方式

一次性定義多個值

 [root@shell scripts]# array=(ll lz lw lz)

第三種定義方式

混合定義的方式

 [root@shell scripts]# array=(qq aa cc [20]=tt [22]=yy)

第四種定義方式

命令定義

  [root@shell scripts]# ls /tmp/
	nging.pid  vmware-root_6223-1681855427
	[root@shell scripts]# array=(`ls /tmp`)

4.調用數組 declare -a 查看當前定義全部的普通數組

 [root@shell scripts]# echo $array    # 默認只能查看下標爲0的值
 mysql
 [root@shell scripts]#
 [root@shell scripts]# echo ${array[*]} # 查看數組中的全部內容
 mysql shell redis
 [root@shell scripts]# echo ${array[@]}
 mysql shell redis
 [root@shell scripts]# echo ${!array[@]} # 查看全部的下標
 0 1 2
 [root@shell scripts]# echo ${array[1]} # 經過下標查看值
 shell
 [root@shell scripts]# echo ${array[2]}
 redis
 [root@shell scripts]# echo ${array[0]}
 mysql
 [root@shell scripts]# echo ${#array[*]} # 查看元素的個數
 3
 案例: 判斷當前公司中幾臺服務器的IP地址是否能ping通

61.135.169.121 123.126.55.41 10.0.0.254 8.8.8.8

定義數組:

 [root@shell scripts]# IP=(61.135.169.121 123.126.55.41 10.0.0.254 8.8.8.8)

獲取數組的值:

  [root@shell scripts]# echo ${IP[*]}
    61.135.169.121 123.126.55.41 10.0.0.254 8.8.8.8

for循環方式去ping每一個IP

  [root@shell scripts]# for i in ${IP[*]};do echo $i;done
  61.135.169.121
  123.126.55.41
  10.0.0.254
  8.8.8.8
  [root@shell scripts]# for i in ${IP[*]};do ping -c2 -W1 $i;done
  [root@shell scripts]# cat array.sh
  #!/bin/sh
  IP=(
  61.135.169.121
  123.126.55.41
  10.0.0.254
  8.8.8.8
  10.0.0.51
  )
  for i in ${IP[*]}
  do
  ping -c2 -W1 $i &>/dev/null
  if [ $? -eq 0 ];then
    echo "ping $i is ok............"
  else
    echo "ping $i is error........."
  fi
  done

關聯數組: 下標可使用數字和字符串 聲明關聯數組: declare -A 數組名稱 使用下標方式定義數組

	[root@shell scripts]# array[index1]=redis
	[root@shell scripts]# array[index2]=mysql
	[root@shell scripts]# array[index3]=shell
	[root@shell scripts]# echo ${array[*]}
	redis mysql shell
第二種方式定義數組
[root@shell scripts]# array=([0]=zabbix [index4]=kvm [index5]=mongo)
[root@shell scripts]# echo $array[*]
zabbix[*]
[root@shell scripts]# echo ${array[*]}
kvm mongo zabbix
[root@shell scripts]# echo ${!array[*]}
index4 index5 0

關聯數組案例: 統計文件中性別出現的次數

 cat sex.txt
 m
 f
 f
 m
 m
 m
 x
 [root@shell scripts]# sort sex.txt|uniq -c
 2 f
 4 m
 1 x
 [root@shell scripts]# sort sex.txt|uniq -c|sort -rn
 4 m
 2 f
 1 x
 [root@shell scripts]# cat sex.txt |uniq -c|sort -rn
 3 m
 2 f
 1 x
 1 m
 [root@shell scripts]# cat sex.txt |sort
 f
 f
 m
 m
 m
 m
 x
 [root@shell scripts]# cat sex.txt |sort |uniq -c
 2 f
 4 m
 1 x
 [root@shell scripts]# cat sex.txt |sort |uniq -c|sort -rn
 4 m
 2 f
 1 x
 [root@shell scripts]# cat s.sh
 for i in `cat sex.txt`
 do
 if [ $i = m ];then
 let m++
 elif [ $i = f ];then
 let f++
 else
 let x++
 fi
 done
 echo "m出現了 $m 次"
 echo "f出現了 $f 次"
 echo "x出現了 $x 次"
 [root@shell scripts]# sh s.sh
 m出現了 4 次
 f出現了 2 次
 
 x出現了 1 次
 --------------------------
 
  [root@shell scripts]# let a++
  [root@shell scripts]# echo $a
  1
  [root@shell scripts]# let array[b]++
  [root@shell scripts]# echo ${array[b]}
  1
  [root@shell scripts]# let a++
  [root@shell scripts]# let b++
  [root@shell scripts]# let b++
  [root@shell scripts]# let a++
  [root@shell scripts]# let array[b]++
  [root@shell scripts]# let array[a]++
  [root@shell scripts]# let array[a]++
  [root@shell scripts]#
  [root@shell scripts]# echo $a
  3
  [root@shell scripts]# echo ${array[b]}
  2
  [root@shell scripts]# echo ${array[a]}
 
  2
 -----------------------------
 
    cat s.sh # 執行過程
    declare -A sex
  for i in `cat sex.txt`
  do
    let sex[$i]++
 
 --------------
 
    i=m
    let sex[m]++
 
 ---
 
    i=f
    let sex[f]++
 
 ---
 
    i=m
    let sex[m]++
  done
  echo "m出現了 $m 次"
  echo "f出現了 $f 次"
  echo "x出現了 $x 次"
  [root@shell scripts]# cat s.sh
  declare -A sex
  for i in `cat sex.txt`
  do
    let sex[$i]++
  done
  echo "m出現了 ${sex[m]} 次"
  echo "f出現了 ${sex[f]} 次"
  echo "x出現了 ${sex[x]} 次"
 
 •   [root@shell scripts]# sh s.sh
 •    m出現了 4 次
 •    f出現了 2 次
 •    x出現了 1 次
 
    遍歷數組:
    [root@shell scripts]# cat s.sh
  declare -A sex
  for i in `cat sex.txt`
  do
    let sex[$i]++
  done
  #echo "m出現了 ${sex[m]} 次"
  #echo "f出現了 ${sex[f]} 次"
  #echo "x出現了 ${sex[x]} 次"
  for a in ${!sex[*]}
  do
  echo "$a 出現了 ${sex[$a]} 次"
  done
  [root@shell scripts]# sh s.sh
  f 出現了 2 次
  m 出現了 4 次
  x 出現了 1 次
 案例: 統計/etc/passwd 中出現的解釋器
  declare -A bash # 執行過程
  while read line
  do
    let bash[`echo $line|awk -F: '{print $NF}'`]++
    root:x:0:0:root:/root:/bin/bash
    let bash[/bin/bash]++
  bin:x:1:1:bin:/bin:/sbin/nologin 
  let bash[/sbin/nologin]++ 
 
done</etc/passwd 
for a in ${!bash[*]} 
do 
echo "$a 出現了 ${bash[$a]} 次" 
done 
[root@shell scripts]# cat bash.sh  
declare -A bash 
while read line 
do 
  let bash[`echo $line|awk -F: '{print $NF}'`]++ 
done</etc/passwd 
for a in ${!bash[*]} 
do 
echo "$a 出現了 ${bash[$a]} 次" 
done 
[root@shell scripts]# sh bash.sh 
/sbin/nologin 出現了 21 次 
/bin/sync 出現了 1 次 
/bin/bash 出現了 26 次 
/sbin/shutdown 出現了 1 次 
/sbin/halt 出現了 1 次

統計當前nginx日誌文件中IP地址出現的次數

  [root@shell scripts]# cat ip.sh
  #!/bin/sh
  declare -A IP
  while read line
  do
  let IP[`echo $line|awk '{print $1}'`]++
  done</var/log/nginx/access.log
  for i in ${!IP[*]}
  do
  echo "$i 出現了 ${IP[$i]} 次"
  done

數組的刪除

 引用數組
 引用數組元素
 ${ARRAY_NAME[INDEX]}
 注意:省略[INDEX]表示引用下標爲0的元素
 引用數組全部元素
 ${ARRAY_NAME[*]}
 ${ARRAY_NAME[@]}
 數組的長度(數組中元素的個數)
 ${#ARRAY_NAME[*]}
 ${#ARRAY_NAME[@]}
 除數組中的某元素:致使稀疏格式
 unset ARRAY[INDEX]
 刪除整個數組
 unset ARRAY
 [root@localhost ~]# unset array[1]
 [root@localhost ~]# echo ${array[*]}
 one three four
 [root@localhost ~]# unset array
 [root@localhost ~]# echo ${array[*]}

數組內容截取和替換

數組數據處理 引用數組中的元素:

 數組切片:
 ${ARRAY[@]:offset:number}
 offset 要跳過的元素個數
 number 要取出的元素個數
 取偏移量以後的全部元素 ${ARRAY[@]:offset}
 向數組中追加元素:
 ARRAY[${#ARRAY[*]}]=value
 聯數組:
 declare -A ARRAY_NAME
 ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)
 注意:關聯數組必須先聲明再調用
    [root@localhost ~]# array=(0 1 2 3 4)
    [root@localhost ~]# echo ${array[@]:1:3}
    1 2 3
    [root@localhost ~]# array=($(echo {a..z}))
    [root@localhost ~]# echo ${array[@]}
    a b c d e f g h i j k l m n o p q r s t u v w x y z
    [root@localhost ~]# echo ${array[@]:1:3}
    b c d
    [root@localhost ~]# array=(1 2 3 4 5)
    [root@localhost ~]# echo ${array[@]/3/three}
    1 2 three 4 5

字符串增刪改查匹配處理

基於模式取子串
${var#*word}:其中word能夠是指定的任意字符
功能:自左而右,查找var變量所存儲的字符串中,第一次出現的word, 刪除字符串開頭至第一次出現word字符串(含)之間的全部字符
${var##*word}:同上,貪婪模式,不一樣的是,刪除的是字符串開頭至最後一次由word指定的字符之間的全部內容
示例:
file=「var/log/messages」
${file#*/}: log/messages
${file##*/}: messages
 ${var%word*}:其中word能夠是指定的任意字符
功能:自右而左,查找var變量所存儲的字符串中,第一次出現的word, 刪除字符串最後一個字符向左至第一次出現word字符串(含)之間的全部字符
file="/var/log/messages"
${file%/*}: /var/log
${var%%word*}:同上,只不過刪除字符串最右側的字符向左至最後一次出現word字符之間的全部字符
示例:
url=http://www.magedu.com:80
${url##*:} 80
${url%%:*} http       
 查找並刪除
${var/pattern}:刪除var表示的字符串中第一次被pattern匹配到的字符串
${var//pattern}:刪除var表示的字符串中全部被pattern匹配到的字符串
${var/#pattern}:刪除var表示的字符串中全部以pattern爲行首匹配到的字符串
${var/%pattern}:刪除var所表示的字符串中全部以pattern爲行尾所匹配到的字符串
字符大小寫轉換
${var^^}:把var中的全部小寫字母轉換爲大寫
${var,,}:把var中的全部大寫字母轉換爲小寫            

shell正則應用

第1章 shell正則應用

1.1 基礎正則表達式

1.2 grep正則表達式實戰

1.2.1 過濾以m開頭的行

1.2.2 過濾以m結尾的行

1.2.3 排除空行, 並打印行號

1.2.4 匹配任意一個字符,不包括空行

1.2.5 匹配全部

1.2.6 匹配單個任意字符

1.2.7 以點結尾

1.2.8 精確匹配到

[1.2.9 匹配有abc的行

[1.2.10 匹配數字所在的行"0-9]"

[1.2.11 匹配全部小寫字母a-z]

1.2.12 重複8三次

1.2.13 重複數字8, 3-5次

1.2.14 至少1次或1次以上

1.3 sed文本處理

1.3.1 sed命令格式

1.3.2 sed正則使用

1.3.3 sed選項參數

1.3.4 sed`內置命令參數

1.3.5 先刪除行,而後管道給後面的sed進行替換

1.3.6 使用-e進行屢次編輯修改操做

1.3.7 打印命令p.

1.3.8 追加命令a

1.3.9 修改命令c

1.3.10 刪除命令d

1.3.11 插入命令i

1.3.12 寫文件命令w

1.3.13 獲取下一行命令n

1.3.14 暫存和取用命令h H g G

1.3.15 反向選擇命令!

1.3.16 sed匹配替換...

1.3.17 刪除文件...

1.3.18 給文件行添加註釋...

1.3.19 添加#註釋符...

1.4 Awk文本處理...

1.4.1 awk 的處理數據的方式...

1.4.2 awk的語法格式...

1.4.3 選項 options.

1.4.4 awk命令格式...

1.4.5 Awk工做原理...

1.4.6 Awk內部變量...

1.4.7 Awk模式動做...

1.4.8 awk示例1.

1.4.9 awk示例2.

[1.4.10 Awk條件判斷... ]

1.4.11 Awk循環語句...

1.4.12 awk數組概述...

[1.4.13 Awk數組案例... ]

第1章 shell正則應用

正則表達式regular expression, RE是一種字符模式,用於在查找過程當中匹配指定的字符。

在大多數程序裏,正則表達式都被置於兩個正斜槓之間;例如/l[oO]ve/就是由正斜槓界定的正則表達式,它將匹配被查找的行中任何位置出現的相同模式。在正則表達式中,元字符是最重要的概念。

正則表達式的做用

>1.Linux正則表達式grep,sed,awk

2.大量的字符串文件須要進行配置,並且是非交互式的

3.過濾相關的字符串,匹配字符串,打印字符串

正則表達式注意事項

>1.正則表達式應用很是普遍,存在於各類語言中,例如:php,python,java等。

2.正則表達式和通配符特殊字符是有本質區別的。

3.要想學好grep、sed、awk首先就要掌握正則表達式。

1.1 基礎正則表達式

元字符意義BRE,正則表達式實際就是一些特殊字符,賦予了他特定的含義。

正則表達式 描述

\    轉義符,將特殊字符進行轉義,忽略其特殊意義

^    匹配行首,awk中,^則是匹配字符串的開始 

$    匹配行尾,awk中,$則是匹配字符串的結尾

^$   表示空行

.    匹配除換行符\n以外的任意單個字符

.*   匹配全部

[ ]   匹配包含在[字符]之中的任意一個字符

[^ ]  匹配[^字符]以外的任意一個字符

[ - ]  匹配[]中指定範圍內的任意一個字符

?    匹配以前的項1次或者0次

\+    匹配以前的項1次或者屢次

\*    匹配以前的項0次或者屢次, .*

()   匹配表達式,建立一個用於匹配的子串

{ n }  匹配以前的項n次,n是能夠爲0的正整數

{n,}  以前的項至少須要匹配n次

{n,m}  指定以前的項至少匹配n次,最多匹配m次,n<=m

|    交替匹配|兩邊的任意一項ab(c|d)匹配abc或abd

特定字符:

>[[:space:]]  空格
[[:digit:]]   [0-9]
[[:lower:]]   [a-z]
[[:upper:]]   [A-Z]
[[:alpha:]]   [a-Z]

1.2 grep正則表達式實戰

I am lizhenya teacher!
I teach linux.
test

I like badminton ball ,billiard ball and chinese chess!
my blog is http: blog.51cto.com
our site is http:www.lizhenya.com
my qq num is 593528156
not 572891888887. 

1.2.1 過濾以m開頭的行

[root@Shell ~]#** grep "^m" test.txt

my blog is http: blog.51cto.com

my qq num is 572891887.

1.2.2 過濾以m結尾的行

[root@Shell ~]#** grep "m$" test.txt

my blog is http: blog.51cto.com

our site is http:www.lizhenya.com

1.2.3 排除空行, 並打印行號

[root@student ~]#grep -vn "^$" lizhenya.txt

1.2.4 匹配任意一個字符,不包括空行

[root@student ~]# grep "." lizhenya.txt

1.2.5 匹配全部

[root@student ~]# grep ".*" lizhenya.txt

1.2.6 匹配單個任意字符

 [root@node1 ~]# grep "lizhen.a" lizhenya.txt

1.2.7 以點結尾的

 [root@student ~]# grep "\.$" lizhenya.txt

1.2.8 精確匹配到

 [root@student ~]# grep -o "8*" lizhenya.txt

1.2.9 匹配有abc的行

 [root@student ~]# grep "[abc]" lizhenya.txt

1.2.10 匹配數字所在的行"[0-9]"

 [root@student ~]# grep "[0-9]" lizhenya.txt

1.2.11 匹配全部小寫字母[a-z]

 [root@student ~]# grep "[a-z]" lizhenya.txt

1.2.12 重複8三次

 [root@student ~]# grep "8\{3\}" lizhenya.txt

1.2.13 重複數字8, 3-5次

 [root@student ~]# grep -E "8{3,5}" test.txt

1.2.14 至少1次或1次以上

 [root@student ~]# grep -E "8{1,}" lizhenya.txt

1.3 sed文本處理

sed是一個流編輯器, 非交互式的編輯器,它一次處理一行內容.

處理時,把當前處理的行存儲在臨時緩衝區中,稱爲「模式空間」(pattern space)

接着用 sed 命令處理緩衝區中的內容,處理完成後, 把緩衝區的內容送往屏幕。

接着處理下一行,這樣不斷重複,直到文件末尾。

文件內容並無改變,除非你 使用重定向存儲輸出。

Sed 要用來自動編輯一個或多個文件;簡化對文件的反覆操做;編寫轉換程序等。

1.3.1 sed命令格式

sed [options] 'command' file(s)

1.3.2 sed正則使用

與 grep同樣,sed 在文件中查找模式時也可使用正則表達式(RE)和各類元字符。

正則表達式是括在斜槓間的模式,用於查找和替換,如下是sed支持的元字符。

使用基本元字符集 ^, $, ., *, [], [^], < >, (), {}

使用擴展元字符集 ?, +, { }, |, ( )

使用擴展元字符的方式 + sed -r

1.3.3 sed選項參數

-e 容許多項編輯

-n 取消默認的輸出

-i 直接修改對應文件

-r 支持擴展元字符

1.3.4 sed`內置命令參數

a 在當前行後添加一行或多行

c 在當前行進行替換修改

d 在當前行進行刪除操做

i 在當前行以前插入文本

p 打印匹配的行或指定行

n 讀入下一輸入行,從下一條命令進行處理

! 對所選行之外的全部行應用命令

h 把模式空間裏的內容重定向到暫存緩衝區

H 把模式空間裏的內容追加到暫存緩衝區

g 取出暫存緩衝區的內容,將其複製到模式空間,覆蓋該處原有內容

G 取出暫存緩衝區的內容,將其複製到模式空間,追加在原有內容後面

1.3.5 先刪除行,而後管道給後面的sed進行替換

[root@Shell ~]# sed '1,9d' passwd |sed 's#root#alex#g'

1.3.6 使用-e進行屢次編輯修改操做

[root@Shell ~]# sed -e '1,9d' -e 's#root#alex#g' passwd

1.3.7 打印命令p

1.3.7.1 打印匹配halt的行

[root@Shell ~]# sed -n '/halt/p' passwd

1.3.7.2 打印第二行的內容

[root@Shell ~]#** sed -n '2p' passwd
bin:x:1:1:bin:/bin:/sbin/nologin

1.3.7.3 打印最後一行

[root@Shell ~]# sed -n '$p' passwd

1.3.8 追加命令a

1.3.8.1 給30行添加配置 \t tab鍵(須要轉義) \n 換行符

[root@Shell ~]# sed -i '30a listen 80;' passwd

1.3.9 修改命令c

1.3.9.1 指定某行進行內容替換

[root@Shell ~]# sed -i '7c SELINUX=Disabled' /etc/selinux/config

1.3.9.2 正則匹配對應內容, 而後進行替換

[root@Shell ~]# sed -i '/^SELINUX=/c SELINUX=Disabled' /etc/selinux/config

1.3.9.3 非交互式修改指定的配置文件

[root@Shell ~]# sed -ri '/UseDNS/cUseDNS no' /etc/ssh/sshd_config
[root@Shell ~]# sed -ri '/GSSAPIAuthentication/c#GSSAPIAuthentication no' /etc/ssh/sshd_config
[root@Shell ~]# sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config

1.3.10 刪除命令d

1.3.10.1 指定刪除第三行, 但不會改變文件內容

[root@Shell ~]# sed '3d' passwd
[root@Shell ~]# sed '3{d}' passwd

1.3.10.2 從第三行刪除到最後一行

[root@Shell ~]# sed '3,$d' passwd

1.3.10.3 刪除最後一行

[root@Shell ~]# sed '$d' passwd

1.3.10.4 刪除全部的行

[root@Shell ~]# sed '1,$d' passwd

1.3.10.5 匹配正則進行該行刪除

[root@Shell ~]# sed /mail/d passwd

1.3.11 插入命令i

1.3.11.1 在文件的某一行上面添加內容

[root@Shell ~]# sed -i '30i listen 80;' passwd

1.3.12 寫文件命令w

1.3.12.1 將匹配到的行寫入到新文件中

[root@Shell ~]# sed -n '/root/w newfile' passwd

1.3.12.2 將passwd文件的第二行寫入到newfile中

[root@Shell ~]# sed -n '2w newfile' passwd

1.3.13 獲取下一行命令n

1.3.13.1 匹配root的行, 刪除root行的下一列

[root@Shell ~]# sed '/root/{n;d}' passwd

1.3.13.2 替換匹配root行的下一列

[root@Shell ~]# sed '/root/{n; s/bin/test/}' passwd

1.3.14 暫存和取用命令h H g G

g: 將hold space中的內容拷貝到pattern space中,原來pattern space裏的內容被覆蓋
G:將hold space中的內容append到pattern space\n後
h: 將pattern space中的內容拷貝到hold space中,原來hold space裏的內容被覆蓋
H: 將pattern space中的內容append到hold space\n後
d: 刪除pattern中的全部行,並讀入下一新行到pattern中
D: 刪除multiline pattern中的第一行,讀入下一行

1.3.14.1 將第一行的寫入到暫存區, 替換最後一行的內容

[root@Shell ~]# sed '1h;$g' /etc/hosts

1.3.14.2 將第一行的寫入到暫存區, 在最後一行調用暫存區的內容

[root@Shell ~]# sed '1h;$G' /etc/hosts

1.3.14.3 將第一行的內容刪除但保留至暫存區, 在最後一行調用暫存區內容追加至於尾部

[root@Shell ~]# sed -r '1{h;d};$G' /etc/hosts

QQ圖片20201022091844

1.3.14.4 將第一行的內容寫入至暫存區, 從第二行開始進行重定向替換

[root@Shell ~]# sed -r '1h;2,$g' /etc/hosts

1.3.14.5 將第一行重定向至暫存區, 2-3行追加至暫存區, 最後追加調用暫存區的內容

[root@Shell ~]# sed -r '1h; 2,3H; $G' /etc/hosts

4 圖解sed ‘1!G;h;$!d’ file

1 sed簡介

sed是面向流的行編輯器。所謂面向流,是指接受標準輸入的輸入,輸出內容到標準輸出上。sed編輯器逐行處理文件(或輸入),並將結果發送到屏幕。

具體過程以下: sed將處理的行讀入到一個臨時緩存區中(也稱爲模式空間pattern space),sed中的命令依次執行,直到全部命令執行完畢,完成後把該行發送到屏幕上,清理pattern space中的內容;接着重複剛纔的動做,讀入下一行,直到文件處理結束。

sed每處理完一行就將其從pattern space中刪除,而後將下一行讀入,進行處理和顯示。處理完輸入 文件的最後一行後,sed便結束運行。sed把每一行都存在臨時緩衝區中,對這個副本進行編輯,因此不會修改原文件。

2 sed執行流程圖

image-20201022145834344

3 什麼是Pattern Space,Hold Space

Pattern Space至關於車間,sed把流內容在這裏進行處理,Hold Space至關於倉庫,加工的半成品在這裏進行臨時存儲。

因爲各類緣由,好比用戶但願在某個條件下腳本中的某個命令被執行,或者但願模式空間獲得保存以便下一次處理,都有可能使得sed在處理文件的時候不按照正常的流程來進行。這個時候,sed設置了一些高級命令來知足用戶的要求。

一些高級命令

g: 將hold space中的內容拷貝到pattern space中,原來pattern space裏的內容被覆蓋

G:將hold space中的內容append到pattern space\n後

h: 將pattern space中的內容拷貝到hold space中,原來hold space裏的內容被覆蓋

H: 將pattern space中的內容append到hold space\n後

d: 刪除pattern中的全部行,並讀入下一新行到pattern中

D: 刪除multiline pattern中的第一行,不讀入下一行

4 圖解sed ‘1!G;h;$!d’ file

1!G 第一行不執行G命令,從第二行開始執行

$!d 最後一行不刪除

[root@localhost test]# cat file
1 1 1
2 2 2
3 3 3
[root@localhost test]# sed '1!G;h;$!d' file
3 3 3
2 2 2
1 1 1

圖中P表明Pattern Space,H表明Hold Space。綠色表明pattern space中的數據,藍色表明hold space中的數據。

1.3.15 反向選擇命令!

1.3.15.1 除了第三行,其餘所有刪除

[root@Shell ~]# sed -r '3!d' /etc/hosts

1.3.16 sed匹配替換

s 替換命令標誌

g 行內全局替換

i 忽略替換大小寫

替換命令s

1.3.16.1 替換每行出現的第一個root

[root@Shell ~]# sed 's/root/alice/' passwd

1.3.16.2 替換以root開頭的行

[root@Shell ~]# sed 's/^root/alice/' passwd

1.3.16.3 查找匹配到的行, 在匹配的行後面添加內容

[root@Shell ~]# sed -r 's/[0-9][0-9]$/& .5/' passwd

1.3.16.4 匹配包含有root的行進行替換

[root@Shell ~]# sed -r 's/root/alice/g' passwd

1.3.16.5 匹配包含有root的行進行替換,忽略大小寫

[root@Shell ~]# sed -r 's/root/alice/gi' /etc/passwd

1.3.16.6 後向引用

[root@Shell ~]# sed -r 's#(Roo)#\1-alice#g' passwd
[root@Shell ~]# ifconfig eth0|sed -n '2p'|sed -r 's#(^.*et) (.*) (net.*$)#\2#g'

示例文件內容以下:

[root@lzy ~]# vim a.txt
/etc/abc/456
etc

刪除文本中的內容,需加轉義

[root@Shell ~]# sed -r '\/etc\/abc\/456/d' a.txt

若是碰到/符號, 建議使用#符替換

[root@Shell ~]# sed -r 's#/etc/abc/456#/dev/null#g' a.txt
[root@Shell ~]# sed -r 's@/etc/abc/456@/dev/null@' a.txt 

1.3.17 刪除文件

1.3.17.1 刪除配置文件中#號開頭的註釋行, 若是碰到tab或空格是沒法刪除

[root@Shell ~]# sed '/^#/d' file

1.3.17.2 刪除配置文件中含有tab鍵的註釋行

[root@Shell ~]# sed -r '/^[ \t]*#/d' file

1.3.17.3 刪除無內容空行

[root@Shell ~]# sed -r '/^[ \t]*$/d' file

1.3.17.4 刪除註釋行及空行

[root@Shell ~]# sed -r '/^[ \t]*#/d; /^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
[root@Shell ~]# sed -r '/^[ \t]*#|^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
[root@Shell ~]# sed -r '/^[ \t]*($|#)/d' /etc/vsftpd/vsftpd.conf 

1.3.18 給文件行添加註釋

1.3.18.1 將第二行到第六行加上註釋信息

[root@Shell ~]# sed '2,6s/^/#/' passwd

1.3.18.2 將第二行到第六行最前面添加#註釋符

[root@Shell ~]# sed -r '2,6s/.*/#&/' passwd

1.3.19 添加#註釋符

[root@Shell ~]# sed -r '3,$ s/^#*/#/' passwd
# sed -r '30,50s/^[ \t]*#*/#/' /etc/nginx.conf 
# sed -r '2,8s/^[ \t#]*/#/' /etc/nginx.conf

1.4 Awk文本處理

awk是一種編程語言,用於在linux/unix下對文本和數據進行處理。

awk數據能夠來自標準輸入、一個或多個文件,或其它命令的輸出。

awk一般是配合腳本進行使用, 是一個強大的文本處理工具。

1.4.1 awk 的處理數據的方式

1.進行逐行掃描文件, 從第一行到最後一行

2.尋找匹配的特定模式的行,在行上進行操做

3.若是沒有指定處理動做,則把匹配的行顯示到標準輸出

4.若是沒有指定模式,則全部被操做的行都被處理

1.4.2 awk的語法格式

awk [options] 'commands' filenames

awk [options] -f awk-script-file filenames

1.4.3 選項 options

-F 定義輸入字段分隔符,默認的分隔符, 空格或tab鍵

命令 command

行處理前 行處理 行處理後

BEGIN{} {} END{}

BEGIN發生在讀文件以前

[root@Shell ~]# awk 'BEGIN{print 1/2}'
0.5

BEGIN在行處理前, 修改字段分隔符

[root@Shell ~]# awk 'BEGIN{FS=":"} {print $1}' /etc/passwd

BEGIN在行處理前, 修改字段讀入和輸出分隔符

[root@Shell ~]# awk 'BEGIN{FS=":";OFS="---"} {print $1,$2}' /etc/passwd
[root@Shell ~]# awk 'BEGIN{print 1/2} {print "ok"} END {print "Game Over"}' /etc/hosts
0.5
ok
ok
ok
Game Over

1.4.4 awk命令格式

1.4.4.1 匹配 awk 'pattern' filename

[root@Shell ~]# awk '/root/' /etc/passwd

1.4.4.2 處理動做 awk '{action}' filename

[root@Shell ~]# awk -F: '{print $1}' /etc/passwd

1.4.4.3 匹配+處理動做 awk 'pattern {action}' filename

[root@Shell ~]# awk -F ':' '/root/ {print $1,$3}' /etc/passwd
[root@Shell ~]# awk 'BEGIN{FS=":"} /root/{print $1,$3}' /etc/passwd

1.4.4.4 判斷大於多少則輸出什麼內容 command |awk 'pattern {action}'

[root@Shell ~]# df |awk '/\/$/ {if ($3>50000) print $4}'

1.4.5 Awk工做原理

# awk -F: '{print $1,$3}' /etc/passwd

1.awk將文件中的每一行做爲輸入, 並將每一行賦給內部變量$0, 以換行符結束

2.awk開始進行字段分解,每一個字段存儲在已編號的變量中,從$1開始[默認空格分割]

3.awk默認字段分隔符是由內部FS變量來肯定, 可使用-F修訂

4.awk行處理時使用了print函數打印分割後的字段

5.awk在打印後的字段加上空格,由於$1,$3 之間有一個逗號。逗號被映射至OFS內部變量中,稱爲輸出字段分隔符, OFS默認爲空格.

6.awk輸出以後,將從文件中獲取另外一行,並將其存儲在$0中,覆蓋原來的內容,而後將新的字符串分隔成字段並進行處理。該過程將持續到全部行處理完畢.

1.4.6 Awk內部變量

*1.$0保存當前記錄的內容*

[root@Shell ~]# awk '{print $0}' /etc/passwd

*2.NR記錄輸入總的編號(行號)*

[root@Shell ~]# awk '{print NR,$0}' /etc/passwd
[root@Shell ~]# awk 'NR<=3' /etc/passwd

*3.FNR當前輸入文件的編號(行號)*

[root@Shell ~]# awk '{print NR,$0}' /etc/passwd /etc/hosts
[root@Shell ~]# awk '{print FNR,$0}' /etc/passwd /etc/hosts

*4.NF保存行的最後一列*

[root@Shell ~]# awk -F ":" '{print NF,$NF}' /etc/passwd /etc/hosts

5.FS指定字段分割符, 默認是空格

以冒號做爲字段分隔符

[root@Shell ~]# awk -F: '/root/{print $1,$3}' /etc/passwd

[root@Shell ~]# awk 'BEGIN{FS=":"} {print $1,$3}' /etc/passwd

以空格冒號tab做爲字段分割

[root@Shell ~]# awk -F'[ :\t]' '{print $1,$2,$3}' /etc/passwd

6.OFS指定輸出字段分隔符*

逗號映射爲OFS, 初始狀況下OFS變量是空格

[root@Shell ~]# awk -F: '/root/{print $1,$2,$3,$4}' /etc/passwd

[root@Shell ~]# awk 'BEGIN{FS=":"; OFS="+++"} /^root/{print $1,$2}' /etc/passwd

7.RS輸入記錄分隔符,默認爲換行符[瞭解]

[root@Shell ~]# awk -F: 'BEGIN{RS=" "} {print $0}' /etc/hosts

8.ORS將文件以空格爲分割每一行合併爲一行[瞭解]*

[root@Shell ~]# awk -F: 'BEGIN{ORS=" "} {print $0}' /etc/hosts

*9.print格式化輸出函數*

[root@Shell ~]# date|awk '{print $2,"5月份""\n",$NF,"今年"}'
[root@Shell ~]# awk -F: '{print "用戶是:" $1 "\t 用戶uid: " $3 "\t 用戶gid:" $4}' /etc/passwd

printf 函數

[root@Shell ~]# awk -F: '{printf "%-15s %-10s %-15s\n", $1, $2, $3}' /etc/passwd

%s 字符類型

%d 數值類型

佔 15 字符

- 表示左對齊,默認是右對齊

printf 默認不會在行尾自動換行,加\n

1.4.7 Awk模式動做

awk語句都由模式和動做組成。

模式部分決定動做語句什麼時候觸發及觸發事件。

若是省略模式部分,動做將時刻保持執行狀態。模式能夠是條件語句或複合語句或正則表達式。

*1.正則表達式*

匹配記錄(整行)

[root@Shell ~]# awk '/^root/' /etc/passwd
[root@Shell ~]# awk '$0 ~ /^root/' /etc/passwd

匹配字段:匹配操做符(~ !~)

[root@Shell ~]# awk '$1~/^root/' /etc/passwd
[root@Shell ~]# awk '$NF !~ /bash$/' /etc/passwd

*2.比較表達式*

比較表達式採用對文本進行比較,只有當條件爲真,才執行指定的動做。

比較表達式使用關係運算符,用於比較數字與字符串。

關係運算符

運算符 含義 示例

< 小於 x<y

<= 小於或等於 x<=y

== 等於 x==y

!= 不等於 x!=y

>= 大於等於 x>=y

> 大於 x>y

uid爲0的列出來

[root@Shell ~]# awk -F ":" '$3==0' /etc/passwd

uid小於10的所有列出來

[root@Shell ~]# awk -F: '$3 < 10' /etc/passwd

用戶登錄的shell等於/bin/bash

[root@Shell ~]# awk -F: '$7 == "/bin/bash" ' /etc/passwd

第一列爲alice的列出來

[root@Shell ~]# awk -F: '$1 == "alice" ' /etc/passw

爲alice的用戶列出來

[root@Shell ~]# awk -F: '$1 ~ /alice/ ' /etc/passwd 

[root@Shell ~]# awk -F: '$1 !~ /alice/ ' /etc/passwd

磁盤使用率大於多少則,則打印可用的值

[root@Shell ~]# df |awk '/\/$/'|awk '$3>1000000 {print $4}'

*3.條件表達式*

[root@Shell ~]# awk -F: '$3>300 {print $0}' /etc/passwd
[root@Shell ~]# awk -F: '{if($3>300) print $0}' /etc/passwd
[root@Shell ~]# awk -F: '{if($3>5555){print $3} else {print $1}}' /etc/passwd

*4.運算表達式*

[root@Shell ~]# awk -F: '$3 * 10 > 500000' /etc/passwd
[root@Shell ~]# awk -F: 'BEGIN{OFS="--"} { if($3*10>50000) {print $1,$3} } END {print "打印ok"}' /etc/passwd
[root@Shell ~]# awk '/southem/{print $5 + 10}' datafile 
[root@Shell ~]# awk '/southem/{print $5 + 10.56}' datafile
[root@Shell ~]# awk '/southem/{print $8 - 10}' datafile 
[root@Shell ~]# awk '/southem/{print $8 / 2 }' datafile 
[root@Shell ~]# awk '/southem/{print $8 * 2 }' datafile 
[root@Shell ~]# awk '/southem/{print $8 % 2 }' datafile

*5.邏輯操做符和複合模式*

&&邏輯與 || 邏輯或 !邏輯非

匹配用戶名爲root而且打印uid小於15的行

[root@Shell ~]# awk -F: '$1~/root/ && $3<=15' /etc/passwd

匹配用戶名爲root或uid大於5000

[root@Shell ~]# awk -F: '$1~/root/ || $3>=5000' /etc/passwd

1.4.8 awk示例1

# awk '/west/' datafile 
# awk '/^north/' datafile 
# awk '$3 ~ /^north/' datafile 
# awk '/^(no|so)/' datafile 
# awk '{print $3,$2}' datafile
# awk '{print $3 $2}' datafile 
# awk '{print $0}' datafile 
# awk '{print "Number of fields: "NF}' datafile 
# awk '/northeast/{print $3,$2}' datafile
# awk '/^[ns]/{print $1}' datafile 
# awk '$5 ~ /\. [7-9]+/' datafile 
# awk '$2 !~ /E/{print $1,$2}' datafile 
# awk '$3 ~ /^Joel/{print $3 "is a nice boy."}' datafile 
# awk '$8 ~ /[0-9][0-9]$/{print $8}' datafile
# awk '$4 ~ /Chin$/{print "The price is $" $8 "."}' datafile 
# awk '/Tj/{print $0}' datafile 
# awk -F: '{print "Number of fields: "NF}' /etc/passwd 
# awk -F"[ :]" '{print NF}' /etc/passwd 

1.4.9 awk示例2

[root@Shell ~]# cat b.txt 
lzy lizhenya:is a:good boy!
[root@Shell ~]# awk '{print NF}' b.txt
4
[root@Shell ~]# awk -F ':' '{print NF}' b.txt
3
[root@Shell ~]# awk -F"[ :]" '{print NF}' .txt
6

1.4.10 Awk條件判斷

if語句格式:{ if(表達式){語句;語句;... }}

打印當前管理員用戶名稱

[root@Shell ~]# awk -F: '{ if($3==0){print $1 "is adminisitrator"} }' /etc/passwd

統計系統用戶數量

[root@Shell ~]# awk -F: '{ if($3>0 && $3<1000){i++}} END {print i}' /etc/passwd

統計普通用戶數量

[root@Shell ~]# awk -F: '{ if($3>1000){i++}} END {print i}' /etc/passwd 

if...else 語句格式: {if(表達式){語句;語句;... }else{語句;語句;...}}

# awk -F: '{if($3==0){print $1} else {print $7}}' /etc/passwd
# awk -F: '{if($3==0) {count++} else{i++} }' /etc/passwd
# awk -F: '{if($3==0){count++} else{i++}} END{print " 管理員個數: "count ; print " 系統用戶數: "i}' /etc/passwd

if...else if...else 語句格式:

{if(表達式 1){語句;語句;... }else if(表達式 2){語句;語句;. .. }else{語句;語句;... }}

[root@Shell ~]# awk -F: '{ if($3==0){i++} else if($3>0 && $3<1000){j++} else if($3>1000) {k++}} END {print i;print j;print k}' /etc/passwd

[root@Shell ~]# awk -F: '{ if($3==0){i++} else if($3>0 && $3<1000){j++} else if($3>1000) {k++}} END {print "管理員個數"i; print "系統用戶個數" j; print "系統用戶個 數" }' /etc/passwd
管理員個數1
系統用戶個數29
0系統用戶個數69

1.4.11 Awk循環語句

1.4.11.1 while循環

[root@Shell ~]# awk 'BEGIN{ i=1; while(i<=10){print i; i++} }'
[root@Shell ~]# awk -F: '{i=1; while(i<=NF){print $i; i++}}' /etc/passwd
[root@Shell ~]# awk -F: '{i=1; while(i<=10) {print $0; i++}}' /etc/passwd
[root@Shell ~]#cat b.txt
111 222
333 444 555
666 777 888 999
[root@Shell ~]# awk '{i=1; while(i<=NF){print $i; i++}}' b.txt

1.4.11.2 for循環

C 風格 for
[root@Shell ~]# awk 'BEGIN{for(i=1;i<=5;i++){print i} }' 
將每行打印 10 次
[root@Shell ~]# awk -F: '{ for(i=1;i<=10;i++) {print $0} }' passwd
[root@Shell ~]# awk -F: '{ for(i=1;i<=NF;i++) {print $i} }' passwd

1.4.12 awk數組概述

將須要統計的某個字段做爲數組的索引,而後對索引進行遍歷

1.4.12.1 統計/etc/passwd中各類類型shell 的數量*
# awk -F: '{shells[$NF]++} END{ for(i in shells){print i,shells[i]} }' /etc/passwd
1.4.12.2 站訪問狀態統計<當前時實狀態ss>*
[root@Shell ~]# ss -an|awk '/:80/{tcp[$2]++} END {for(i in tcp){print i,tcp[i]}}'
1.4.12.3 統計當前訪問的每一個IP的數量<當前時實狀態 netstat,ss>*
[root@Shell ~]# ss -an|awk -F ':' '/:80/{ips[$(NF-1)]++} END {for(i in ips){print i,ips[i]}}'

1.4.13 Awk數組案例

Nginx日誌分析,日誌格式以下:

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
           '$status $body_bytes_sent "$http_referer" '
           '"$http_user_agent" "$http_x_forwarded_for"';
52.55.21.59 - - [25/Jan/2018:14:55:36 +0800] "GET /feed/ HTTP/1.1" 404 162 "https:www.google.com/" "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; de) Presto/2.9.168 Version/11.52" "-"

1.4.13.1 統計當天的PV量

[root@Shell ~]# grep "25/Jan/2018" log.bjstack.log |wc -l

[root@Shell ~]# awk "/25\/Jan\/2018/" log.bjstack.log |wc -l

[root@Shell ~]# awk '/25\/Jan\/2018/ {ips[$1]++} END {for(i in ips) {sum+=ips[i]} {print sum}}' log.bjstack.log

1.4.13.2 統計pv量

[root@Shell ~]# awk '$4>="[25/Jan/2018:15:00:00" && $4<="[25/Jan/2018:19:00:00 {print $0}"' log.bjstack.log |wc -l

1.4.13.3 統計一天內訪問最多的10個IP*

[root@Shell ~]# awk '/25\/Jan\/2018/ {ips[$1]++} END {for(i in ips){ print ips[i],i}}' log.bjstack.log |sort -rn|head

1.4.13.4 統計訪問次數最多的10個IP

[root@Shell ~]# awk '$4>="[25/Jan/2018:15:00:00" && $4<="[25/Jan/2018:19:00:00"' log.bjstack.log |awk '{ips[$1]++} END {for(i in ips){print ips[i],i}}'|sort -rn|head

1.4.13.5 統計當天訪問大於100次的IP*

[root@Shell ~]# awk '/25\/Jan\/2018/ {ips[$1]++} END {for(i in ips){if(ips[i]>10){print i,ips[i]}}}' log.bjstack.log

1.4.13.6 統計當天訪問最多的10個頁面($request top 10)*

[root@Shell ~]# awk '/25\/Jan\/2018/ {request[$7]++} END {for(i in request){print request[i],i}}' log.bjstack.log |sort -rn|head

1.4.13.7 統計當天每一個URL訪問內容總大小($body_bytes_sent)*

[root@Shell ~]# awk '/25\/Jan\/2018/ {request[$7]++;size[$7]+=$10} END {for(i in request){print request[i],i,size[i]}}' log.bjstack.log |sort -rn|head

1.4.13.8 統計當天每一個IP訪問狀態碼數量($status)*

[root@Shell ~]# awk '{ip_code[$1 " " $9]++} END {for(i in ip_code){print ip_code[i],i}}' log.bjstack.log|sort -rn|head

1.4.13.9 統計2018年01月25日,訪問狀態碼爲404及出現的次數($status)*

[root@Shell ~]# grep "404" log.bjstack.log |wc -l
[root@Shell ~]# awk '{if($9=="404") code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log

1.4.13.10 統計2018年01月25日,8:30-9:00訪問狀態碼是404*

[root@Shell ~]# awk '$4>="[25/Jan/2018:15:00:00" && $4<="[25/Jan/2018:19:00:00" && $9=="404" {code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log

[root@Shell ~]# awk '$9=="404" {code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log

1.4.13.11 統計2018年01月25日,各類狀態碼數量,統計狀態碼出現的次數

[root@Shell ~]# awk '{code[$9]++} END {for(i in code){print i,code[i]}}' log.bjstack.log

[root@Shell ~]# awk '{if($9>=100 && $9<200) {i++}

else if ($9>=200 && $9<300) {j++}

else if ($9>=300 && $9<400) {k++}

else if ($9>=400 && $9<500) {n++}

else if($9>=500) {p++}}

END{print i,j,k,n,p,i+j+k+n+p}' log.bjstack.log
相關文章
相關標籤/搜索