shell程式設計,實戰高階進階教學

明日秋風發表於2020-11-25

shell程式設計從入門到高階教程

一.shell程式設計初識

1.1 shell能做什麼

1. 自動化批量系統初始化程式 (update,軟體安裝,時區設定,安全策略...)
2. 自動化批量軟體部署程式 (LAMP,LNMP,Tomcat,LVS,Nginx)
3. 應用管理程式 (KVM,叢集管理擴容,MySQL,DELLR720批量RAID)
4. 日誌分析處理程式(PV, UV, 200, !200, top 100, grep/awk)
5. 自動化備份恢復程式(MySQL完全備份/增量 + Crond)
6. 自動化管理程式(批量遠端修改密碼,軟體升級,配置更新)
7. 自動化資訊採集及監控程式(收集系統/應用狀態資訊,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
8. 配合Zabbix資訊採集(收集系統/應用狀態資訊,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
9. 自動化擴容(增加雲主機——>業務上線)
    zabbix監控CPU 80%+|-50%  Python API  AWS/EC2(增加/刪除雲主機) + Shell Script(業務上線)
10. 俄羅斯方塊,列印三角形,列印聖誕樹,列印五角星,執行小火車,坦克大戰,排序演算法實現,等眾多功能的一個實現
11. Shell可以做任何事(一切取決於業務需求)

1.2 shell程式設計初識

shell的定義

Shell 是命令直譯器
Shell 也是一種程式設計語言,它有變數,關鍵字,各種控制語句,有自己的語法結構,利用shell程式設計語言可以編寫功能很強、程式碼簡短的程式**

shell的分類和更改

cat /etc/shells

/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh

預設shell: bash

注意:各種指令碼里凡是/bin/sh表示的是使用預設bash

檢視當前使用的shell:
#echo $SEHLL

shell 的更改:
#chsh
#vim /etc/passwd

適用範圍
什麼時候不適合使用Shell程式設計:
1. 資源緊張的專案,特別是那些速度是重要因素的地方(排序,散序,等等)
2. 程式要進行很複雜的數學計算,特別是浮點計算,任意精度的計算,或者是複數計算
3. 要求交叉編譯平臺的可移植性(使用C或者是Java代替)
4. 需要結構化程式設計的複雜應用(需要變數型別檢查和函式原型等等)
5. 對於影響系統全域性性的關鍵任務應用。
6. 安全非常重要。你必須保證系統完整性和抵抗入侵,攻擊和惡意破壞。
7. 專案由連串的依賴的各個部分組成。
8. 多種檔案操作要求(Bash被限制成檔案順序存取,並且是以相當笨拙,效率低下的逐行的存取方式)
9. 需要良好的多維陣列支援。
10. 需要類似連結串列或樹這樣的資料結構。
11. 需要產生或操作圖象或圖形使用者介面。
12. 需要直接存取系統硬體。
13. 需要埠號或是socket I/O。
14. 需要使用可重用的函式庫或介面。
15. 所有的私有的不開源的應用程式(Shell指令碼的原始碼是直接可讀,能被所有人看到的)

如果你需要有上面的任意一種應用,請考慮其他的更強大的指令碼語言――Perl,Tcl,Python,Ruby,或者可能是其他更高階的編譯型語言,例如C,C++或者是Java

例項(僅展示用,後續有詳細講解)

相對於shell指令碼是可以實現大多數自動化部署功能,可以快速的提升工作效率,比如如下指令碼,實現眾多 批量化部署

[root@master1 ~]# cat jiaoben.sh 
#!/usr/bash


liu () {
cat <<-EOF

1.關閉防火牆,一鍵三連,修改selinux                                        
2.檢視系統狀態[磁碟大小和使用率 記憶體大小和使用率 cpu負載 執行時間 版本號]    
3.一鍵配置yum源,一鍵三聯,關閉防火牆,	                         
4.修改主機名,配置靜態IP							
5.yum安裝mysql-5.7                 					
6.安裝yum安裝 nginx			
7.安裝jdk8.tomcat8版本   	

EOF
}

one (){
        systemctl stop firewalld 
        iptables -X && iptables -F && iptables -Z
        systemctl disable firewalld && systemctl status firewalld 
        sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config && getenforce
}





two () {

cat <<-EOF
1.磁碟大小和使用率  
2.記憶體大小和使用率  
3.cpu負載	   
4.執行時間	      
5.版本號	       

}
EOF

printf "\e[1;31m 請選擇你想使用的功能(1/2/3/4/5): \e[0m" && read ww		
case $ww in
	1)
	a=`df -Th |awk '{print $1,$2,$5}'`     ##查詢所有磁碟記憶體資訊
	echo "磁碟大小是使用率分別為: $a"
	;;
	2)
	b=`free -h |grep 'Mem' |awk '{print $2,$3}'`
	echo "記憶體大小和使用率分別為: $b"
	;;
	3)
	c=`uptime |awk '{print $11,$12,$13}'`
	echo "cpu 負載為:  $c"
	;;
	4)
	d=`uptime |awk '{print $3,$4}'`
        echo "執行時間為:  $d"
	;;
	5)
	e=`uname -r`
        echo "版本號為:  $e"
        ;;
        *)
        echo "error select"
        ;;
esac
}



