1. 程式人生 > >Shell基礎入門

Shell基礎入門

單詞 $$ 4.4 屏幕 $0 提示 示例 eof 多次

目錄

  • Shell基礎入門
    • 1、什麽是Shell?
    • 2、Shell腳本的結構
    • 3、Shell的變量
      • 3.1.自定義環境變量
      • 3.2.普通變量
      • 3.3.位置參數變量
      • 3.4、狀態變量
    • 4、條件測試和比較
      • 4.1、條件測試常用的語法形式:
      • 4.2、文件測試表達式的用法:
      • 4.3、字符串測試表達式
      • 4.4、整數二元比較操作符
      • 4.5、邏輯操作符
    • 5、If條件語句
    • 6、case語句
    • 7、for循環語句
    • 8、while循環語句
    • 9、函數
      • 9.1、shell函數的常見語法
      • 9.2、shell函數的執行
    • 10、數組

Shell基礎入門

1、什麽是Shell?

? Shell是一個命令解釋器,它的作用是解釋執行用戶輸入的命令及程序,用戶每輸入一條命令,Shell就解釋執行一條。Shell存在於操作系統的最外層,負責和用戶直接對話,把用戶的輸入解釋給操作系統,並處理各種各樣的操作系統輸出結果 ,然後輸出到屏幕返回給用戶。

? 而Shell腳本就是命令或程序語句不在命令行下執行,而是通過一個程序文件來執行。

2、Shell腳本的結構

? 在Linux系統中,Shell腳本通常是使用Vim編輯器進行編寫,其內容大多數是命令和程序結構控制語句以及註釋構成。其規範的格式如下:

  • Shell腳本的第一行為指定腳本解釋器,通常為:
#/bin/bash   或    #/bin/sh
  • Shell腳本添加版本和個人信息
# Date:  2019-03-08  15:30
# Author: KIM
# Description: nginx_log_cut
# Version: 1.1
  • Shell腳本中盡量不使用中文
  • Shell腳本的命名應該以.sh為擴展名
  • Shell腳本應該放在固定的路徑下,如/server/scripts
  • 使用合適的代碼縮進規範

3、Shell的變量

變量的類型:環境變量(全局變量)和普通變量(局部變量)

環境變量:也可稱為全局變量,可在在創建他們的shell及其派生出來的任意子進程shell中使用,環境變量又可氛圍自定義環境變量和bash的內置環境變量

普通變量:也可稱為局部變量,只能在創建他們的shell函數或者shell腳本中使用,普通變量一般由開發者在開發腳本程序時創建。

3.1.自定義環境變量

如果像要設置環境變量,就要在給變量賦值之後或在設置變量時用export命令。除了export命令,帶-x選項的declare內置命令也可以完成同樣的功能(註意:此處不要再變量名前加$)

exprot命令和declare命令的格式如下:

  • ①export 變量名=value
  • ②變量名=value; export 變量名
  • ③declare -x 變量名=value
[root@localhost ~]# export NAME=long
[root@localhost ~]# echo $NAME
long
[root@localhost ~]# declare -x NAME=li
[root@localhost ~]# echo $NAME
li
[root@localhost ~]# NAME=ing;export NAME
[root@localhost ~]# echo $NAME
ing

3.2.普通變量

定義方式有3種:

? 變量名=value #賦值時不加引號

? 變量名=‘value‘ #賦值時加單引號

? 變量名="value" #賦值時加雙引號

定義腳本退出狀態碼

? exit: 退出腳本

? exit #

如果腳本沒有明確定義退出狀態碼,那麽,最後執行的一條命令的退出碼即為腳本的退出狀態碼;

測試腳本是否有語法錯誤:

? bash -n 腳本

? bash -x 腳本:單步執行

3.3.位置參數變量

? 在Shell中存在一些特殊的位置參數變量,當我們在命令行、函數或腳本執行時傳遞參數時,此時就需要使用位置參數變量。如下表:

位置變量 作用
$0 獲取當前執行的Shell腳本文件名,如果執行腳本包含路徑,那麽就包含腳本路徑
$n 獲取當前執行Shell腳本的第N個參數,n=1..9,當n=0時,表示腳本文件名;n>9用大括號括起來{10},接的參數要 以空格隔開
$# 獲取當前執行的Shell腳本後面接的參數的總個數
$* 獲取當前Shell腳本所有傳參的參數,不加引號和$@相同;加了引號,如"$*"表示所有參數視為單個字符串,相當於"$1 $2 $3"
$@ 獲取當前Shell腳本所有傳參的參數,不加引號和$*相同

3.4、狀態變量

