系統學習------shell腳本編程

BASH基礎

Shell爲Linux提供了編程環境
程序 = 指令 + 數據

編程風格:

過程式:以指令爲中心,數據服務於命令
對象式:以數據爲中心,命令服務於數據
shell是一種過程式編程

過程式編程:
順序執行
循環執行
選擇執行


如何判斷shell腳本: 文件中頭行: #!/bin/bash 標識

運行腳本方法:
給文件給予可執行權限,通過具體的文件路徑執行文件執行

chmod +x xxx.sh 
./path/to/xxx.sh

直接運行解釋器,將腳本作爲解釋器程序的參數運行

bash xx.sh 
[root@node1 ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/

系統運行命令尋找的路徑,如果你將腳本文件放入到以上路徑下,可以將腳本直接作爲命令去執行。

變量

變量命名:

	命名只能使用英文字母,數字,下劃線;首字母不能以數字開頭
	中間不能夠有空格,可以使用_下換線
	不能夠使用標點符號(特殊字符)
	不能夠使用bash中的關鍵字

有效的命名:
RUNshell
L_inux
var1

無效的命名:
?var1
user*name


賦值

將語句給變量賦值
for file in “ls /etc/”
for file in $(ls /etc/)

[root@node1 ~]# for file in "ls /tmp/";do echo $file;done

將文件路徑/tmp/下的所有文件賦給變量file,do…;done之間是循環體。
該條命令是循環顯示/tmp/下的文件名。

使用變量:
定義變量: varname=“value”
使用變量: echo $varname | echo ${varname}
建議:使用{}作爲邊界

只讀變量:
readonly關鍵字可以將變量作爲只讀變量,變量無法被修改

[root@node1 ~]# vim 1.sh

在裏面寫入

#!/bin/bash 
url="www.baidu.com"
echo ${url}
url="www.google.com"
echo ${url}

把"www.baidu.com"賦值給變量url,使用變量
再把"www.google.com"賦值給變量url,使用變量
然後給1.sh執行權限

[root@node1 ~]# chmod +x 1.sh

接下來直接執行" ./1.sh ",會輸出

"www.baidu.com"
"www.google.com"

如果將1.sh修改爲

#!/bin/bash 
url="www.baidu.com"
echo ${url}
readonly url
url="www.google.com"
echo ${url}

就會輸出兩次"www.baidu.com"並報錯,因爲url是隻讀變量了所以不能被修改。

刪除變量:unset關鍵字

#!/bin/bash 
url="www.baidu.com"
echo ${url}
unset url
echo ${url}

此時執行" ./1.sh “,第一次輸出"www.baidu.com”,第二次爲空。

變量的種類:

本地變量:生效範圍僅作爲當前shell進程(其他shell或者子shell無法使用)
例如將a=1,echo$a就是1;然後bash進入子shell,再echo $a就是空值。

[root@node1 ~]# a=1
[root@node1 ~]# echo ${a}
1
[root@node1 ~]# bash	# 進入到子shell中
[root@node1 ~]# echo ${a}

環境變量:生效範圍爲當前shell進程及子進程
變量聲明方式1: export varname=“value”
變量聲明方式2: declare -x varname=“value”
bash當中有很多內建的環境變量: PATH SHELL

局部變量:生效範圍爲當前shell進程中某代碼片段(通常函數)

位置變量:1,2,3…來表示,讓腳本在腳本片段中調用通過命令行傳遞給他的參數。
用法: ./xx.sh 參數1 參數2 參數3 …

	調用:
		$1,$2,$3: 對應的是參數1,參數2,參數3....
		$0: 對應是命令本身
		$*: 傳遞給腳本的所有參數(把所有參數當成一個整體)
		$@: 傳遞給腳本的所有參數 
		$#: 傳遞給腳本的參數個數

案例1:

[root@node1 ~]# cat 1.sh 
#!/bin/bash 
echo ${1}
echo ${2}
echo $0
echo $*
echo $@
echo $# ·
[root@node1 ~]# ./1.sh Linux Python
Linux
Python
./1.sh
Linux Python
Linux Python
2

案例2:判斷所給文件的行數
判斷anaconda-ks.cfg文件的行數

[root@node1 ~]# wc -l anaconda-ks.cfg | awk '{print $1}'
48
[root@node1 ~]# cat linecount.sh 
#!/bin/bash
lines=`wc -l $1 | awk '{print $1}'`
echo "This file have ${lines} line"

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

shell數組

語法格式:

定義:array_name=(value1 value2 …)
使用:array_name[0]

示例1:
array_1=(A B C)
array_1[0]=A
array_1[1]=B
array_1[2]=C

讀取數組: ${array_name[index]}
修改數組中的元素:
array_1[index]=N_value

讀取數組中的所有元素:
${array_name[*]}
${array_name[@]}

讀取數組中的所有元素個數:
${#array_name[*]}
${#array_name[@]}

實例:

#!/bin/bash
array_1=(A B C D)
echo ${array_1[0]}
echo ${array_1[3]}
echo ${array_1[*]}
echo ${array_1[@]}
echo ${#array_1[*]}
echo ${#array_1[@]}

shell的環境配置

bash配置文件

  • 生效範圍分類:
    全局配置:/etc/profile /etc/profile.d/* /etc/bashrc
    個人配置: ~/.bashrc ~/.bash_profile

功能:

  1. 用於定義環境變量
  2. 運行命令或腳本
  • 功能分類:
  1. profile類:爲交互式的shell提供配置
    全局配置:/etc/profile /etc/profile.d/*
    個人配置:~/.bash_profile
  2. bashrc類:爲非交互式的shell提供配置
    全局配置:/etc/bashrc
    個人配置:~/.bashrc
    功能: 定義本地變量
  • shell登錄:
    交互式: su - Username
    /etc/profile -> /etc/profile.d/* -> ~/.bash_profile
    非交互式: su Username
    ~/.bashrc -> /etc/bashrc

      編輯環境配置文件:
      	定義的新設置立即生效: 
      		1. 重新啓動shell
      		2. source命令  -> source /etc/profile 
    

bash算數運算符

算數運算符: + - * / …
增強型: += -= *= /= %=

使用算數符:
(1)let varname=算數表達式
(2)varname=$[算數表達式]
(3)varname=$((算數表達式))
(4)varname=$( expr arg1 arg2 arg3 …)

Note:
乘法符號* 有些情況下需要轉義 *
bash中有個內建隨機生成器: $RANDOM

練習題

  1. 計算/etc/passwd文件中第10行的用戶ID和第20行的用戶ID和
#!/bin/bash 
id1=`cat /etc/passwd | head -n 10 | tail -1 | awk -F: '{print $3}'`
id2=`sed -n '20p' /etc/passwd | cut -d: -f3`
let sum_id="id1+id2"
echo $sum_id

在這裏插入圖片描述
head -n 10是前十行,再加上tail -1纔是第十行。然後用awk命令過濾,awk -F指定‘:’作爲分隔符打印第三個字段,即11。

sed -n '20p’打印文件的第二十行,cut -d指定以‘:’爲分隔符,-f3爲提取第個三域。


  1. 傳遞兩個文件文件參數給腳本,計算這兩個文件之中所有空白之和
#!/bin/bash 
blank1=`cat $1 | grep "^$" | wc -l`
blank2=`cat $2 | grep "^$" | wc -l`
let sum_blank=blank1+blank2
echo $sum_blank

其中“$1”“$2”是傳入的文件名參數,執行腳本文件是就可以寫成:
./sum_blank.sh 文件名1 文件名2

grep引號中的第一個字符^和最後一個$:
^: 表示字符串開始。
$: 表示字符串結束。
grep "^$"表示精確匹配文件中的空格

wc指令我們可以計算文件的Byte數、字數、或是列數,wc -l只顯示行數。

  1. 統計/etc/,/var/,/usr/目錄下所有一級目錄和文件之和
#!/bin/bash 
sum_etc=`ls /etc/ | wc -l`
sum_var=`ls /var/ | wc -l`
sum_usr=`ls /usr/ | wc -l`
let sum_all="sum_etc+sum_usr+sum_usr"
echo $sum_all

條件測試

bash條件測試:
判斷某些需求是否滿足,需要由測試機制來實現
專用的測試表達式需要由命令輔助完成測試過程

測試命令: test Experssion

  • 測試類型:
    數值測試
    字符串測試
    文件測試

數值測試:
-gt 是否大於
-ge 是否大於等於
-eq 是否等於
-ne 是否不等
-lt 是否小於
-le 是否小於等於

字符串測試:
== : 是否等於
> : 是否大於
< : 是否小於
!=: 是否不等於
=~ : 左側字符串是否能被右側的Pattern匹配
Note: 此表達式一般用於 [[]]中
-z “String” : 測試字符串是否爲空,空則爲真;非空則爲假
-n “String” : 測試字符串是否不空,不空爲真;空則爲假

文件測試:
簡單存在性測試:
-a file: 文件存在則爲正,不存在則爲假
存在及類型測試:
-b file: 是否存在且爲塊設備文件
-c file: 是否存在且爲字符設備文件
-d file: 是否存在且爲目錄文件
-f file: 是否存在且爲普通文件
-h file: 是否存在且爲符號鏈接文件(-l 可以)
-p file: 是否存在且爲管道文件
-s file: 是否存在且爲socket文件

文件權限測試:
-r file: 是否存在可讀權限
-w file:是否存在可寫權限
-x file:是否存在可執行權限

文件特殊權限測試:
-g file: 是否存在且擁有sgid權限
-u file: 是否存在且擁有suid權限
-k file: 是否存在且擁有sticky權限

文件大小測試:
-s file: 是否存在且非非空
文件是否打開測試:
- fd : fd表示文件愛你描述是否已經打開且與終端相關
-N file: 文件自動上一次讀取之後是否被修改過
-O file:當前用戶是否爲文件屬主
-G file:當前用戶是否爲文件數組

雙目測試:
file1 -ef file2: file1與file2是否指向同一個設上的相關inode
file1 -nt file2: file1是否新於file2
file1 -ot file2: file1是否舊於file2

組合測試條件:

  • 第一種方式: && ||
    && 全真則爲真
    || 有真則爲真
  • 第二種方式:
    -a -> && -> experssion1 && experssion2
    -o -> || -> experssion1 || experssion2
    ! Experssion

bash退出碼
腳本中一旦遇到exit命令,腳本會立即終止,終止退出碼取決於exit命令後面的數字
如果腳本中未給出退出碼,整個腳本的退出碼由最後腳本中最後一行命令的執行結果決定

exit N

練習1: 接收一個文本路徑作爲參數,如果參數個數小於1,則提示用戶“至少給1個參數”,並立即退出

[ $# -lt 1 ] && echo "at least one args ...." && exit 1 

$#判斷傳給腳本的參數個數,小於1就打印“at least one args”並退出,退出碼爲1。

 [ $# -gt 1 ] && echo "test ok  ...." && exit 0

於是我們可以根據退出碼知道執行情況。

Note: 使用條件測試的時候;[ experssion ]; 條件測試表達式experssion與中括號兩邊是有空格的

選擇執行:

語法:

  • 第一種:
    if 判斷條件;then
    條件爲真的執行代碼塊
    fi

  • 第二種:
    if 判斷條件;then
    條件爲真的執行代碼塊
    else
    條件爲假的執行代碼塊
    fi

  • 第三種:
    if 判斷條件1;then
    條件爲真的代碼塊
    elif 判斷條件2;then
    條件1爲假;條件2爲真的執行代碼塊
    else
    條件12都爲假的執行代碼塊
    fi

練習1: 判斷兩個數是否相等

#!/bin/sh

set num1 = $1
set num2 = $2

#這個判斷如何寫?
if test $num1 -eq $num2
then
echo "num1 is equal to num2"
else
echo "num1 is not equal to num2"
fi;

在這裏插入圖片描述
練習2: 判斷用戶是否存在,如果不存在添加用戶並設置密碼和用戶名相同;如果存在立即退出;退出狀態碼爲0

#!/bin/bash  

#定義函數  
Find_u(){  
#判斷輸入值是否爲空,如果爲空,則函數結束,返回值1  
[ -z $1 ] && return 1  
#判斷用戶是否存在,存在則顯示要求,不存在,函數結束,返回值1  
if id $1 &> /dev/null ;then 
    echo "$1 UID is `id -u $1`"
    echo "$1 Shell is `grep "^$1:" /etc/passwd \
    |cut -d':' -f7 `"  
else
    return 1  
fi  
}  
#循環執行,以符合題目要求  
while :;do
read -p "Please input A username[quit to exit]: " User 
    if [ $User = quit ];then 
        exit 0  
    else
        Find_u $User 
        Res=$?  
        [ $Res -eq 1 ] && echo "No such $User."
    fi  
done

在這裏插入圖片描述
知識點: 命令的使用 -> 條件測試 + 選擇執行結構 + 狀態碼

用戶交互

read命令

-a : 將內容讀取寫入到數組中

[root@localhost ~]# read -a array_test1
hello linux test
[root@localhost ~]# echo "get ${#array_test1[*]} values in array"
get 3 values in array

-d : 表示定界符;
-e : 只用於互相交互的腳本
-n : 用於限定最多可以有多少個字符有效讀入


-p : 用於給出提示符
echo -n "please input value into array_test "
read -a array_test
或直接
read -p “please input value into array_test…” array_test

示例

[root@node1 ~]# cat read.sh 
#!/bin/bash 
read -p "please input one number ......" number1		
echo $number1

其中number1是用於接收用戶輸入的。


-r : 特殊字符生效
-s :對於一些特殊字符不打印的情況
-t :表示等待輸入的時間時長

練習1: 輸入姓名之後,進行輸出

#!/bin/bash 
read -p "Input your name" name 
echo ${name}

練習2:輸入一個文件判斷文件類型,判斷輸入文件是否爲目錄文件;是則輸出yes;否則直接退出

#!/bin/bash 
read -p "please input your test file ...." file 
	if [ -d file ];then 
		echo "this is a directory file ...."
		exit 0 
	else
		echo "this is not a directory file ..."
		exit 1
	fi 

運用條件測試中的具體命令可解決類似題目,可到上文條件測試去看具體命令。

循環結構

循環體: 需要執行的語句,可能執行n遍

for循環

語法:

	for 變量名 in 列表;do 
		循環體
	done 

執行機制:依次將列表中的元素賦值給變量名然後去執行一遍循環體;當列表中的元素耗盡時,退出

示例:

[root@node1 ~]# for i in `ls /tmp/`;do echo $i;done

循環輸出/tmp/下的文件名

[root@node1 ~]# for i in {20,30};do ping -c4 192.168.10.$i;done 

分別循環ping 192.168.10.20 和 192.168.10.30 ,輸出4行

[root@node1 ~]# for i in `seq 1 10`;do echo $i;done

seq 數字生成器:m到n數字

練習題1: 創建用戶user1-user10家目錄,並且在user1-user10家目錄下創建1.txt - 10.txt文件內容,輸出格式爲
練習題2: 列出/var/目錄下各個子目錄所佔磁盤大小

while循環

語法:

while 條件測試;do
	循環體
done 

執行機制: 當條件測試爲真是就執行一遍循環體;爲假時退出循環

經典使用:讀取文件中的內容
寫法1:

#!/bin/bash 
while read linecontext;do
	echo "+++$linecontext"
done < /root/test.txt

寫法2:

cat $1 | while read linecontext
do
	echo $linecount 
done 

until循環

執行機制:條件爲假時執行循環體;條件爲真時退出
語法:

until 條件測試;do
	循環體
	
done 

函數

函數定義:

function Fun_name(){
	函數體
	返回值
}

調用函數:Fun_name

示例1

#!/bin/bash 
function sum_b(){
  blank1=`cat $1 | grep "^$" | wc -l`
  blank2=`cat $2 | grep "^$" | wc -l`
  let sum_blank=blank1+blank2
  echo $sum_blank
  return 0
}
#調用函數 傳入參數
sum_b $1 $2

返回值的獲取: 可以通過 echo $?來獲取

總結

shell腳本編程要對命令熟悉,通過對命令的組合進行shell編程。
shell腳本應用:通過shell成當前資源(cpu disk vm …)的使用情況 每天都需要統計。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章