tree () {

f=`cat /etc/redhat-release  |awk '{print $4}' |awk -F'.' '{print $1}'`
if [ $f -eq 7 ];then
        echo "此版本為centos $f 的版本"
        read -p "是否重新配置yum源 (y|n): " yn

    if [ "$yn" = "y" ];then
	yum install -y wget ntpdate net-tools vim bash-completion ShellCheck
	systemctl stop firewalld
	iptable -X && iptable -F && iptable -Z
	systemctl disable firewalld
	sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config
	setenforce 0
        cp /etc/yum.repos.d/* /tmp/ && rm -rf  /etc/yum.repos.d/*
	curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
	curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
	yum clean all && yum makecache fast

	ntpdate -b ntp1.aliyun.com
     else
    echo "不需要重新配置yum源"

   fi
fi
}


four () {
cat <<-EOF
"請輸入您的選項"
1.修改主機名
2.配置靜態ip
EOF


printf "\e[1;31m 請選擇你想使用的功能(1/2): \e[0m" && read wen
case $wen in
        1)
        read -p "請輸入你的主機名" name
        hostnamectl set-hostname  $name &&bash
        ;;
        2)
        cp /etc/sysconfig/network-scripts/ifcfg-ens33 /tmp/ifcfg-ens33.bak
        read -p "請輸入你的ip的末尾" ipname
echo "TYPE=Ethernet
BOOTPROTO=static
DEFROUTE=yes
NAME=ens33
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.229.$ipname
PREFIX=24                          
GATEWAY=192.168.229.2
DNS1=223.5.5.5
DNS2=223.6.6.6" >/etc/sysconfig/network-scripts/ifcfg-ens33
        systemctl restart network
        ;;
        *)
        echo "eroor select"
        ;;
esac

}



five () {

if rpm -qa |grep "mysql" |egrep -v grep > /dev/null
   then
            echo cunzai!
   else
      	    echo no !

        sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config
        systemctl stop firewalld && systemctl disable firewalld
        yum -y groupinstall "Development Tools"
        cd /usr/local/src/
        wget https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
        rpm -ivh mysql80-community-release-el7-1.noarch.rpm
        sed -ri 21s/enabled=0/enabled=1/g  /etc/yum.repos.d/mysql-community.repo
        sed -ri 28s/enabled=1/enabled=0/g  /etc/yum.repos.d/mysql-community.repo
        yum -y install mysql-community-server
        systemctl start mysqld && systemctl enable mysqld
        grep "password" /var/log/mysqld.log
	
fi
}


six (){
      if rpm -qa |grep "nginx" |egrep -v grep > /dev/null
	then 
		echo cunzai!
	else
		echo no !
	yum -y install gcc gcc-c++ pcre pcre-devel
	yum -y install   nginx

	fi
}



liu
printf "\e[1;31m 請選擇你想要實現的功能(1/2/3/4/5/6):  \e[0m" && read sb

case $sb in
	1)one
	;;
	2)two
	;;
	3)tree
	;;
	4)four
	;;
	5)five
	;;
	6)six
	;;
*)
	echo "eroor select"
esac

1.3 shell程式設計特點

bash特性

    補全
    歷史
    別名
    快捷鍵
    前後臺作業
    重定向
    管道
    命令排序執行
        ;   &&    ||
    萬用字元
            {}      ?  *
    正規表示式
    指令碼

歷史命令



檢視歷史命令
    history                        /etc/profile  下的historysize 可以修改
呼叫歷史命令
   上下健     
   !關鍵字   
   !歷史命令列號      
   !! 執行上一條命令
   !$  上一條命令
   alt+.   
   esc    .   上一條命令的最後一個引數    
   Ctrl+r	在歷史命令中查詢,輸入關鍵字調出之前的命令
   關鍵字+pgup/phdn  可以切換關鍵字相關的歷史命令
                                                        
顯示歷史命令執行時間:
1.設定變數:
    HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S"
2.再次執行history檢視結果 

別名
    檢視別名
        alias
     設定別名
        臨時設定

Bash部分快捷鍵(常用) ,這裡採用的都是Linux編輯器上的常用快捷鍵,有利於高效的展開工作

	Ctrl+a 切換到命令列開始(跟home一樣,但是home在某些unix環境下無法使用)
	Ctrl+e 切換到命令列末尾
	Ctrl+u 清除剪下游標之前的內容
	Ctrl+k 清除剪下游標之後的內容
	ctrl+y	貼上剛才鎖刪除的字元
	Ctrl+r	在歷史命令中查詢,輸入關鍵字調出之前的命令

萬用字元置換



*,?,[]{}

例:
字元	        含義	                        例項
*	        匹配 0 或多個字元	         a*b  a與b之間可以有任意長度的任意字元, 也可以一個也沒有, 如aabcb, axyzb, a012b, ab。
?	        匹配任意一個字元	             a?b  a與b之間必須也只能有一個字元, 可以是任意字元, 如aab, abb, acb, a0b。
[list] 	    匹配 list 中的任意單一字元	a[xyz]b   a與b之間必須也只能有一個字元, 但只能是 x 或 y 或 z, 如: axb, ayb, azb。
[!list] 	匹配 除list 中的任意單一字元	a[!0-9]b  a與b之間必須也只能有一個字元, 但不能是阿拉伯數字, 如axb, aab, a-b。
[^list] 	匹配 除list 中的任意單一字元	a[^0-9]b  a與b之間必須也只能有一個字元, 但不能是阿拉伯數字, 如axb, aab, a-b。
[c1-c2]	    匹配 c1-c2 中的任意單一字元 如:[0-9] [a-z]	a[0-9]b  0與9之間必須也只能有一個字元 如a0b, a1b... a9b。
{string1,string2,...}	匹配 sring1 或 string2 (或更多)其一字串	a{abc,xyz,123}b    a與b之間只能是abc或xyz或123這三個字串之一。

1.4shell指令碼規範


[root@master1 ~]#  vim helloworld.sh   
								---.sh代表這個檔案是個shell指令碼,擴充名字尾,如果省略.sh則不易判斷該檔案是否為shell指令碼
1. #!/usr/bin/env bash    ---shebang蛇棒, 直譯器, 翻譯
2. #
3. # Author: tiger
3. # Email: tigerfive@163.com
4. # Github: https://github.com/tigerfive			---這就是註釋, 你沒看錯
5. # Date: 2019/**/**
6. 
7. printf "hello world\n"

[root@master1 ~]# sh helloworld.sh      #sh命令是啟動指令碼
hello world 
[root@master1 ~]# ./helloworld.sh      #./   也是啟動sh 指令碼命令



第一行: “#!/usr/bin/env bash”叫做shebang, shell語法規定shell指令碼檔案第一行為整個檔案的直譯器

第二行: 為“#”開頭的行為註釋行預設不會被程式所讀取, 用來說明檔案及標定所屬人員使用, 也可用來解釋程式

第七行: 為格式化列印語句printf, printf可以把後面的“hello world”列印到指定的終端中, \n為換行符


1.5 指令碼執行方式

建立bash指令碼

	1.建立指令碼檔案
	    指定命令直譯器
	    註釋
	    編寫bash指令集合
	2.修改許可權

bash指令碼的執行

bash
#./scripts
#/shelldoc/scripts
#. ./scripts               使用當前shell執行
#source ./scripts      使用當前shell執行  比如cd /tmp會改變當前shell環境,但是其他的方式不會
#bash scripts

子shell

(cmds)   表示開啟子shell
   # pwd
   
	/root/shell
   # (cd /tmp;touch test;)
   # ls /tmp/test
	/tmp/test
   # pwd
    /root/shell
    子shell能夠繼承父shell的一些屬性,但是子shell不能夠反過來改變父shell的屬性
    子shell的好處之一可以將複雜的任務分成多個小任務,並行處理,加快執行速度。
   
    所有shell的父shell父程式:init    

{cmds}  不開啟子shell
    # { cd /tmp;touch test1; }
    # pwd
      /tmp

bash指令碼測試

	•sh –x script
		這將執行該指令碼並顯示所有變數的值
	•sh –n script
		不執行指令碼只是檢查語法模式,將返回所有錯誤語法
	•sh –v script
		執行指令碼前把指令碼內容顯示在螢幕上

1.6變數的型別 【要點】

變數
shell 變數? 用一個固定的字串去表示不固定的內容
bash作為程式設計語言和其它高階語言一樣也提供使用和定義變數的功能

變數分類
預定義變數、環境變數、自定義變數、位置變數

變數的型別:
	1. 自定義變數
		定義變數:		變數名=變數值 變數名必須以字母或下劃線開頭,區分大小寫   ip1=192.168.2.115 
		引用變數:		$變數名 或 ${變數名}
		檢視變數:		echo $變數名  set(所有變數:包括自定義變數和環境變數)
		取消變數:		unset 變數名
		作用範圍:		僅在當前shell中有效


[root@master ~]# a=8          
[root@master ~]# echo $a
8
[root@master ~]# echo $a2

[root@master ~]# echo ${a}2
82


環境變數:shell在開始執行時已經定義好的
	env       檢視所有環境變數
	set        檢視所有變數
	   環境變數擁有可繼承性:export之後就擁有繼承性
	export   匯出變數(作用範圍)
    臨時生效
    永久生效
            寫道4個登陸指令碼中     ~/.bashrc   ~/profile                
            更好放在/etc/profile.d/* 下建立獨立的環境變數配置檔案


            
            jdk
            
	常用環境變數:USER	UID	HOME 	   HOSTNAME	 PWD	 PS1	PATH
            
    PATH:儲存所有命令所在的路徑
    
3. 位置變數
		$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10}
		
4. 預定義變數
	    $0    指令碼名
		$*		所有的引數
		$@ 	所有的引數
		$# 	引數的個數
		$$ 	當前程式的PID
		$!     上一個後臺程式的PID
		$?		上一個命令的返回值 0表示成功	
		
			示例1:
		# vim test.sh
		echo "第2個位置引數是$2"
		echo "第1個位置引數是$1"
		echo "第4個位置引數是$4"

		echo "所有引數是: $*"
		echo "所有引數是: $@"
		echo "引數的個數是: $#"
		echo "當前程式的PID是: $$"

		echo '$1='$1
		echo '$2='$2
		echo '$3='$3
		echo '$*='$*
		echo '$@='$@
		echo '$#='$#
		echo '$$='$$
	瞭解$*和$@區別


變數賦值

變數的賦值方式:
1. 顯式賦值
	變數名=變數值
	示例:
	ip1=192.168.229.61
	school="BeiJing 1000phone"
	today1=`date +%F`
	today2=$(date +%F)
				
2. read 從鍵盤讀入變數值
	read 變數名
	read -p "提示資訊: "  變數名
	read -t 5 -p "提示資訊: "  變數名
	read -n 2 變數名
		
		

示例一:ping,採用變數寫一個,判斷ip地址能否ping通
[root@master ~]# cat ping2.sh 
#!/bin/bash							
read -p "Input IP: " ip                
ping -c2 $ip &>/dev/null		   
if [ $? = 0 ];then					
	echo "host $ip is ok"	    
else											
	echo "host $ip is fail"	    
fi		
		
[root@master ~]# sh ping2.sh 
Input IP: 192.168.229.61          #輸入您需要ping的IP地址
host 192.168.229.61 is ok		 #顯示ok則以完成測試

	

定義或引用變數時注意事項:
" "  	    弱引用
' ' 	    強引用
[root@master ~]# school=1000phone
[root@master ~]# echo   "${school} is good"    
1000phone is good
[root@master ~]# echo '${school} is good'
${school} is good

		
` `  	命令替換 等價於 $()   反引號中的shell命令會被先執行
[root@master ~]# touch `date +%F`_file1.txt  
[root@master ~]# touch $(date +%F)_file2.txt 
			   
[root@master ~]# disk_free3="df -Ph |grep '/$' |awk '{print $4}'"	錯誤
[root@master ~]# disk_free4=$(df -Ph |grep '/$' |awk '{print $4}')
[root@master ~]# disk_free5=`df -Ph |grep '/$' |awk '{print $4}'`		

變數運算

1. 整數運算
	方法一:expr
    expr 1 + 2
	expr $num1 + $num2    			+  -  \*  /  %
	
	方法二:$(())
	echo $(($num1+$num2))      	+  -  *  /   %
	echo $((num1+num2))
	echo $((5-3*2))	 
	echo $(((5-3)*2))
	echo $((2**3))
	sum=$((1+2)); echo $sum

	方法三:$[]
	echo $[5+2]						         +  -  *  /  %
	echo $[5**2]

	方法四:let
	let sum=2+3; echo $sum
	let i++; echo $i

	2. 小數運算
	echo "2*4" |bc
	echo "2^4" |bc
	echo "scale=2;6/4" |bc
	awk 'BEGIN{print 1/2}'
	echo "print 5.0/2" |python



2.浮點運算:
bash本身不能做小數計算:需要bc命令轉換
#echo "2*4" | bc
#echo "2^4" | bc
#echo "scale=2;6/4" | bc
    scale: 精度
#awk 'BEGIN{print 1/2}'
#echo "print 5.0/2" | python

    計算我的信用卡一個月的利息,假設我欠10000塊錢
    #!/bin/bash 
    m=$( echo 5/10000|bc -l)
    #因為shell不支援小數,所以要用bc轉換一下
    sum=10000
    for i in {1..365} 
    do    
        sum=$(echo $sum+$sum*$m | bc )
        echo $sum 
    done
    echo $sum




3.變數引用
轉義:\
    為了顯示元字元,需要引用。當一個字元被引用時,其特殊含義被禁止
    把有意義的變的沒意義,把沒意義的變的有意義
    
    \n  \t  \r
    # echo -e '5\\n6\n7'
    5\n6
    7
    
    # yum install httpd mysql  -y
    # yum groupinstall  KDE\ Plasma\ Workspaces
    
完全引用:''    //強引  硬引
部分引用:""   //弱引  軟引
#ls -lh --sort=size | tac
#echo hello;world
#echo you own $1250

例子:  從這個例子舉出的是,弱引和強引的區別,強引列印原文,弱引列印變數
[root@master ~]# num=15
[root@master ~]# echo 1793班有$num個女生
1793班有15個女生
[root@master ~]# echo "1903班有$num個女生"
1903班有15個女生
[root@master ~]# echo '1903班有$num個女生'
1903班有$num個女生


讀取使用者標準輸入:read    【重點引數】
read:功能就是讀取鍵盤輸入的值,並賦給變數
#read  -t  5  var
#read -p "提示資訊" var

示例:
#!/bin/bash
read system  setting network
echo "This is computer function $system"
echo "This is computer function $setting"
echo "This is computer function $network"



示例:2
暫停使用者輸入:
[root@master ~]# shiyan.sh 
read  -p "如果你同意以上協議請按回車繼續! "  answer
echo 這是下面的操作了
sleep 1
echo 這是下面的操作了
sleep 1
echo 這是下面的操作了
sleep 1
echo 這是下面的操作了
sleep 1
echo 這是下面的操作了

[root@master ~]# sh shiyan.sh 
如果你同意以上協議請按回車繼續! 
這是下面的操作了
這是下面的操作了
這是下面的操作了
這是下面的操作了
這是下面的操作了


示例3:
[root@master ~]# vim shiyan2.sh 
#!/bin/bash 
read  -p "Do you want to continue [Y/N]? "  answer 
case $answer in 
Y|y) 
    echo "fine ,continue";; 
N|n) 
    echo "ok,good bye";; 
*) 
    echo "error choice";; 
esac
exit 0

[root@master ~]# sh shiyan2.sh 
Do you want to continue [Y/N]? Y  
fine ,continue
[root@master ~]# sh shiyan2.sh 
Do you want to continue [Y/N]? N
ok,good bye



-s 選項
    能夠使read命令中輸入的資料不顯示在監視器上(實際上,資料是顯示的,只是read命令將文字顏色設定成與背景相同的顏色
#!/bin/bash 
read  -s  -p "Enter your password: " pass 
echo "your password is $pass" 
exit 0


取消螢幕回顯:
stty -echo
stty echo


變數長度
[root@master ~]# a=123
[root@master ~]# echo ${#a}        # 表示$var的長度
3

變數巢狀
[root@master ~]# a=8
[root@master ~]# name8=9
[root@master ~]# eval echo \$name$a
9


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~·~·~~~~~~~~~~~~~~~~~~~~~~~~~~~~

示例4:  【這個指令碼引數內容需要熟記,後續經常用到的】
[root@master ~]# vim shiyan4.sh    #操作選項指令碼,簡單實現
#!/bin/bash
echo -e '1.配置yum客戶端'
echo 2.新增A記錄
echo 3.一鍵安裝lamp環境
echo '4.一鍵配置靜態IP'
read -p "請選擇你想使用的功能(1/2/3/4):" num

con_ip(){
	echo 這是配置IP地址的小工具
}

case $num in
	1):;;
	2):;;
	3):;;
	4)con_ip;;
	*):;;
esac

[root@master ~]# sh shiyan4.sh
1.配置yum客戶端
2.新增A記錄
3.一鍵安裝lamp環境
4.一鍵配置靜態IP
請選擇你想使用的功能(1/2/3/4):1
[root@master ~]# sh shiyan4.sh
1.配置yum客戶端
2.新增A記錄
3.一鍵安裝lamp環境
4.一鍵配置靜態IP
請選擇你想使用的功能(1/2/3/4):2
[root@master ~]# 



1.7 i++和++i

++在前就是先算++
++在後就是後算++

i++++i (瞭解)

對變數的值的影響:
[root@master ~]# i=1
[root@master ~]# let i++
[root@master ~]# echo $i
2
[root@master ~]# j=1
[root@master ~]# let ++j
[root@master ~]# echo $j
2

對錶達式的值的影響:
[root@master ~]# unset i
[root@master ~]# unset j
[root@master ~]# i=1
[root@master ~]# j=1
[root@master ~]# let x=i++         先賦值,再運算
[root@master ~]# let y=++j         先運算,再賦值
[root@master ~]# echo $i
2
[root@master ~]# echo $j
2
[root@master ~]# echo $x
1
[root@master ~]# echo $y
2


二.shell變成

1.shell條件測試

測試
test 條件
條件為真返回 0,條件為假返回 1    【這個必須要記住,0為真,1為假】

[ 條件 ]

test能夠理解3中型別的表示式
1.檔案測試
2.字串比較
3.數字比較

字串
     -n STRING
          the length of STRING is nonzero
     -z STRING
          the length of STRING is zero
      STRING1 = STRING2
            the strings are equal
      STRING1 != STRING2
            the strings are not equal

數字    			【重點引數】
eq 等於equal         ne 不等於
ge 大於等於           gt 大於
le 小於等於           lt 小於

檔案
-f 存在且是普通檔案
-d 存在且是目錄
-l 存在且是符號連結
-b 塊裝置
-c 字元裝置
-e 檔案存在
-r
-w
-x
file1 -nt file2    file1 比 file2 新(修改時間)
file1 -ot file2    file1 比 file2 舊(修改時間)

=========

格式1: test 條件表示式
格式2: [ 條件表示式 ]
格式3: [[ 條件表示式 ]]

man test

===檔案測試 [ 操作符 檔案或目錄 ]
[root@master ~]# test -d /home
[root@master ~]# echo $?
0      #零說明改目錄存在
[root@master ~]# test -d /home11111
[root@master ~]# echo $?
1      #一說明改目錄存在
[root@master ~]# [ -d /home ]

[ -e dir|file ]
[ -d dir ]
[ -f file ]		是否存在,而且是檔案
[ -r file ]		當前使用者對該檔案是否有讀許可權
[ -x file ]
[ -w file ]
[ -L file ]
[root@master ~]# [ ! -d /ccc ]  && mkdir /ccc
[root@master ~]# [ -d /ccc ]  || mkdir /ccc


===數值比較 [ 整數1 操作符 整數2 ]   【重點】
[ 1 -gt 10 ]	    大於
[ 1 -lt 10 ]	    小於
[ 1 -eq 10 ]	    等於
[ 1 -ne 10 ]        不等於
[ 1 -ge 10 ]	    大於等於
[ 1 -le 10 ]	    小於等於


===字串比較
提示:使用雙引號
[root@master ~]# [ $(id -u) -eq 0 ] || echo "必須是超級使用者才能執行"
[root@master ~]# [ "$USER" = "root" ];echo $?
0
[root@master ~]# [ "$USER" = "alice" ];echo $?
1
[root@master ~]# [ "$USER" != "alice" ];echo $?
0
[root@master ~]# [ 1 -lt 2 -a 5 -gt 10 ];echo $?
1
[root@master ~]# [ 1 -lt 2 -o 5 -gt 10 ];echo $?
0
[root@master ~]# [[ 1 -lt 2 && 5 -gt 10 ]];echo $? 
1
[root@master ~]# [[ 1 -lt 2 || 5 -gt 10 ]];echo $? 
0
[root@master ~]# [[ "$USER" =~ ^r ]];echo $?		  # 使用正則  正則只能使用[[]]
0



==C語言風格的數值比較
[root@master ~]# ((1<2));echo $?
0
[root@master ~]# ((1==2));echo $?
1
[root@master ~]# ((1>2));echo $?
1
[root@master ~]# ((1>=2));echo $?
1
[root@master ~]# ((1<=2));echo $?
0
[root@master ~]# ((1!=2));echo $?
0
[root@master ~]# ((`id -u`>0));echo $?
1
[root@master ~]# (($UID==0));echo $?
0


示例1:
判斷變數是不是數字:
[root@master ~]# num1=123
[root@master ~]# num2=sss1414ss
[root@master ~]# [[ "$num1" =~ ^[0-9]+$ ]];echo $?     #判斷是數字,則成功得出值為0
0
[root@master ~]# [[ "$num1" =~ ^[0-9]+$ ]];echo $?     #判斷結果為英文加數字。得出結果是錯誤的
1


示例2:判斷是否輸入的是一個數值
[root@master ~]# vim shiyan5.sh
#!/bin/bash                                                               
#判斷使用者輸入的是否是數字                                         
read -p "請輸入一個數值: " num           #設立條件                                                                                                           
while :                                                                        
do                                                                               
        if [[ $num =~ ^[0-9]+$ ]];then       #if語句進行判斷               
                break                                                        
        else                                                       
                read -p "不是數字,請重新輸入數值: " num    #判斷失敗則需要衝i重新輸入 
       fi                                                                         
done                                                                                
echo "你輸入的數字是: $num"      #判斷成功  
[root@master ~]# sh shiyan5.sh    #執行指令碼
請輸入一個數值: 123               #輸入數字匹配成功
你輸入的數字是: 123
[root@master ~]# sh shiyan5.sh 
請輸入一個數值: abc         #輸入的不是數值,則需要重新輸入
不是數字,請重新輸入數值: 123
你輸入的數字是: 123



示例3:判斷限定使用者輸入八位數的數值
[root@master ~]# vim shiyan6.sh 
#!/usr/bin/env bash
read -p "其請輸入一個8位數數字: " NUM
if [ ${#NUM} -eq 8 ];then
  echo "你輸入的是一個8位數"
 else
   echo "你輸入的不是8位數字:拜拜嘞,您!"
   exit
fi
if [[ ! $NUM =~ ^[0-9]+$ ]];then
	echo "你輸入的不是8位數字!"
 else 
        echo "你輸入的是8位數字: good"
fi

[root@master ~]# sh shiyan6.sh 
其請輸入一個8位數數字: 123455
你輸入的不是8位數字:拜拜嘞,您!
[root@master ~]# sh shiyan6.sh 
其請輸入一個8位數數字: 14725836              
你輸入的是一個8位數
你輸入的是8位數字: good


需要注意:shell是一門很強大的程式語言,所有的判斷條件都可以靈活應用,採用if語句判斷髮,條件篩選法,以及後續所用到的函式結合法,精通shell可以高效率的完成多數機器自動化部署

2. shell分支if語句

流程控制:if
•在一個shell指令碼中的命令執行順序稱作指令碼的流。大多數指令碼會根據一個或多個條件來改變它們的流。
•流控制命令:能讓指令碼的流根據條件而改變的命令稱為條件流控制命令
•exit語句:退出程式的執行,並返回一個返回碼,返回碼為0正常退出,非0為非正常退出,例如:
•exit 0

if語句架構框架:   【必會技能】
單分支結構
if 條件測試
then 命令序列
fi

雙分支結構
if 條件測試
then 命令序列
else 命令序列
fi

多分支結構
if 條件測試1
then 命令序列

[elif 條件測試2
then 命令序列

elif 條件測試3 
then 命令序列]...

else 命令序列
fi

示例1:
read -p "確認開始安裝KVM [y]: " kvm_install
if [ ! "${kvm_install}" = "y" ];then
         echo -e "$red_col輸入不正確! $reset_col"
         exit
fi

當然你也可以這樣寫,但是這樣寫不太好看:
if list1;then list2;elif list3;then list4;else list5;fi;




示例2:  編寫指令碼輸出一個數值
[root@master ~]# cat shiyan7.sh      
#!/usr/bin/env bash 
if [ "$1" = "hello" ]; then      #新增hello值
	echo "Hello! How are you ?"
elif [ "$1" = "" ]; then        #新增空 值
	echo "You MUST input parameters"
else
	echo "The only accept parameter is hello"
fi

[root@master ~]# sh shiyan7.sh  hello     #輸入變數引數hello數值 則會列印出如下
Hello! How are you ?
[root@master ~]# sh shiyan7.sh      #後面未輸入引數,則代表輸入的是空,則會輸出如下內容
You MUST input parameters



示例3:  寫出一個輸入不同指令碼得出不同輸入結果的sh檔案
[root@master ~]# vim shiyan8.sh
#/usr/bash
echo "Press y to continue"
read yn
if [ "$yn" = "y" ]; then
echo "script is running..."
else
echo "STOP!"
fi
[root@master ~]# sh shiyan8.sh
Press y to continue
y      #輸入y 得出以下值
script is running...
[root@master ~]# sh shiyan8.sh
Press y to continue
yn    #輸入yn得出以下值
STOP!



多個條件聯合
邏輯與
	if [ $condition1 ] && [ $condition2 ]
	if [ $condition -a $condition2 ]
	if [[ $condition1 && $condition2 ]]

邏輯或
	if [ $condition1 ] || [ $condition2 ]
	if [ $condition -o $condition2 ]
	if [[ $condition1 || $condition2 ]]



case  命令  【這個也是一個重點模式,必須記住】

case 語句是 shell 中流控制的第二種方式,語法如下:

case 變數 in
    模式1)
     	命令序列1
     	;;
    模式2)
     	命令序列2
     	;;
    模式3)
     	命令序列3
     	;;
    *)
     	無匹配後命令序列
		;;
esac	

命令;;表明流應該跳轉到case語句的最後,類似C語言中的break指令。




示例4: 寫出一個多項選項的操作指令碼命令,比如abcd四個選項專案
[root@master ~]# cat shiyan9.sh 
#!/usr/bin/env bash
cat <<-EOF
+-------------------------------------------------------------------------+
|                       System_tools V1.0                                 |
+-------------------------------------------------------------------------+
|               a. Stop And Disabled Firewalld.                           |
|               b. Disabled SELinux Secure System.                        |
|               c. Install Apache Service.                                |
|               d. Quit                                                   |
+-------------------------------------------------------------------------+
EOF
printf "\e[1;31m Please input your select:  \e[0m" && read var
case "$var" in
  "a")
        systemctl stop firewalld && systemctl disable firewalld
        ;;
  "b")
        setenforce 0 && sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config
        ;;
  "c")
        yum -y install httpd httpd-tools
        echo "httpd is installd"
        ;;
  "d")
        exit
        ;;
  *)
        printf "請按照上方提供的選項輸入!!!\n"
        ;;
esac

[root@master ~]# sh shiyan9.sh    #執行指令碼
+-------------------------------------------------------------------------+
|                       System_tools V1.0                                 |
+-------------------------------------------------------------------------+
|               a. Stop And Disabled Firewalld.                           |
|               b. Disabled SELinux Secure System.                        |
|               c. Install Apache Service.                                |
|               d. Quit                                                   |
+-------------------------------------------------------------------------+
 Please input your select:  a
[root@master ~]# sh shiyan9.sh 
+-------------------------------------------------------------------------+
|                       System_tools V1.0                                 |
+-------------------------------------------------------------------------+
|               a. Stop And Disabled Firewalld.                           |
|               b. Disabled SELinux Secure System.                        |
|               c. Install Apache Service.                                |
|               d. Quit                                                   |
+-------------------------------------------------------------------------+
 Please input your select:  b
setenforce: SELinux is disabled




[root@master ~]# vim shiyan10.sh
#!/usr/bin/env bash
printf "\e[1;31m 確定要繼續刪除嗎 yes/no: \e[0m" && read var    #\e[1;31m 這個是給字型新增顏色
case $var in
  "yes"|"y"|'Y'|"YES")
          echo "繼續刪除"
          ;;
   *)
          echo "停止刪除"
          ;;
esac
[root@master ~]# sh shiyan10.sh    #執行指令碼
 確定要繼續刪除嗎 yes/no: yes    #選擇yes
繼續刪除



示例:使用函式實現選項條件內容,函式名字可自己隨意定義,如hepl () { }
[root@master ~]# vim shiyan10.sh   
#!/usr/bin/env bash

help () {
cat <<-EOF
System_tools V1.0          
h	顯示命令幫助       
f	顯示磁碟分割槽        
d	顯示磁碟掛載      
m	檢視記憶體使用
u	檢視系統負載    
q	退出程式         
EOF
}
help
printf "\e[5;7;31m 確定要繼續刪除嗎 yes/no: \e[0m" && read var
case $var in
     "h"|"H")
       help
       ;;
     "f"|"F")
      fdisk -l
       ;;
      "d"|"D")
      df -Th
       ;;
      "m"|"M")
       free -h
       ;;
      "u"|"U")
       uptime
       ;;
       "q"|"Q")
        exit
       ;;
esac

[root@master ~]# sh shiyan10.sh 
 確定要繼續刪除嗎 yes/no: yes
繼續刪除
[root@master ~]# vim shiyan11.sh
[root@master ~]# sh shiyan11.sh    #輸入得出以下選項內容
System_tools V1.0          
h	顯示命令幫助       
f	顯示磁碟分割槽        
d	顯示磁碟掛載      
m	檢視記憶體使用
u	檢視系統負載    
q	退出程式         
 確定要繼續刪除嗎 yes/no: h
System_tools V1.0          
h	顯示命令幫助       
f	顯示磁碟分割槽        
d	顯示磁碟掛載      
m	檢視記憶體使用
u	檢視系統負載    
q	退出程式     




案例4:簡單的JumpServer

		跳板主機
		1)mysql1
		2)mysql2
		3)bj-web1
		h)  help
		q)  exit



#跳板主機可以通過指令碼實現主機之間的來回切換
#!/usr/bin/env bash     
cat <<-EOF
 		跳板主機
		1)master2
		2)master3
		3)master4
		h)  help
		q)  exit
EOF
printf "\e[5;7;31m 請輸入密碼 : \e[0m" && read pass
if [ $pass = "ppp" ];then
  else 
  echo "password is error" && exit
fi
printf "\e[5;7;31m 請輸入你要連線的機器 : \e[0m" && read var
## 先免密和解析
case $var in
   1)
   ssh master2             
   ;;
   2)
   ssh master3
   ;;
   3)
   ssh master4
   ;;
   h)
   echo "you need help!!!"
   ;;
   q)
   exit
   ;;
esac 


[root@master ~]# sh shiyan12.sh 
跳板主機
1)master2
2)master3
3)master4
h)  help
q)  exit
 請輸入密碼 : ppp
 請輸入你要連線的機器 : master2



3. shell迴圈for於語句

迴圈次數是固定的

for i in {取值範圍}
do
	迴圈體
done




下面用for循壞語句採用的指令碼,【實戰技術非常強悍,需牢記】
示例1:  1-150數字求和
[root@master ~]# cat shiyan13.sh
#!/usr/bin/env bash
num=0
for i in {1..150}                      # {1..150}/(1,2,3,4,5, 100)/`seq 100`
do
   num=$[$num+$i]
done
echo $num
[root@master ~]# sh shiyan13.sh 
11325    					 #求和的結果
 

示例2:找出1-20中能被2整除的數字
[root@master ~]# cat sy1.sh
#!/usr/bash
for i in {1..20}
do
  a=$[$i%2]
  if [ $a -eq 0 ]; then
     echo $i
  fi
done

[root@master ~]# sh sy1.sh    #執行指令碼以下都是能被2整除的數字
2
4
6
8
10
12
14
16
18
20


示例3:檢測主機存活性,把229網段的主機能ping通的ip全部列出,當然還有一種更簡單的方法
採用:nmap -sP 192.168.229.0/24 |grep "report for"   #可直接在命令列過濾所有ip
[root@master ~]# cat sy2.sh    
#!/usr/bin/env bash
for i in {1..254}
do
 ( ping -c1 -W1 192.168.229.$i &>/dev/null
  if [ $? -eq 0 ]; then 
     echo "192.168.229.$i is tong"
  fi
 )&
done

[root@master ~]# sh sy2.sh 
192.168.229..1 is tong
192.168.229..2 is tong
192.168.229..51 is tong
192.168.229..53 is tong
192.168.229..54 is tong
192.168.229..52 is tong
192.168.229..61 is tong
192.168.229.100 is tong


示例4:計算檔案lxw.txt中數值之和
[root@master ~]# cat lxw.txt 
6324346
341241
34632151
34621125
36126
1512616
3461261
126162161253

[root@master ~]# cat sy3.sh 
#!/usr/bash
sum=0
for i in `cat lxw.txt`     #選擇檔案絕對路徑
do
  sum=$[$sum+$i]          #採用變數
done
echo $sum         #得出結果值
[root@master ~]# sh sy3.sh 
126243090119


示例5. C 語言風格的 求1-500之和
[root@master ~]# cat sy4.sh 
#!/usr/bash
for (( i=1; i<=500; i++))
do
   sum=$[$sum+$i]
done

echo $sum
[root@master ~]# sh sy4.sh 
125250



4.shell迴圈while語句

迴圈次數不一定是固定的
可以固定
可以不固定
while迴圈語句 框架如下:

while 條件
do
      迴圈體
done
==當條件測試成立(條件測試為真),執行迴圈體


##完善系統工具的輸出及操作性
#!/usr/bin/env bash
while 1>0
do
    cat <<-EOF
    +-------------------------------------------------------------------------+
    |                   System_tools V1.0                                     |
    +-------------------------------------------------------------------------+
    |           a. Stop And Disabled Firewalld.                               |
    |           b. Disabled SELinux Secure System.                            |
    |           c. Install Apache Service.                                    |
    |           d. Quit                                                       |
    +-------------------------------------------------------------------------+
EOF
    printf "\e[1;35m Please input your select: \e[0m" && read var

    case "$var" in
      "a")
        systemctl stop firewalld && systemctl disable firewalld
        ;;
      "b")
        sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config
        ;;
      "c")
        yum -y install httpd httpd-tools
        ;;
      "d")
        exit
        ;;
      *)
        printf "請按照上方提供的選項輸入!!!\n"
        ;;
    esac
    if [ $? -eq 0 ];then
        clear
    else
        printf "\e[1;31m Warning: Your program exist ERROR!!! \e[0m\n"
        break
    fi
done


示例:判斷是否是字母
[root@master ~]# vim sy2.sh 
#!/usr/bin/env bash
read -p "請輸入一個字母" var
if [ ${#var} -eq 1 ]; then
    echo "$var"
else
    echo "$var 不是一個字母"
    exit 4
fi

[root@master ~]# sh sy2.sh 
請輸入一個字母123
123 不是一個字母
[root@master ~]# sh sy2.sh 
請輸入一個字母abc
abc 不是一個字母
[root@master ~]# sh sy2.sh 
請輸入一個字母a
a



示例:用while迴圈列印出乘法表格
#!/usr/bin/env bash
num=9
row=1
while [ $row -le $num ] ;do
    col=1
    while [ $col -le $row ] ;do
        printf "$row*$col=$[$row*$col]\t"
        col=$[col+1]
    done
    echo
    row=$[row+1]
done

[root@master ~]# sh sy7.sh
1*1=1	
2*1=2	2*2=4	
3*1=3	3*2=6	3*3=9	
4*1=4	4*2=8	4*3=12	4*4=16	
5*1=5	5*2=10	5*3=15	5*4=20	5*5=25	
6*1=6	6*2=12	6*3=18	6*4=24	6*5=30	6*6=36	
7*1=7	7*2=14	7*3=21	7*4=28	7*5=35	7*6=42	7*7=49	
8*1=8	8*2=16	8*3=24	8*4=32	8*5=40	8*6=48	8*7=56	8*8=64	
9*1=9	9*2=18	9*3=27	9*4=36	9*5=45	9*6=54	9*7=63	9*8=72	9*9=81	

[root@master ~]# cat sy9.sh 
#!/usr/bash
i=1

while  [ $i -le 16 ]
do 
   n=16
   while [ $n -ge $i ] 
   do
     echo -n " "
     n=$[$n-1]
   done
   m=1
   while [ $m -le $i ]
   do
      echo -n "* "
      m=$[$m+1]
   done

  echo 
  i=$[$i+1]
done

[root@master ~]# sh sy9.sh 
                * 
               * * 
              * * * 
             * * * * 
            * * * * * 
           * * * * * * 
          * * * * * * * 
         * * * * * * * * 
        * * * * * * * * * 
       * * * * * * * * * * 
      * * * * * * * * * * * 
     * * * * * * * * * * * * 
    * * * * * * * * * * * * * 
   * * * * * * * * * * * * * * 
  * * * * * * * * * * * * * * * 
 * * * * * * * * * * * * * * * *




6.shell迴圈until語句

迴圈次數不一定是固定的
可以固定
可以不固定

until 條件
do
    迴圈體
done
==當條件測試成立(條件測試為假),執行迴圈體

[root@host ~]# cat until.sh
#!/bin/bash
x=1
until [ $x -ge 8 ]      
do   
      echo $x
      x=`expr $x + 1`
done
x=1
while [ ! $x -ge 10 ]
do
      echo $x
      x=`expr $x + 1`
done

[root@master ~]# sh sy6.sh     #執行指令碼 結果列印為1-7  1-9
1
2
3
4
5
6
7
1
2
3
4
5
6
7
8
9

7.shell迴圈控制shift、continue、break、exit

shift命令
位置引數可以用shift命令左移。比如shift 3表示原來的$4現在變成$1,原來的$5現在變成$2等等,原來的$1$2$3丟棄,$0不移動。不帶引數的shift命令相當於shift 1。
對於位置變數或命令列引數,其個數必須是確定的,或者當 Shell 程式不知道其個數時,可以把所有引數一起賦值給變數$*。
若使用者要求 Shell 在不知道位置變數個數的情況下,還能逐個的把引數一一處理,也就是在 $1 後為 $2,$2 後面為 $3,則需要用shift把所有引數變成$1



示例1:
[root@master ~]# cat sy10.sh
#!/usr/bin/env bash
a=1
until [ $# -eq 0 ]
do
echo "第$a個引數為: $1 引數個數為: $#"
shift
a=$[a+1]
done

[root@master ~]# sh sy10.sh  1 2 3 4 5 a
第1個引數為:1引數個數為:6
第2個引數為:2引數個數為:5
第3個引數為:3引數個數為:4
第4個引數為:4引數個數為:3
第5個引數為:5引數個數為:2
第6個引數為:a引數個數為:1
從上可知 shift 命令每次執行一次,變數的個數($#)減一,而變數值提前一位


示例2:用 until 和 shift 命令計算所有命令列引數的和。

[root@master ~]# cat  sy12.sh 
#!/usr/bash
sum=0
until [ $# -eq 0 ]
do
	sum=`expr $sum + $1`
	shift
done
echo "sum is: $sum"

[root@master ~]# sh sy12.sh  89 999 4564 5644
sum is: 11296     #求出的總和

 Shift 命令還有另外一個重要用途:
    Bash 定義了9個位置變數,從 $1$9,這並不意味著使用者在命令列只能使用9個引數,藉助 shift 命令可以訪問多於9個的引數。

  Shift 命令一次移動引數的個數由其所帶的引數指定。例如當 shell 程式處理完前九個命令列引數後,可以使用 shift 9 命令把 $10 移到 $1

8.Linux指令碼中的break continue exit return

Linux指令碼中的break continue exit return     【下面的引數能記住多少算多少,這些都是非常使用的引數】

break
結束並退出迴圈

continue
在迴圈中不執行continue下面的程式碼,轉而進入下一輪迴圈

exit
退出指令碼,
常帶一個整數給系統,如 exit 0

return
在函式中將資料返回
或返回一個結果給呼叫函式的指令碼

可理解為:break是立馬跳出迴圈;continue是跳出當前條件迴圈,繼續下一輪條件迴圈;exit是直接退出整個指令碼

例如:
在迴圈過程中,有時候需要在未達到迴圈結束條件時強制跳出迴圈,Shell使用兩個命令來實現該功能:breakcontinuebreak命令

break命令允許跳出所有迴圈(終止執行後面的所有迴圈)。

下面的例子中,指令碼進入死迴圈直至使用者輸入數字大於5。要跳出這個迴圈,返回到shell提示符下,需要使用break命令。

[root@master shell]# cat sy1.sh     #這個指令碼加了continue,不會跳出循壞,輸入比5大的數值即可跳出迴圈!
#!/usr/bash
while :
do 
	echo -n "input a number between 1 to 5:"
	read anum
	case $anum in
		1|2|3|4|5) echo "you number is $anum!"
		;;
		*) echo "you do not select a number between 1 to 5,game is over!"
			break
		;;
	esac
done
continue

[root@master shell]# sh sy1.sh 
input a number between 1 to 5:1
you number is 1!
input a number between 1 to 5:2
you number is 2!
input a number between 1 to 5:3
you number is 3!
input a number between 1 to 5:4
you number is 4!
input a number between 1 to 5:6
you do not select a number between 1 to 5,game is over
continue命令與break命令類似,只有一點差別,它不會跳出所有迴圈,僅僅跳出當前迴圈。


示例: 對上面的例子進行修改:新增continue這個引數就可以陷入無限迴圈!!!!
[root@master shell]# vim sy2.sh
#!/bin/bash
while :
do
    echo -n "Input a number between 1 to 5: "
    read aNum
    case $aNum in
        1|2|3|4|5) echo "Your number is $aNum!"
        ;;
        *) echo "You do not select a number between 1 to 5!"
            continue
            echo "Game is over!"
        ;;
    esac
done

[root@master shell]# sh sy2.sh 
Input a number between 1 to 5: 1
Your number is 1!
Input a number between 1 to 5: 2
Your number is 2!
Input a number between 1 to 5: 3
Your number is 3!
Input a number between 1 to 5: 4
Your number is 4!
Input a number between 1 to 5: ^C
[root@master shell]# 

執行程式碼發現,當輸入大於5的數字時,該例中的迴圈不會結束,語句

 程式碼如下:
echo "Game is over!"


#實戰--shell版本jumpserver開發  模板,測試自己的搭建shell指令碼思路
#!/usr/bin/env bash
#
# Author: 

可以先新增上賬密驗證環節

while :
do
	trap ':' INT EXIT TSTP TERM HUP
	clear
	cat <<-EOF
	+-------------------------------------+
	|	JumpServer @Version1.0	          |
	+-------------------------------------+
	|    a. WebServer Apache.	          |
	|    b. MySQL Databases Server.	      |
	|    c. PHP Development Computer.     |
	|    d. Quit			              |
	+-------------------------------------+
EOF
	read -p "Please input your jump to server's number: " computer
	case $computer in
	a)
		ssh jumper@192.168.229.62                  //可以巢狀子case迴圈
		;;
	b)
		ssh jumper@192.168.229.63
		;;
	c)
		ssh jumper@192.168.229.64
		;;
	d)
		exit
		;;
	*)
		printf "ERROR: Please redo your select!"
		;;
	esac
done




三.shell函式與正則 【重中之重高階進階版】

1.shell程式設計之函式

函式,是搭建整個shell指令碼的一個強大得多功能,之前我們學的都是如何搭建區域性模式,正如蓋一棟房子,只把邊邊角角建築好,接下來就需要各大部門的協調整合才能進行一個竣工驗收的效果,函式正式如此!
 
 function (功能) 功能函式

定義函式
呼叫函式
取消函式
函式傳參 

名稱空間 
    local
返回值 
    return value
    value不能超過0-255

shell函式function

1.函式宣告

function_name () { 
   list of commands
}

函式名 function_name,這就是你將使用它從其他地方在你的指令碼呼叫。
取消函式
unset myfunc	//取消函式


示例1:通過函式調取字元值
[root@master shell]# cat sy3.sh 
#!/usr/bash
myfunc()   #新增函式,可隨意定義
{
echo “This is my first shell function}
myfunc		   #呼叫函式,這一步必須有,比如你新增是個函式就得在最後呼叫十個函式
[root@master shell]# sh sy3.sh 
“This is my first shell function”



2.函式傳參
在Shell中,呼叫函式時可以向其傳遞引數。在函式體內部,通過 $n 的形式來獲取引數的值,例如,$1表示第一個引數,$2表示第二個引數
示例:
[root@master shell]# cat sy4.sh 
#!/bin/bash
funParam(){
    echo "第一個引數為 $1 !"
    echo "第二個引數為 $2 !"
    echo "第十個引數為 $10 !"
    echo "第十個引數為 ${10} !"
    echo "第十一個引數為 ${11} !"
    echo "引數總數有 $# 個!"
    echo "作為一個字串輸出所有引數 $* !"
}
funParam 1 2 3 4 5 6 7 8 9 34 73
[root@master shell]# sh sy4.sh     #把函式引數進行引數傳遞呼叫
第一個引數為 1 !
第二個引數為 2 !
第十個引數為 10 !
第十個引數為 34 !
第十一個引數為 73 !
引數總數有 11 個!
作為一個字串輸出所有引數 1 2 3 4 5 6 7 8 9 34 73 !

2.shell程式設計之陣列


陣列中可以存放多個值。Bash Shell 只支援一維陣列(不支援多維陣列),初始化時不需要定義陣列大小(與 PHP 類似)。

與大部分程式語言類似,陣列元素的下標由0開始。

陣列、陣列名稱、陣列成員(a[0])、下標、陣列成員值  

陣列定義:
[root@master shell]# cat sy5.sh    
#!/usr/bash     
declare -a myarry=(5 6 7 8)
     echo ${myarry[3]}     #這裡選擇陣列從上面數字6開始為第一個,選擇3則是第8個,所以輸出結果等於8
[root@master shell]# sh sy5.sh
8


定義方法2:
    # array=( one two three four five six )
    # array2=(tom jack alice)
    # array3=(`cat /etc/passwd`)	       將該檔案中的每一個行作為一個元素賦值給陣列array3
    # array4=(`ls /var/ftp/Shell/for*`)
    # array5=(tom jack alice "bash shell")
    # colors=($red $blue $green $recolor)         //先給變數賦值 或者重新定義陣列
    # array5=(1 2 3 4 5 6 7 "linux shell" [20]=saltstack)   //直接指定下標位置,陣列下標不一定連續,可以為空

定義方法3:
   #!/bin/bash
   area[11]=23
   area[13]=37
   area[51]="UFOs"

#  陣列成員不必一定要連貫或連續的. 
#  陣列的一部分成員允許不被初始化.
#  陣列中空缺元素是允許的.

 訪問陣列
當您設定任何陣列變數,並可訪問它,如下所示:
${array_name[index]}
示例:
[root@master shell]# cat sy6.sh 
#!/bin/sh
NAME[0]="BJ"
NAME[1]="SH"
NAME[2]="SZ"
NAME[3]="GZ"
NAME[4]="HZ"
echo "First Index: ${NAME[0]}"     #這裡是可以呼叫函式陣列比如0對應BJ
echo "Second Index: ${NAME[1]}"

[root@master shell]# sh sy6.sh
First Index: BJ
Second Index: SH


您可以訪問陣列中的所有專案通過以下方式之一:
${array_name[*]}
${array_name[@]}

[root@master shell]# cat sy7.sh 
#!/usr/bin
NAME[0]="BJ"
NAME[1]="SH"
NAME[2]="SZ"
NAME[3]="GZ"
NAME[4]="HZ"
echo "First Index: ${NAME[*]}"     #調取所有
echo "Second Index: ${NAME[@]}"

[root@master shell]# sh sy7.sh 
First Index: BJ SH SZ GZ HZ
Second Index: BJ SH SZ GZ HZ

'疑難點
shell陣列中"*" 和 "@" 區別
關於在shell指令碼中陣列變數中 “*”跟 “@” 區別
“*”當變數加上“” 會當成一串字串處理.
“@”變數加上“” 依然當做陣列處理.
在沒有加上“” 的情況下 效果是等效的.'


示例:
[root@master shell]# cat sy8.sh
#!/usr/bin/env bash
array=('gz' 'cloud' '19')
echo "case 1"
for line in "${array[@]}"
do
echo $line
done
echo "case 2"
for line in "${array[*]}"
do
echo $line
done
echo "case 3"
for line in ${array[*]}
do
echo $line
done
echo "case 4"
for line in ${array[@]}
do
echo $line
done

[root@master shell]# sh sy8.sh 
case 1
gz
cloud
19
case 2
gz cloud 19
case 3
gz
cloud
19
case 4
gz
cloud
19


3. 正規表示式RE [重點正規表示式,必會技能]

正規表示式基本元字元
正規表示式擴充元字元

正規表示式(regular expression, RE)是一種字元模式, 用於在查詢過程中匹配指定的字元. 在大多數程式裡, 正規表示式都被置於兩個正斜槓之間;

例如/l[oO]ve/就是由正斜槓界定的正規表示式, 它將匹配被查詢的行中任何位置出現的相同模式. 在正規表示式中,元字元是最重要的概念

正規表示式與萬用字元:

    1.正規表示式一般用於處理文字內容,常用命令有grep,sed,awk,vim等

        萬用字元一般用於匹配檔名,常用命令有find,ls,cp等

    2.各符號的含義不盡相同.

什麼地方使用正規表示式

    vim grep sed awk  nginx apache mail垃圾郵件過濾。。。  perl java python 等等都使用正則

構成  

    1.元字元(基本元字元、擴充套件元字元)        

    2.除元字元之外的任意字元都是表示他字面意思的正規表示式


正規表示式的匹配過程

正規表示式是按照從表示式最左端第一個字元開始,左到右依次一個一個字元進行匹配.當字串中有字元成功匹配到正規表示式中字元,則從這個位置開始嘗試正規表示式中的下一個字元進行匹配,如果匹配成功則繼續從這個位置開始匹配正規表示式中下一個字元;如果匹配不成功,則“回溯”到第一次匹配的字元處重新從正規表示式中第一個字元開始匹配。

No.1 正規表示式基本元字元 【以下所有的引數最好能記住多少記住多少,在企業實戰是最能體現工作效益的】

基本正規表示式元字元
元字元							  功能					 			示例	
^								行首定位符							^love	
$								行尾定位符		 					love$		
.								匹配單個字元						    l..e
*								匹配前導符0到多次				    ab*love
.*                  			任意多個字元
[]								匹配方括號中的任意一個字元			[lL]ove
[ - ]							匹配指定範圍內的一個字元	    	    [a-z0-9]ove
[^]				    			匹配不在指定組內的字元				 [^a-z0-9]ove
\								用來轉義元字元				   		 love\.	
\<			        			詞首定位符							 \<love
\>				    			詞尾定位符							 love\>


:% s/172.16.130.1/172.16.130.5/
:% s/\(172.16.130.\)1/\15/
:% s/\(172.\)\(16.\)\(130.\)1/\1\2\35/
:3,9 s/\(.*\)/#\1/

x\{m\}							字元x重複出現m次                  o\{5\}
x\{m,\}		    				字元x重複出現m次以上              o\{5,\}		
x\{m,n\}						字元x重複出現m到n次			   	o\{5,10\}	



No.2 正規表示式擴充元字元

powershell
擴充套件正規表示式元字元
+								匹配一個或多個前導字元			[a-z]+ove	
?								匹配零個或一個前導字元			lo?ve	
a|b				    			匹配a或b					 love|hate	
()								組字元loveable|rs   love(able|rs)	ov+  ov+ (ov)+
(..)(..)\1\2	    			標籤匹配字元		   			(love)able\1er
x{m}			    			字元x重複m次					 o{5	
x{m,}							字元x重複至少m次				o{5,}
x{m,n}		   					字元x重複m到n次				 o{5,10}

編寫正規表示式的3 個步驟:
    1 知道要匹配的內容以及它如何出現在文字中。
    2 編寫一個模式來描述要匹配的內容
    3 測試模式來檢視它匹配的內容,不能錯,不能漏,不能多

shell文字處理三劍客 【重點戲文字處理三劍客】

1.正規表示式RE

正規表示式RE
========================================================
重要的文字處理工具:vim、sed、awk、grep

一、什麼是正規表示式?
正規表示式(regular expression, RE)是一種字元模式,用於在查詢過程中匹配指定的字元。
在大多數程式裡,正規表示式都被置於兩個正斜槓之間;例如/l[oO]ve/就是由正斜槓界定的正規表示式,它將匹配被查詢的行中任何位置出現的相同模式。在正規表示式中,元字元是最重要的概念。

匹配數字:     ^[0-9]+$                                       123 456 5y7
匹配Mail: 	[a-z0-9_]+@[a-z0-9]+\.[a-z]+	              tigerfive2010@126.com
匹配IP:		[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}
				
[root@master shell]# egrep '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' /etc/sysconfig/network-scripts/ifcfgens33
IPADDR=172.16.100.1
NETMASK=255.255.255.0
GATEWAY=172.16.100.254
[root@master shell]# egrep '[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}' /etc/sysconfig/network-scripts/ifcfg-ens33 
IPADDR=172.16.100.1
NETMASK=255.255.255.0
GATEWAY=172.16.100.254


二、元字元
定義:元字元是這樣一類字元,它們表達的是不同於字面本身的含義
shell元字元(也稱為萬用字元)	   由shell來解析,如rm -rf *.pdf,元字元* Shell將其解析為任意多個字元
正規表示式元字元				   由各種執行模式匹配操作的程式來解析,比如vi、grep、sed、awk、python

[root@master shell]# rm -rf *.pdf

[root@master shell]# grep 'abc*' /etc/passwd
abrt:x:173:173::/etc/abrt:/sbin/nologin

vim示例:
:1,$ s/tom/David/g		       //如tom、anatomy、tomatoes及tomorrow中的“tom”被替換了,而Tom確沒被替換
:1,$ s/\<[Tt]om\>/David/g
                               

1. 正規表示式元字元:
===基本正規表示式元字元
元字元			功能										    示例	
========================================================	
^					行首定位符							    ^love	
$					行尾定位符							    love$		
.					匹配單個字元						     	l..e		
*					匹配前導符0到多次				        ab*love
.*                  任意多個字元
[]					匹配指定範圍內的一個字元		          [lL]ove
[ - ]				匹配指定範圍內的一個字元		          [a-z0-9]ove
[^]				    匹配不在指定組內的字元		        	[^a-z0-9]ove
\					用來轉義元字元						    love\.	
\<			        詞首定位符							    \<love						
\>				    詞尾定位符							    love\>						
\(..\)				匹配稍後使用的字元的標籤	      :% s/172.16.130.1/172.16.130.5/
																	:% s/\(172.16.130.\)1/\15/
																	:% s/\(172.\)\(16.\)\(130.\)1/\1\2\35/
																	:3,9 s/\(.*\)/#\1/	
																	
x\{m\}			字元x重複出現m次					    o\{5\}
x\{m,\}		    字元x重複出現m次以上			        o\{5,\}						
x\{m,n\}		字元x重複出現m到n次			    	o\{5,10\}					

===擴充套件正規表示式元字元
+					匹配一個或多個前導字元			[a-z]+ove	
?					匹配零個或一個前導字元			lo?ve	
a|b				    匹配a或b								    love|hate
()					組字元									    loveable|rs    love(able|rs)	ov+  ov+ (ov)+
(..)(..)\1\2	    標籤匹配字元						    (love)able\1er
x{m}			    字元x重複m次							o{5}		
x{m,}				字元x重複至少m次				        o{5,}
x{m,n}		  	    字元x重複m到n次					    o{5,10}

2. POSIX字元類:
表示式       	  功能                              		    示例
[:alnum:]        字母與數字字元                            [[:alnum:]]+  
[:alpha:]  	     字母字元(包括大小寫字母)	                 [[:alpha:]]{4}
[:blank:]        空格與製表符                         	 [[:blank:]]*
[:digit:]        數字字母                                 [[:digit:]]?
[:lower:]        小寫字母                                  [[:lower:]]{5,}
[:upper:]        大寫字母                            	  [[:upper:]]+
[:punct:]        標點符號                                  [[:punct:]]
[:space:]        包括換行符,回車等在內的所有空白            [[:space:]]+




正則匹配示例:vim
/love/			               	
/^love/                           love開頭的行
/love$/                           love結尾的行
/l.ve/                            l和ve間可以存在任意一個字元
/lo*ve/                           l和ve間可以存在任意多個o
/[Ll]ove/                         Love love 
/love[a-z]/                       lovea loveb .. lovez
/love[^a-zA-Z0-9]/                love_ love/ 

/.*/                              整行
/^$/                              空行  /^[ ]*$/        
/^[A-Z]..$/	                      一個大寫字母加兩個任意字元的行
/^[A-Z][a-z]*3[0-5]/             
/[a-z]*\./	                      任意多個小寫字母加上一個.
/^ *[A-Z][a-z][a-z]$/	          
/^[A-Za-z]*[^,][A-Za-z]*$/	
/\<fourth\>/		
/\<f.*th\>/	                      f為詞首 th為詞尾的任意單詞	
/5{2}2{3}\./                      55222.
/^$/              					空行 
/^[ \t]*$/	     					 空行
	
註釋行
/^#/
/^[ \t]*#/	
:1,$ s/\([Oo]ccur\)ence/\1rence/	
:1,$ s/\(square\) and \(fair\)/\2 and \1/	




2.shell變成之grep


grep家族
========================================================

grep:  	在檔案中全域性查詢指定的正規表示式,並列印所有包含該表示式的行
egrep: 	擴充套件的egrep,支援更多的正規表示式元字元
fgrep: 	固定grep(fixed grep),有時也被稱作快速(fast grep),它按字面解釋所有的字元


一、grep命令格式
grep [選項] PATTERN filename filename ...
# grep 'Tom' /etc/passwd
# grep 'bash shell' /etc/test	
找到:				        grep返回的退出狀態為0
沒找到:				    grep返回的退出狀態為1
找不到指定檔案:	    grep返回的退出狀態為2

grep 程式的輸入可以來自標準輸入或管道,而不僅僅是檔案,例如:
# grep 'tom'
# ps aux |grep 'sshd'
# ll |grep '^d'
# grep 'alice' /etc/passwd /etc/shadow /etc/group


二、grep使用的元字元
grep:						使用基本元字符集	^, $, ., *, [], [^], \< \>,\(\),\{\}, \+, \|
egrep(或grep -E):	使用擴充套件元字符集	?, +, { }, |, ( )
注:grep也可以使用擴充套件集中的元字元,僅需要對這些元字元前置一個反斜線

\w					        所有字母與數字,稱為字元[a-zA-Z0-9]		 'l[a-zA-Z0-9]*ve'	        'l\w*ve'
\W				        所有字母與數字之外的字元,稱為非字元	'love[^a-zA-Z0-9]+' 	    'love\W+'
\b					        詞邊界													     '\<love\>'		            '\blove\b'						


三、grep 示例
grep -E 或 egrep
# egrep 'NW' datafile	
# egrep 'NW' d*		
# egrep '^n' datafile
# egrep '4$' datafile
# egrep TB Savage datafile
# egrep 'TB Savage' datafile
# egrep '5\..' datafile	
# egrep '\.5' datafile		
# egrep '^[we]' datafile
# egrep '[^0-9]' datafile	
# egrep '[A-Z][A-Z] [A-Z]' datafile
# egrep 'ss* ' datafile		
# egrep '[a-z]{9}' datafile
# egrep '\<north' datafile
# egrep '\<north\>' datafile	
# egrep '\<[a-r].*n\>' datafile
# egrep '^n\w*\W' datafile	
# egrep '\bnorth\b' datafile

# egrep 'NW|EA' datafile
# egrep '3+' datafile
# egrep '2\.?[0-9]' datafile	
# egrep '(no)+' datafile
# egrep 'S(h|u)' datafile
# egrep 'Sh|u' datafile


三、grep選項
-i, --ignore-case					忽略大小寫
-l, --files-with-matches		只列出匹配行所在的檔名
-n, --line-number				在每一行前面加上它在檔案中的相對行號
-c, --count							顯示成功匹配的行數
-s, --no-messages				禁止顯示檔案不存在或檔案不可讀的錯誤資訊
-q, --quiet, --silent				靜默--quiet, --silent
-v, --invert-match				反向查詢,只顯示不匹配的行
-R, -r, --recursive				遞迴針對目錄
--color								顏色
-o, --only-matching			只顯示匹配的內容
-B, --before-context=NUM  print NUM lines of leading context
-A, --after-context=NUM   	 print NUM lines of trailing context
-C, --context=NUM         	 print NUM lines of output context


示例:
[root@master ~]# egrep 'ifcfg' /etc/*          檔案
[root@master ~]# grep -R 'ifcfg' /etc           目錄

[root@master ~]# egrep 'root' /etc/passwd /etc/shadow /etc/hosts
/etc/passwd:root:x:0:0:root:/root:/bin/bash
/etc/passwd:operator:x:11:0:operator:/root:/sbin/nologin
/etc/shadow:root:$6$gcO6Vp4t$OX9LmVgpjtur67UQdUYfw7vJW.78.uRXCLIxw4mBk82Z99:7:::

[root@master ~]# egrep -l 'root' /etc/passwd /etc/shadow /etc/hosts
/etc/passwd
/etc/shadow

[root@master ~]# egrep -n 'root' /etc/passwd /etc/shadow /etc/hosts 
/etc/passwd:1:root:x:0:0:root:/root:/bin/bash
/etc/passwd:11:operator:x:11:0:operator:/root:/sbin/nologin
/etc/shadow:1:root:$6$gcO6Vp4t$OX9LmVgpjtur67UQdUy8.M78.uRXCLIxw4mBk82ZrNlxyf54

[root@master ~]# egrep '54:04:A6:CE:C2:1F' /etc/sysconfig/*
[root@master ~]# egrep 'ifcfg' /etc/*           檔案
[root@master ~]# grep -R 'ifcfg' /etc           目錄
[root@master ~]# egrep -R '54:04:A6:CE:C2:1F' /etc/sysconfig/

[root@master ~]# egrep '^IPADDR' /etc/sysconfig/network-scripts/ifcfg-eth0 |egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
192.168.2.254
[root@master ~]# egrep '^IPADDR' /etc/sysconfig/network-scripts/ifcfg-eth0 |egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}'
192.168.2.254
[root@master ~]#  grep -r 'ifcfg' ./* -l       #謹慎使用
[root@master ~]# grep --help |grep '\-R'
  -R, -r, --recursive       equivalent to --directories=recurse
  
[root@master ~]#  grep --help |egrep -A5 '\-R'

shell程式設計之SED [強調實戰必備技能之一]

文字的操作
sed是一個“非互動式的”面向字元流的編輯器。
awk是一種負責模式匹配的程式設計語言,,的典型示例是將資料轉換成格式化的報表。

sed  stream editor
    是一種文字編輯器,預設情況下是不會修改原檔案的。
    也是一種非互動式的編輯器
    
 
工作原理
    sed 是一種線上的、非互動式的編輯器,它一次處理一行內容。處理時,把當前處理的行儲存在臨時緩衝區
中,稱為“模式空間”(pattern space),接著用sed命令處理緩衝區中的內容,處理完成後,把緩衝區的內容
送往螢幕。接著處理下一行,這樣不斷重複,直到檔案末尾。檔案內容並沒有改變,除非你使用重定向儲存輸出。
Sed主要用來自動編輯一個或多個檔案;簡化對檔案的反覆操作;編寫轉換程式等。


  語法:
    sed    [選項]   '行的定位 動作'  檔案...
    sed [options] 'command' file(s)
    sed [options] -f scriptfile file(s)
注:
sed和grep不一樣,不管是否找到指定的模式,它的退出狀態都是0
只有當命令存在語法錯誤時,sed的退出狀態才是非0 

            
支援正規表示式
	與grep一樣,sed在檔案中查詢模式時也可以使用正規表示式(RE)和各種元字元。正規表示式是
括在斜槓間的模式,用於查詢和替換,以下是sed支援的元字元。
使用基本元字符集	^, $, ., *, [], [^], \< \>,\(\),\{\}
使用擴充套件元字符集	?, +, { }, |, ( )

使用擴充套件元字元的方式:
\+
sed -r
  
==地址(定址)  
  模式:
    1. 空模式,表示所有的行都執行動作
    2. 以行號作為模式
        1). 單獨的行號
             如:1   就是處理第1行
                 $   處理最後一行
        2). 起始行,結束行
              如:1,5     處理第1到5行
        3). 起始行~步長
              每隔多少行操作一次
              如:2~2 從第2行開始,每隔1行
        4). 起始位置,+N
             表示從起始位置開始,後面的N行都進行處理
             如:3,+5   處理3-8行
    3. 以正則作為模式             
        1). /正規表示式/
            如:/^root/
        2). /正規表示式1/,/正規表示式2/
           表示從第一次匹配到正規表示式1開始到第一次匹配到正規表示式2之間的所有行
              如:/^bin/,/sh$/