位置變量 作用
$? 獲取上一個執行指令的狀態返回值,0為成功,非0為失敗
$$ 獲取當前執行的Shell腳本的進程號
$! 獲取上一個在後臺運行的進程的進程號
$_ 獲取在之前執行命令或腳本的最後一個參數

4、條件測試和比較

4.1、條件測試常用的語法形式:

test <測試表達式>

[ 測試表達式 ]:[]兩端需要有空格

[[ 測試表達式 ]]:兩端需要有空格,與[]和test的區別在於,在[[]]中可以使用通配符等進行模式匹配;並且與&&、||、>、<等操作符可用於[[]]中,但不能用於[]中,在[]中一般使用 -a、-o、-gt(用於整數)、-lt。除了使用通配符的功能之外,建議放棄該用法。

((測試表達式)):一般用於if語句裏,兩端不需要空格

示例:

[root@localhost ~]# test -f file && echo ture || echo false

false

表示如果file文件存在,則輸出true,否則輸出false。

[root@localhost ~]# [ -f /etc/passwd ] && echo 0 || echo 1

0

表示如果/etc/passwd文件存在,則輸出0,否則輸出1

[root@localhost ~]# [[ -f /etc/passwd && -d /123/ ]] && echo 0 || echo 1

1

表示如果/etc/passwd文件存在,並且/123/目錄也存在,則輸出0,否則輸出1

4.2、文件測試表達式的用法:

常用的文件測試操作符:

-d:文件存在且為目錄則為真,即測試表達式成立

-f:文件存在且為普通文件則為真,即測試表達式成立

-e:文件存在則為真,即測試表達式成立

-r:文件存在且可讀則為真,即測試表達式成立

-w:文件存在且可寫則為真,即測試表達式成立

-x:文件存在且可執行為真,即測試表達式成立

-s:文件存在且文件大小不為0,即測試表達式成立

tips:測試文件的讀、寫、執行屬性,不光是看文件屬性rwx的表示進行判斷,還需要看當前執行測試的用戶是否真的可以按照對應的權限操作該文件。

特殊條件測試表達式案例:

以下寫法適用於所有的條件測試表達式,是工作中比較常用替代if語句的方法。判斷條件測試表達式的條件成立與否,還需要繼續執行多條命令語句的語法形式如下:

[ 條件1 ] && {

COMMAND 1

COMMAND 2

COMMAND 3

}

[[ 條件1 ]] && {

COMMAND 1

COMMAND 2

COMMAND 3

}

test 條件1 && {

COMMAND 1

COMMAND 2

COMMAND 3

}

相當於if語句:

if [ 條件1 ]

then

COMMAND 1

COMMAND 2

COMMAND 3

fi

4.3、字符串測試表達式

字符串測試操作符:

-n "字符串":若字符串的長度不為0,則為真,即測試表達式成立。

-z "字符串":若字符串的長度為0,則為真,即測試表達式成立。

"串1" = "串2":若串1等於串2,則為真,即測試表達式成立,可用==代替=

"串1" != "串2":若串1不等於串2,則為真,即測試表達式成立,可用!==代替!=

tips:字符串比較時等號兩邊需要空格,沒有空格會導致邏輯錯誤。

4.4、整數二元比較操作符

在[]和test中使用 在(())和[[]]中使用

-eq         == 或 =
-ne         !=
-gt         >
-ge         >=
-lt         <
-le         <=

4.5、邏輯操作符

在[]和test中使用 在(())和[[]]中使用

-a          &&
-o          ||
!           !

5、If條件語句

條件語句語法:

單分支if語句
if 判斷條件; then
  statement1
  statement2
  ...
fi

雙分支的if語句:
if 判斷條件; then
    statement1
    statement2
    ...
else
    statement3
    statement4
    ...
fi

多分支的if語句:
if 判斷條件1; then
  statement1
  ...
elif 判斷條件2; then
  statement2
  ...
elif 判斷條件3; then
  statement3
  ...
else
  statement4
  ...
fi

6、case語句

? case條件語句相當於多分支的if/elif/else條件語句,但是它比這些條件語句看起來更規範更公正,常被應用於事先系統服務啟動腳本等企業應用場景中。

? 在case語句中,程序會將case獲取的變量的值與表達式部分的值1、值2、值3等逐個進行比較,如果獲取的變量值和某個值(例如值1)相匹配,就會執行值後面對應的指令,直到執行到雙分號;;才會停止,然後再跳出語句主體,執行case語句後面的其他指令。

? 如果沒有找到匹配變量的任何值,則執行"*)"後面的指令(通常是給使用提示),直到遇到雙分號;;或esac結束,這部分相當於if多分支語句中最後的else語句部分。

