Shell基礎入門

Shell基礎入門

一、什麼是Shell?

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

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

二、Shell腳本的結構

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

  • 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
  • 使用合適的代碼縮進規範

三、Shell的變量

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

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

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

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種:app

​ 變量名=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.四、狀態變量

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

四、條件測試和比較

4.一、條件測試經常使用的語法形式:

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.二、文件測試表達式的用法:

經常使用的文件測試操做符:

-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.三、字符串測試表達式

字符串測試操做符:

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

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

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

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

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

4.四、整數二元比較操做符

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

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

4.五、邏輯操做符

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

-a          &&
-o          ||
!           !

五、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

六、case語句

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

​ 在case語句中,程序會將case獲取的變量的值與表達式部分的值一、值二、值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"

七、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:豎向打印五、四、三、二、1這5個數字
#!/bin/bash
for I in `seq 5 -1 1`
do 
echo $I
done

八、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:
一、判斷一個指定的bash腳本是否有語法錯誤;若是有錯誤,則提醒用戶鍵入Q或者q無視錯誤並退出,其它任何鍵能夠經過vim打開這個指定的腳本;
二、若是用戶經過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.一、shell函數的常見語法

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

9.二、shell函數的執行

shell的函數分爲最基本的函數和能夠傳參的函數兩種,其執行的方式分別以下:

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

函數名

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

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

函數名 參數1 參數2

說明:shell的位置參數($一、$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 $* --這裏的$*就是把命令行接收的全部參數做爲函數參數穿給函數內部,是一種經常使用手法

十、數組

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
相關文章
相關標籤/搜索