==動作        
  動作-----處理命令:   ! 非 : 放在命令前面表示取反
    1.  d    刪除                                delete               
    2.  p    列印                                print
    3.  r    讀取                                read              
    4.  w    寫                                  write
    5.  a    追加   在匹配的行下面插入內容           append   
    6.  i    插入   在匹配行的上一行插入內容         insert
    7.  c    修改    本行替換                      change
    8.  y    轉換的命令,一一對應轉換
    9.  n    處理下一行                            next
    10. q    退出,不會再向模式空間讀入新的行          quit
    11. s    查詢替換
             '模式s/舊的內容(正規表示式)/替換內容(新的內容)/[修飾符]'
             修飾符:
                g:全域性替換
                n:n為數字,1-512    替換第n個匹配到的內容
                p:列印      -n
                w:把處理過的行寫入到另一個檔案
     12. l   列出非列印字元

擴充套件:
h				把模式空間裡的內容複製到暫存緩衝區(覆蓋)
H				把模式空間裡的內容追加到暫存緩衝區
g				取出暫存緩衝區的內容,將其複製到模式空間,覆蓋該處原有內容
G				取出暫存緩衝區的內容,將其複製到模式空間,追加在原有內容後面
x				交換暫存緩衝區與模式空間的內容


    \u   upper   大寫字母
    \l    lower    小寫字母
   將檔案中所有小寫字母替換成大寫字母:
    # sed  's/[a-z]/\u&/g'  /tmp/pass
    