case語句語法:選擇結構
SWITCH為變量的值,對變量的值進行引用,並進行判斷選擇

case SWITCH in 
value1)
  statement
  ...
  ;;
value2)
  statement
  ...
  ;;
*)
  statement
  ...
  ;;
esac

範例1:根據用戶的輸入判斷用戶輸入的是哪個數字,如果是1-9的任意數字,則輸出對應的數字;如果是其他數字及字符,則返回輸入不正確的提示並退出程序。
[root@localhost ]# vim num.sh 
#!/bin/bash
while :;do
read -p "please input your number:" A
case $A in
1)
    echo "You input the number is 1."
    ;;
[2-9]*)
    echo "You input then number is $A ."
    ;;
q|Q)
    echo "quiting ..."
    exit 0
    ;;
*)
    echo "Your input is error.please input your number again."
    ;;
esac
done

範例2:給出水果列表,並輸出客戶選擇的水果,且用不同的顏色顯示不同的水果。
#!/bin/bash
cat << EOF
1. apple
2. pear
3. banana
4.cheery
EOF
while :;do
read -p "Do you have some fruits." FRUITS
case $FRUITS in 
1)
    echo -e "\033[31mapple\033[0m"
    ;;
2)
    
    echo -e "\033[32mpear\033[0m"
    ;;
3)
    
    echo -e "\033[37mbanana\033[0m"
    ;;
4)
    
    echo -e "\033[36mcherry\033[0m"
    ;;
*)
    echo "Your choice is error."
    exit 1
    ;;
esac
done

範例3:給輸出的字符加顏色基礎知識
在linux腳本中,可以通過echo的-e參數,結合特殊的數字給不同的字符加上顏色並顯示。
內容的顏色可以用數字表示,範圍為30-37,每個數字代表一種顏色,代碼如下:
echo -e "\033[30m 黑色字體 long \033[0m"
echo -e "\033[31m 紅色字體 long \033[0m"
echo -e "\033[32m 綠色字體 long \033[0m"
echo -e "\033[33m 棕色字體 long \033[0m"
echo -e "\033[34m 藍色字體 long \033[0m"
echo -e "\033[35m 洋紅色字體 long \033[0m"
echo -e "\033[36m 藍綠色字體 long \033[0m"
echo -e "\033[37m 白色字體 long \033[0m"

7、for循環語句

for循環語句和while循環語句類似,但for循環主要用於執行次數有限的循環,而不是用於守護進程及無限循環。


1.for循環語法結構:
for 變量名 in 變量取值列表
do
指令...
done

tips:在此結構中,“in 變量取值列表”可以省略,省略時相當於in "$@",也就是使用for i就相當於使用for i in "$@"

在這種for循環語句結構語法中,for關鍵字後面有一個變量名,變量名依次獲取in關鍵字後面的變量取值列表內容(以空格隔開),每次取一個,然後進入循環執行循環內的指令,當執行到done時結束本次循環。之後,“變量名”再繼續獲取列表裏的下一個變量值,繼續執行循環體內的指令,當執行到done時結束返回,以此類推,直到取完列表裏的最後一個值並進入循環執行到done結束為止。

第二種語法結構:
for((expr1;expr2;expr3))
do
指令...
done

範例1:計算1-100整數和

#!/bin/bash
declare -i SUM=0

for I in {1..100};do
    let SUM+=$I
done
echo $SUM

declare -i SUM2=0
for ((I=1;I<=100;I++));do
    let SUM2+=$I
done
echo $SUM2
範例2:豎向打印5、4、3、2、1這5個數字
#!/bin/bash
for I in `seq 5 -1 1`
do 
echo $I
done

8、while循環語句

循環語句命令常用於重復執行一條指令或一組指令,知道條件不滿足為止,shell腳本語言的循環語句常見的有while、until、for、select循環語句。
while循環語句主要用來重復執行一組命令或語句,在企業實際應用中,常用於守護進程或持續運行的程序,除此以外,大多數循環都會用到for循環語句。

1.while循環語句語法:

while <條件表達式>
do
指令...
done
while循環語句會對緊跟while命令後的條件表達式進行判斷,如果該條件表達式成立,則執行while循環體中的命令或語句,每一次執行到done時,就會重新判斷while條件表達式是否成立,直到條件表達式不成立時才會跳出while循環體。如果一開始條件表達式就不成立,那麽程序就不會進入循環體。

while的特殊用法一,死循環:
while :;do
  
done