==選項:
        -n:靜默輸出,關閉模式空間的輸出,不會輸出未匹配到的行 一般與p命令結合使用
        -e:允許進行多項編輯,也就是說對同一行做多次處理、. 可以做多點編輯  
                -e '動作1'  -e '動作2'   ==  '動作1;動作2'
        -f: 後接sed指令碼,指定執行的sed指令碼(將模式動作寫到檔案中)
        -r:允許使用擴充套件正則
        -i:直接作用於原檔案    沒有輸出  在使用-i之前一定先不加-i看看效果
               -i.bak:修改原檔案之前進行備份
               
控制流
	 !  命令取反 例: 1!d 刪除第一行以外的行
	 {} 命令組合 命令用分號分隔 {1h;G} 可以理解為 -e 引數的另一種寫法
	 =  列印行號(輸入行的號碼,而非處理的次數行號) 例如: sed -n '2{=;p}' infile
	  n  讀入下一行到模式空間 例:'4{n;d}' 刪除第5行
	  N  追加下一行到模式空間.
      P  輸出多行模式空間的第一部分,直到第一個嵌入的換行符為止。在執行完指令碼的最後一個命令之後,模式空間的內容自動輸出。P命令經常出現在N命令之後和D命令之前。
	  d: 刪除pattern中的所有⾏行,並讀入下一新行到P中  [P來表示模式空間,M來表示保持空間]
      D:D 刪除M ,P中的第一行,不讀入下一行           [P來表示模式空間,M來表示保持空間]
	  這三個命令能建立一個輸入. 輸出迴圈,用來維護兩行模式空間,但是一次只輸出一行。
      這個迴圈的目的是隻輸出模式空間的第一行,然後返回到指令碼的頂端將所有的命令應用於模式空間的第二行。沒有這個迴圈,當執行指令碼中的最後一個命令時,模式空間中的這兩行都將被輸出。


 
#### sed命令示例
刪除命令:d
# sed -r '3d' datafile
# sed -r '3{d;}' datafile
# sed -r '3{d}' datafile

# sed -r '3,$d' datafile
# sed -r '$d' datafile
# sed -r '/north/d' datafile		
# sed -r '/sout/d' datafile

替換命令:s
# sed -r 's/west/north/g' datafile	
# sed -r 's/^west/north/' datafile	
# sed -r 's/[0-9][0-9]$/&.5/' datafile				//&代表在查詢串中匹配到的內容
# sed -r 's/Hemenway/Jones/g' datafile	
# sed -r 's/(Mar)got/\1ianne/g' datafile	
# sed -r 's#3#88#g' datafile			

讀檔案命令:r
# sed -r '/Suan/r /etc/newfile' datafile	
# sed -r '2r /etc/hosts' a.txt		
# sed -r '/2/r /etc/hosts' a.txt

寫檔案命令:w
# sed -r '/north/w newfile' datafile		
# sed -r '3,$w /new1.txt' datafile

追加命令:a
# sed -r '2a\1111111111111' /etc/hosts
# sed -r '2a\1111111111111\
> 222222222222\
> 333333333333' /etc/hosts

插入命令:i
# sed -r '2i\1111111111111' /etc/hosts
# sed -r '2i111111111\
> 2222222222\
> 3333333333' /etc/hosts