舉例輸入文件路徑,進行判斷:
#!/bin/bash
while :;do
    read -p "File path:" FILEPATH
    [ $FILEPATH == 'quit'] && break
    if [ -e $FILEPATH ];then
        echo "$FILEPATH exists"
    else
        echo "No $FILEPATH."
    fi
done


while的特殊用法二:
while read LINE; do

done < /PATH/TO/SOMEFILE

舉例:判斷/etc/passwd中哪個用戶的shell為bash shell,如果是bash shell就顯示該用戶的用戶名

#!/bin/bash
#
FILE=/etc/passwd

while read LINE;do
    [ `echo $LINE |awk -F: '{print $7}'` == '/bin/bash' ] && echo $LINE |awk -F: '{print $1}'
done < $FILE


2.until循環語句語法:
until <條件表達式>
do
指令...
done
until的用戶和while類似,區別是until會在條件表達式不成立時,進入循環執行指令;條件表達式成立時,終止循環。

範例1:
1、判斷一個指定的bash腳本是否有語法錯誤;如果有錯誤,則提醒用戶鍵入Q或者q無視錯誤並退出,其它任何鍵可以通過vim打開這個指定的腳本;
2、如果用戶通過vim打開編輯後保存退出時仍然有錯誤,則重復第1步中的內容;否則,就正常關閉退出。

./syntax.sh a.sh

until bash -n $1 &> /dev/null; do
    read -p "Syntax error, [Qq] to quit, others for editing: "  CHOICE
    case $CHOICE in
    q|Q)
        echo "Something wrong, quiting."
        exit 5
        ;;
    *)
        vim + $1
        ;;
    esac
done

echo "0K"

break: 提前退出循環
continue:提前結束本輪循環,而進入下一輪循環;

計算1-100的奇數和
#!/bin/bash

let SUM=0
let I=1
while [ $I -le 100 ];do
    if [ $[$I%2] -eq 1 ];then
        let SUM+=$I
    fi
        let I++
done
echo $SUM

continue舉例:
#!/bin/bash
let SUM=0
let I=0
while [ $I -lt 100 ];do
    let I++
    if [ $[$I%2] -eq 0 ];then
        continue---當判斷取余為0時,直接提前結束本輪循環體
    fi
    let SUM+=$I
done
echo $SUM

break舉例:
#!/bin/bash
#
let SUM=0
for I in {1..1000};do
    let SUM+=$I
    if [ $SUM -gt 5000 ];then
        break---直接退出循環
    fi
done
echo $I
echo $SUM

企業生產範例:
寫一個shell腳本解決類DDOS攻擊的生產案例。請根據web日誌或系統網絡連接數,監控某個ip的並發連接數,若短時間內PV達到100,即調用防火墻命令封掉對應的ip,防火墻命令為:iptables -I INPUT -s IP地址 -j DROP

#!/bin/bash
file=$1
while true;do
awk '{print $1}' $1 |grep -v "^$"|sort |uniq -c > /tmp/tmp.log
exec </tmp/tmp.log
while read line
do
ip=`echo $line |awk '{print $2}'`
count=`echo $line |awk 'print $1'`
if [ $count -gt 500 ] && [ `iptables -L -n|grep "$ip"|wc -l` -lt 1 ]
then
iptables -I INPUT -s $ip -j DROP
echo "$line is droped" >> /tmp/droplist_$(date +%F).log
fi
done
sleep 3600
done

9、函數

函數的功能:實現代碼的重用

9.1、shell函數的常見語法

標準寫法:
function 函數名 () {
指令...
return n
}
簡化寫法1:
fucntion 函數名 {
指令...
return n
}
簡化寫法2:
函數名 () {
指令...
return n
}
tips:在shell函數語法中,function表示聲明一個函數,這部分可以省略不寫。

9.2、shell函數的執行

shell的函數分為最基本的函數和可以傳參的函數兩種,其執行的方式分別如下:

  • ①執行不帶參數的函數時,直接輸入函數名即可(註意不帶小括號)。格式如下:

函數名

說明:執行函數時,函數名前的function和函數後的小括號都不用帶;
函數的定義必須要在執行的程序前面定義或加載;
return命令和exit命令類似,return的作用是退出函數,exit是退出腳本文件;
如果將函數放在獨立的文件中,被腳本加載時,需要使用source或"."來加載;
函數內,一般使用local定義的局部變量,這些變量離開函數後就會消失;

  • ②帶參數的函數執行方法,格式如下:

函數名 參數1 參數2