修改命令:c
# sed -r '2c\1111111111111' /etc/hosts
# sed -r '2c\111111111111\
> 22222222222\
> 33333333333' /etc/hosts

獲取下一行命令:n
# sed -r '/eastern/{ n; d }' datafile
# sed -r '/eastern/{ n; s/AM/Archile/ }' datafile			

暫存和取用命令:h H g G
# sed -r '1h;$G' /etc/hosts
# sed -r '1{h;d};$G' /etc/hosts
# sed -r '1h; 2,$g' /etc/hosts
# sed -r '1h; 2,3H; $G' /etc/hosts

暫存空間和模式空間互換命令:x
# sed -r '4h; 5x; 6G' /etc/hosts

反向選擇: !
# sed -r '3d' /etc/hosts
# sed -r '3!d' /etc/hosts


多重編輯選項:-e
# sed -r -e '1,3d' -e 's/Hemenway/Jones/' datafile	
# sed -r '1,3d; s/Hemenway/Jones/' datafile	

# sed -r '2s/WE/1000phone/g; 2s/Gray/YYY/g' datafile
# sed -r '2{s/WE/1000phone/g; s/Gray/YYY/g}' datafile
	

七、sed常見操作
刪除配置檔案中#號註釋行
sed -ri '/^#/d' file.conf 
sed -ri '/^[ \t]*#/d' file.conf

刪除配置檔案中//號註釋行 
sed -ri '\Y^[ \t]*//Yd' file.conf

刪除無內容空行 
sed -ri '/^[ \t]*$/d' file.conf


刪除註釋行及空行:
# sed -ri '/^[ \t]*#/d; /^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
# sed -ri '/^[ \t]*#|^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
# sed -ri '/^[ \t]*($|#)/d' /etc/vsftpd/vsftpd.conf

修改檔案:
# sed -ri '$a\chroot_local_user=YES' /etc/vsftpd/vsftpd.conf
# sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config
# sed -ri '/UseDNS/cUseDNS no' /etc/ssh/sshd_config
# sed -ri '/GSSAPIAuthentication/cGSSAPIAuthentication no' /etc/ssh/sshd_config

給檔案行新增註釋:
# sed -r '2,6s/^/#/' a.txt
# sed -r '2,6s/(.*)/#\1/' a.txt
# sed -r '2,6s/.*/#&/' a.txt                                             &匹配前面查詢的內容






示例二:
示例檔案 
file1.txt
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

用Massachusetts替換MA:
#sed 's/MA/Massachusetts/' file1.txt

使用多重指令:
# sed 's/ MA/, Massachusetts/ ; s/ PA/, Pennsylvania/' file1.txt
或者:
#sed -e's/ MA/, Massachusetts/' -e's/ PA/, Pennsylvania/' file1.txt

使用指令碼檔案:
指令碼:namestate
s/ MA/, Massachusetts/
s/ PA/, Pennsylvania/
s/ CA/, California/
s/ VA/, Virginia/
s/ OK/, Oklahoma/

$ sed -f namestate file1.txt

儲存輸出:
$ sed -f namestate file1.txt > newfile.txt

阻止輸入行自動顯示:
$ sed -n 's/MA/Massachusetts/p' file1.txt

常見出錯資訊:
‘’不匹配
s/src/dst/ 缺少最後的“/”




sed流編輯器用法及解析

sed: stream editor(流編輯器)的縮寫. 它們最常見的用法是進行文字的替換. 

[root@master ~]# sed '1d' passwd 	//刪除檔案的第1行
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    
[root@master ~]# sed '1,2d' passwd 	//刪除檔案的第1,2行
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    
    
[root@master ~]# cat e.txt 
    /etc/abc/456
    etc
[root@master ~]# sed -r 's#/etc/abc#/var/lib#' e.txt 
    /var/lib/456
    etc
    
[root@master ~]# cat passwd 
    root:x:0:0:root:/root:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@master ~]# sed '2,$d' passwd		//刪除第2行到最後一行s
    root:x:0:0:root:/root:/bin/bash
[root@master ~]# sed '/root/d' passwd 	//匹配到root,刪除此行
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@master ~]# sed '/root/,2d' passwd 	//匹配到root行,到此行的第2行
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    
  [root@master ~]# cat -n passwd 
         1  root:x:0:0:root:/root:/bin/bash
         2  bin:x:1:1:bin:/bin:/sbin/nologin
         3  daemon:x:2:2:daemon:/sbin:/sbin/nologin
         4  adm:x:3:4:adm:/var/adm:/sbin/nologin
         5  lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
         6  sync:x:5:0:sync:/sbin:/bin/sync
         7  shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
         8  halt:x:7:0:halt:/sbin:/sbin/halt
         9  mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
        10  operator:x:11:0:operator:/root:/sbin/nologin
   [root@master ~]# sed '1~2d' passwd 		//刪除奇數行
    bin:x:1:1:bin:/bin:/sbin/nologin
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    sync:x:5:0:sync:/sbin:/bin/sync
    halt:x:7:0:halt:/sbin:/sbin/halt
    operator:x:11:0:operator:/root:/sbin/nologin
    
   [root@master ~]# sed '0~2d' passwd 		//刪除偶數行
    root:x:0:0:root:/root:/bin/bash
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
    shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
    mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
    




1. sed可以從stdin中讀取內容
       $ cat filename | sed 's/pattern/replace_string/'
2. 選項-i會使得sed用修改後的資料替換原檔案
       $ sed -i 's/pattern/replace_string/' filename
3. g標記可以使sed執行全域性替換
       $ sed 's/pattern/replace_string/g' filename
4. g標記可以使sed匹配第N次以後的字元被替換
       $ echo "thisthisthisthis" | sed 's/this/THIS/2g'
5. sed中的分隔符可以替換成別的字元, 因為s標識會認為後面的字元為分隔符
       $ sed 's:text:replace_text:'
       $ sed 's|text|replace_text|'
6. sed可以利用指令來刪除檔案中的空行
       $ sed '/^$/d' filename
7. 替換指定的字串或數字
       $ cat sed_data.txt
       11 abc 111 this 9 file contains 111 11 99 numbers 0000
       
       $ sed -i 's/\b[0-9]\{3\}\b/NUMBER/g' sed_data.txt
       $ cat sed_data.txt
       11 abc NUMBER this 9 file contains NUMBER 11 99 numbers 0000
8. 由於在使用-i引數時比較危險, 所以我們在使用i引數時在後面加上.bak就會產生一個備份的檔案,以防後悔
       $ sed -i.bak 's/pattern/replace_string/' filename
9. sed如果在指令碼中使用的話, 不可避免的要呼叫變數, 所以以下這種方式可以用來呼叫變數即‘’換成了“”
       $ text=hello
       $ echo "hello world" | sed "s/$text/HELLO/"