說明:shell的位置參數($1、$2...$#、$*、$@、$?)都可以作為函數的參數來使用;
此時父腳本的參數臨時地被函數參數所掩蓋或隱藏;
$0比較特殊,它仍是父腳本的名稱;
當函數執行完成時,原來的命令行腳本的參數即可恢復;
函數的參數變量是在函數體裏面定義的;

舉例:使用函數多次顯示一個菜單

#!/bin/bash
#
function SHOWMENU {
cat << EOF
d|D) show disk usages
m|M) show memory usages
s|S) show swap usages
q|Q) quit
EOF
}
SHOWMENU
SHOWMENU
SHOWMENU

範例1:將函數的傳參轉換成腳本文件命令行傳參,判斷任意指定的URL是否存在異常.
1)實現腳本傳參,檢查web URL是否正常。
#!/bin/bash
#判斷傳參數量是否為1
if [ $# -ne 1 ];then
echo "Usage:`basename $0` url."
exit 1
fi
#利用wget進行訪問
wget --spider -q -o /dev/null --tries=1 -T 5 $1 #T為超時時間,$1為腳本參數
if [ $? -eq 0 ];then
echo "$1 is yes."
else
echo "$1 is no."
fi
2)將上述檢測的功能寫成函數,並將函數傳參轉換成腳本命令行傳參,判斷任意指定的URL是否存在異常。
#!/bin/bash
function usage() {
echo "Usage:`basename $0` url."
exit 1
}
function check_url() {
wget --spider -q -o /dev/null --tries=1 -T 5 $1
if [ $? -eq 0 ];then
echo "$1 is yes."
else
echo "$1 is no."
fi
}
function main() {
if [ $# -ne 1 ];then
useage
fi
check_url $1
}
main $* --這裏的$*就是把命令行接收的所有參數作為函數參數穿給函數內部,是一種常用手法

10、數組

1.shell數組的定義:
方法1:用小括號將變量值括起來賦值給數組變量,每個變量之間要用空格進行分隔。
語法如下:
array=(value1 value2 value3...)

[root@localhost ~]# array=(1 2 3)
[root@localhost ~]# echo ${array[*]}
1 2 3

方法2:動態定義數組變量,使用命令的輸出結果作為數組的內容。
語法為:
array=($(命令))   或  array=(`命令`)
示例如下:
[root@localhost ~]# mkdir /array/ -p
[root@localhost ~]# touch /array/{1..3}.txt
[root@localhost ~]# ll /array/
total 0
-rw-r--r-- 1 root root 0 Aug 23 21:45 1.txt
-rw-r--r-- 1 root root 0 Aug 23 21:45 2.txt
-rw-r--r-- 1 root root 0 Aug 23 21:45 3.txt
[root@localhost ~]# array=($(ls /array/))
[root@localhost ~]# echo ${array[*]}
1.txt 2.txt 3.txt

2.shell數組的打印及輸出
①打印數組元素
[root@localhost ~]# array=(1 2 3)
[root@localhost ~]# echo ${array[0]}
1
[root@localhost ~]# echo ${array[1]}
2
[root@localhost ~]# echo ${array[2]}
3
②打印數組元素個數
[root@localhost ~]# echo ${array[*]}    --使用*或@可以得到整個數組的內容
1 2 3
[root@localhost ~]# echo ${#array[*]}   -使用${#數組名[*]}可以得到數組的長度
3
[root@localhost ~]# echo ${array[@]}
1 2 3
[root@localhost ~]# echo ${#array[@]}
3
③數組的刪除
因為數組的本質還是變量,因此可以通過unset 數組[下標] 清除相應的數組元素,如果不帶下標,則表示清除整個數組的所有數據。

④數組內容的截取與替換
[root@localhost ~]# array=(1 2 3 4 5)
[root@localhost ~]# echo ${array[@]:1:3}    -截取1號到3號數組元素
2 3 4
[root@localhost ~]# echo ${array[@]/1/a}    -把數組中的1替換成a
a 2 3 4 5
[root@localhost ~]# echo ${array[@]}
1 2 3 4 5

tips:調用方法為${數組名[@或*]/查找字符/替換字符},該操作不會改變原數組的內容。

範例1:使用循環批量輸出數組元素
#!/bin/bash
array=(1 2 3 4 5)
for((i=0;i<${#array[@]};i++))
do
echo ${array[i]}
done

範例2:利用bash for循環打印下面這句話中不大於6的單詞
I am oldboy teacher welcome to oldboy training class
#!/bin/bash
array=(I am oldboy teacher welcom to oldboy training class)
for((i<0;i<${#array[@]};i++))
do
length=`echo ${array[i]} |wc -l`
if [ $length -lt 6 ];then
echo ${array[i]}
fi
done

Shell基礎入門