10. 模式空間保持空間示例
            1. 在每行下面新增一個空行  
                sed 'G' a.sh   
            2. 將第一行換到最後一行   
                sed -r '1{h;d};$G' a.sh           
            3. 交換第一行和第二行內容      
               sed -r '1{h;d};2G' a.sh      
            4. 將第一行復制到每個偶數行下面   
                 sed '1h;2~2G;' a.sh
            5. 交換第一行和第三行的內容     
                sed '1{h;d};2{x;H;d};3G' a.sh   
            6. 用sed實現tac的效果
               sed '1!G;h;$!d' a.sh   
            7. 實現行列轉化
               sed -r 'H;${x;s/\n/ /g};$!d' a.sh
               sed -n 'H;${x;s/\n/ /g;p}' a.sh


            
    
sed  -n '1p' aa.txt 
sed  '' aa.txt 
sed  -n '$p' aa.txt 
sed  -n '1,4p' aa.txt 
sed  -n '1~2p' aa.txt 
sed  -n '2~2p' aa.txt 
sed  -n '2,+3p' aa.txt 
sed -n  '/Alice/p'  aa.txt 
sed -n  '/A/p'  aa.txt 
sed -n  '/A.*/p'  aa.txt 
sed -n  '/A$/p'  aa.txt 
sed -n  '/MA$/p'  aa.txt 
sed -n  '/MA$/,/VA$/p'  aa.txt 
sed -n  '/MA$/,/VA$/!p'  aa.txt 
sed 's/[a-z]/\u&/g' aa.txt  
sed -n '/Alice/n;p' aa.txt 

sed -n '1~2n;p' aa.txt 
sed -n '1~2p' aa.txt 

sed -n '6q;p' aa.txt 
sed -n 'p;6q' aa.txt

sed -n 'p;6r /etc/hosts' aa.txt


sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce 0

sed -in 'p;1r /etc/hosts' aa.txt

sed  '1d' aa.txt
sed  '1!d' aa.txt

sed -r 's/\<[0-9][0-9]\>/&.5/' aa.txt 



sed -nr '3{n;p}' aa.txt
sed -nr '3n;p' aa.txt




暫存和取用命令:h H g G
# sed -r '1h;$G' /etc/hosts
# sed -r '1{h;d};$G' /etc/hosts
# sed -r '1h; 2,$g' /etc/hosts
# sed -r '1h; 2,3H; $G' /etc/hosts

暫存空間和模式空間互換命令:x
# sed -r '4h; 5x; 6G' /etc/hosts



sed -r '1h;2,$g' aa.txt 
sed -r '1h;2,$G' aa.txt  
sed -r '1H;2,$g' aa.txt  
sed -r '1H;2,$G' aa.txt  


sed -r '1g;$x' aa.txt
sed -r '1h;$x' aa.txt

sed -r '4h; 5x; 6G'  aa.txt




1. 在每行下面新增一個空行  
    sed 'G' a.sh   
2. 將第一行換到最後一行   
    sed -r '1{h;d};$G' a.sh           
3. 交換第一行和第二行內容      
   sed -r '1{h;d};2G' a.sh      
4. 將第一行復制到每個偶數行下面   
     sed '1h;2~2G;' a.sh
5. 交換第一行和第三行的內容     
    sed '1{h;d};2{x;H;d};3G' a.sh   
6. 用sed實現tac的效果
   sed '1!G;h;$!d' a.sh   
7. 實現行列轉化
   sed -r 'H;${x;s/\n/ /g};$!d' a.sh
   sed -n 'H;${x;s/\n/ /g;p}' a.sh



shell程式設計之AWK

awk是行處理器: 相比較螢幕處理的優點,在處理龐大檔案時不會出現記憶體溢位或是處理緩慢的問題,通常用來格式化文字資訊
awk處理過程: 依次對每一行進行處理,然後輸出
預設分隔符是空格或者tab鍵

awk簡介
	awk 是一種程式語言,用於在linux/unix下對文字和資料進行處理。資料可以來自標準輸入、一個
或多個檔案,或其它命令的輸出。它支援使用者自定義函式和動態正規表示式等先進功能,是linux/unix
下的一個強大程式設計工具。它在命令列中使用,但更多是作為指令碼來使用。
	awk的處理文字和資料的方式是這樣的,它逐行掃描檔案,從第一行到最後一行,尋找匹配的特定
模式的行,並在這些行上進行你想要的操作。如果沒有指定處理動作,則把匹配的行顯示到標準輸出(
螢幕),如果沒有指定模式,則所有被操作所指定的行都被處理。awk分別代表其作者姓氏的第一個字
母。因為它的作者是三個人,分別是Alfred Aho、Brian Kernighan、Peter Weinberger。gawk是awk的
GNU版本,它提供了Bell實驗室和GNU的一些擴充套件。




awk的兩種形式語法格式
awk [options] 'commands' filenames
awk [options] -f awk-script-file filenames

==options:
-F   定義輸入欄位分隔符,預設的分隔符是空格或製表符(tab)
BEGIN{}            {}              END{}

行處理前       		行處理 	       行處理後
[root@master ~]# awk 'BEGIN{print 1/2} {print "ok"} END{print "----"}' /etc/hosts
0.5
ok
----

BEGIN{}     通常用於定義一些變數,例如BEGIN{FS=":";OFS="---"}

==awk命令格式:
awk 'pattern' filename					    示例:awk -F: '/root/' /etc/passwd		
awk '{action}' filename					    示例:awk -F: '{print $1}' /etc/passwd			
awk 'pattern {action}' filename		    示例:awk -F: '/root/{print $1,$3}' /etc/passwd		
													    示例:awk 'BEGIN{FS=":"} /root/{print $1,$3}' /etc/passwd
command |awk 'pattern {action}'      示例:df -P| grep  '/' |awk '$4 > 25000 {print $4}'


awk工作原理
awk -F":" '{print $1,$3}' /etc/passwd
(1)awk使用一行作為輸入,並將這一行賦給變數$0,每一行可稱作為一個記錄,以換行符結束
(2)然後,行被:分解成欄位,每個欄位儲存在已編號的變數中,從$1開始
(3)awk如何知道空格來分隔欄位的呢?因為有一個內部變數FS來確定欄位分隔符,初始時,FS賦為空格或者是tab
(4)awk列印欄位時,將以設定的方法,使用print函式列印,awk在列印的欄位間加上空格,因為$1,$3間有一個,逗號。逗號比較特殊,對映為另一個變數,成為輸出欄位分隔符OFS,OFS預設為空格
(5)awk列印欄位時,將從檔案中獲取另一行,並將其儲存在$0中,覆蓋原來的內容,然後將新的字串分隔成欄位並進行處理。該過程持續到處理檔案結束。

awk中的特殊變數:

- NR: 表示記錄編號, 當awk將行為記錄時, 該變數相當於當前行號
- NF: 表示欄位數量, 當awk將行為記錄時, 該變數相當於當前列號
- $0: 表示當前記錄的文字內容
- $1: 表示當前記錄的第一列文字內容
- $2: 表示當前記錄的第二列文字內容

    RS(輸入記錄分隔符)        (record sign) The input record separator, by default a newline.
    FS(輸入欄位分隔符)        (filed sign)
    NR(Number of record)行數
    FNR按不同的檔案飛開      
    ORS(輸出記錄分隔符)       (output frecord sign)The output record separator, by default a newline. 
    OFS(輸出欄位分隔符)       (output filed sign)
    NF 欄位個數              (Number of filed)



    FS(輸入欄位分隔符) (filed sign)
    FS(輸入欄位分隔符) (filed sign)

awk -F':' '{print $1}' pass
awk  'BEGIN{FS=":"} {print $1}' pass


OFS(輸出欄位分隔符)    (output filed sign)
awk  'BEGIN{FS=":";OFS="===="} {print $1,$2}' pass


NR   表示記錄編號, 當awk將行為記錄時, 該變數相當於當前行號
awk  '{print NR,$0}' pass
awk 'NR==3{print $0}' pass                   

FNR  表示記錄編號, 當awk將行為記錄時, 該變數相當於當前行號(不同檔案分開)
awk  '{print FNR,$0}' pass aa.txt


RS(輸入記錄分隔符)    
awk  'BEGIN{RS=":"} {print NR,$0}' pass
	

ORS(輸出記錄分隔符)
awk  'BEGIN{RS=":";ORS="~~"} {print NR,$0}' pass	

    [root@host ~]# awk 'BEGIN{FS=":"} {print $1}' /etc/passwd
    root
    bin
    daemon
    adm
    lp
    sync
    shutdown
    halt
    mail
    operator
    games
    
    OFS(輸出欄位分隔符)    (output filed sign)
    [root@host ~]# awk 'BEGIN{FS=":";OFS=".."} {print $1,$2}' /etc/passwd
    root..x
    bin..x
    daemon..x
    adm..x
    lp..x
    sync..x
    shutdown..x
    
    NR   表示記錄編號, 當awk將行為記錄時, 該變數相當於當前行號
    [root@host ~]# awk -F: '{print NR,$0}' a.txt file1.txt 
    1 love
    2 love.
    3 loove
    4 looooove
    5 
    6 isuo
    7 IPADDR=192.168.6.5
    8 hjahj123
    9 GATEWAY=192.168.1.1
    10 NETMASK=255.255.255.0
    11 DNS=114.114.114.114
    
    
    FNR  表示記錄編號, 當awk將行為記錄時, 該變數相當於當前行號(不同檔案分開)
    [root@host ~]# awk -F: '{print FNR,$0}' a.txt file1.txt 
    1 love
    2 love.
    3 loove
    4 looooove
    5 
    1 isuo
    2 IPADDR=192.168.6.5
    3 hjahj123
    4 GATEWAY=192.168.1.1
    5 NETMASK=255.255.255.0
    6 DNS=114.114.114.114
    
    

    RS(輸入記錄分隔符)       
    [root@master ~]# cat passwd 
    root:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologin
    [root@master ~]# awk -F: 'BEGIN{RS="bash"} {print $0}' passwd 
    root:x:0:0:root:/root:/bin/
    bin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologin
    
    
    ORS(輸出記錄分隔符)
    [root@master ~]# cat passwd 
    root:x:0:0:root:/root:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    
    [root@master ~]# awk -F: 'BEGIN{ORS=" "} {print $0}' passwd 
    root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin
    
    練習:將檔案合併為一行
    [root@master ~]# awk 'BEGIN{ORS="" } {print $0}' /etc/passwd
    練習:把一行內容分為多行
    [root@master ~]# cat d.txt 
    root:x:0:0:root:/root:/bin/bash
    [root@master ~]# awk 'BEGIN{RS=":"} {print $0}' d.txt 
    root
    x
    0
    0
    root
    /root
    /bin/bash
    



awk實用理解案例:

1. 列印一個檔案中的第2列和第3列
       $ awk '{ print $2, $3}' filename
2. 列印指定行指定列的某個字元
       $ awk -F: 'NR==3{ print $7 }' /etc/passwd
3. 統計一個檔案的行數
       $ awk '{ print NR}' filename
4. 在指令碼中, 傳遞變數到awk中
       $ var=1000
       $ echo | awk -v VARIABLE=$var '{ print VARIABLE }'
   
5. 指定欄位分隔符-F活在BEGIN{ FS=":" }
       $ awk -F: '{ print $2, $3 }' filename
       $ awk 'BEGIN{ FS=":" }{ print $2, $3 }' filename
6. 在awk中使用for迴圈
       $ grep '05/Sep/2017' cd.mobiletrain.org.log | awk '{ ips[$1]++ } END {for (i in ips){print i,ips[i]}}' | sort -k2 -rn | head -n10 
       182.140.217.111 138
       121.12.22.33 100
       10.19.3.2 90
       23.29.112.23 80
       121.31.30.189 45
       187.23.43.123 40
7. 在awk中使用if條件判斷

    $ awk -F: '{if($3==0) {print $1 " is administrator."}}' /etc/passwd
    統計系統使用者
    $ awk -F":" '{if($3>0 && $3<1000){i++}} END{print i}' /etc/passwd
    



相關文章