複習shell腳本

什麼是shell腳本。首先它是一個腳本,並不能作爲正式的編程語言。因爲是跑在linux的shell中,所以叫shell腳本。說白了,shell腳本就是一些命令的集合。舉個例子,我想實現這樣的操作:

1)進入到/tmp/目錄;

2)列出當前目錄中所有的文件名;

3)把所有當前的文件拷貝到/root/目錄下;

4)刪除當前目錄下所有的文件。

簡單的4步在shell窗口中需要你敲4次命令,按4次回車。這樣是不是很麻煩?當然這4步操作非常簡單,如果是更加複雜的命令設置需要幾十次操作呢?那樣的話一次一次敲鍵盤會很麻煩。所以不妨把所有的操作都記錄到一個文檔中,然後去調用文檔中的命令,這樣一步操作就可以完成。其實這個文檔呢就是shell腳本了,只是這個shell腳本有它特殊的格式。

Shell腳本能幫助我們很方便的去管理服務器,因爲我們可以指定一個任務計劃定時去執行某一個shell腳本實現我們想要需求。這對於linux系統管理員來說是一件非常值得自豪的事情。現在的139郵箱很好用,發郵件的同時還可以發一條郵件通知的短信給用戶,利用這點,我們就可以在我們的linux服務器上部署監控的shell腳本,比如網卡流量有異常了或者服務器web服務器停止了就可以發一封郵件給管理員,同時發送給管理員一個報警短信這樣可以讓我們及時的知道服務器出問題了。

在正式寫shell腳本之前先給你一條建議:凡是自定義的腳本建議放到/usr/local/sbin/目錄下,這樣做的目的是,一來可以更好的管理文檔;二來以後接管你的管理員都知道自定義腳本放在哪裏,方便維護。

shell腳本的基本結構以及如何執行

下面寫第一個你的shell腳本吧:

[root@aiker ~]# cd /usr/local/sbin/
[root@aiker sbin]# vim first.sh
#! /bin/bash

## This is my first shell script.
## Writen by Aiker 2018-07-22.

date
echo "Hello world!"

Shell腳本通常都是以.sh 爲後綴名的,這個並不是說不帶.sh這個腳本就不能執行,只是大家的一個習慣而已。所以,以後你發現了.sh爲後綴的文件那麼它可能是一個shell腳本了。test.sh中第一行要以 “#! /bin/bash” 開頭,它代表的意思是,該文件使用的是bash語法。如果不設置該行,雖然你的shell腳本也可以執行,但是這不符合規範。 # 表示註釋,在前面講過的。後面跟一些該腳本的相關注釋內容以及作者和創建日期或者版本等等。當然這些註釋並非必須的,如果你懶的很,可以省略掉,但是不建議省略。因爲隨着工作時間的逐漸過渡,你寫的shell腳本也會越來越多,如果有一天你回頭查看自己寫過的某個腳本時,很有可能忘記該腳本是用來幹什麼的以及什麼時候寫的。所以寫上註釋是有必要的。另外系統管理員並非只有你一個,如果是其他管理員查看你的腳本,他看不懂豈不是很鬱悶。下面該運行一下這個腳本了:

[root@aiker sbin]# sh first.sh
2018年 07月 22日 星期五 18:58:02 CST
Hello world!

其實shell腳本還有一種執行方法就是:

[root@aiker sbin]# ./first.sh
-bash: ./first.sh: 權限不夠
[root@aiker sbin]# chmod +x first.sh
[root@aiker sbin]# ./first.sh
2018年 07月 22日 星期五 18:58:51 CST
Hello world!

要想使用該種方法運行shell腳本,前提是腳本本身有執行權限,所以需要給腳本加一個 ‘x’ 權限。另外使用sh命令去執行一個shell腳本的時候是可以加-x選項來查看這個腳本執行過程的,這樣有利於我們調試這個腳本哪裏出了問題:

[root@aiker sbin]# sh -x first.sh
+ date
2018年 07月 22日 星期五 20:00:11 CST
+ echo 'Hello world!'
Hello world!

本例中有一個命令 date 之前從未介紹過,這個命令在shell腳本中使用非常頻繁,有必要向你介紹一下它的用法。

命令 : date

舉幾個比較實用的例子來帶你掌握它的用法:

[root@aiker sbin]# date +"%Y-%m-%d %H:%M:%S"
2018-07-22 19:41:01

date在腳本中最常用的幾個用法:

data +%Y 以四位數字格式打印年份

date +%y 以兩位數字格式打印年份

date +%m 月份

date +%d 日期

date +%H 小時

date +%M 分鐘

date +%S

date +%w 星期,如果結果顯示0 則表示週日

有時在腳本中會用到一天前的日期:

[root@aiker sbin]# date -d "-1 day" +%d
23

或者一小時前:

[root@aiker sbin]# date -d "-1 hour" +%H
18

甚至1分鐘前:

[root@aiker sbin]# date -d "-1 min" +%M
50

shell腳本中的變量

在shell腳本中使用變量顯得我們的腳本更加專業更像是一門語言,變量的作用當然不是爲了專業。如果你寫了一個長達1000行的shell腳本,並且腳本中出現了某一個命令或者路徑幾百次。突然你覺得路徑不對想換一下,那豈不是要更改幾百次?你固然可以使用批量替換的命令,但也是很麻煩,並且腳本顯得臃腫了很多。變量的作用就是用來解決這個問題的。

[root@aiker sbin]# cat variable.sh
#! /bin/bash

## In this script we will use variables.
## Writen by Aiker 2018-07-22.

d=`date +%H:%M:%S`
echo "The script begin at $d."
echo "Now we'll sleep 2 seconds."
sleep 2
d1=`date +%H:%M:%S`
echo "The script end at $d1."

腳本中使用到了反引號,你是否還記得它的作用? ‘d’ 和 ‘d1’ 在腳本中作爲變量出現,定義變量的格式爲 變量名=變量的值 當在腳本中引用變量時需要加上 ‘$’ 符號,這跟前面講的在shell中自定義變量是一致的。下面看看腳本執行結果吧:

[root@aiker sbin]# sh variable.sh
The script begin at 20:16:57.
Now we'll sleep 2 seconds.
The script end at 20:16:59.

下面我們用shell計算兩個數的和:

[root@aiker sbin]# cat sum.sh
#! /bin/bash

## For get the sum of tow numbers.
## Aiker 2018-07-22.

a=1
b=2
sum=$[$a+$b]

echo "$a+$b=$sum"

數學計算要用[ ]括起來並且外頭要帶一個 ‘$’ 腳本結果爲:

[root@aiker sbin]# sh sum.sh
1+2=3

Shell腳本還可以和用戶交互:

[root@aiker sbin]# cat read.sh
#! /bin/bash

## Using 'read' in shell script.
## Aiker 2018-07-22.

read -p "Please input a number: " x
read -p "Please input another number: " y
sum=$[$x+$y]
echo "The sum of the two numbers is: $sum"

read 命令就是用在這樣的地方,用於和用戶交互,把用戶輸入的字符串作爲變量值。腳本執行過程如下:

[root@aiker sbin]# sh read.sh
Please input a number: 2
Please input another number: 10
The sum of the two numbers is: 12

我們不妨加上 -x 選項再來看看這個執行過程:

[root@aiker sbin]# sh -x read.sh
+ read -p 'Please input a number: ' x
Please input a number: 22
+ read -p 'Please input another number: ' y
Please input another number: 13
+ sum=35
+ echo 'The sum of the two numbers is: 35'
The sum of the two numbers is: 35

有時候我們會用到這樣的命令 /etc/init.d/iptables restart 前面的/etc/init.d/iptables文件其實就是一個shell腳本,爲什麼後面可以跟一個 “restart”? 這裏就涉及到了shell腳本的預設變量。實際上,shell腳本在執行的時候後邊是可以跟參數的,而且還可以跟多個。

[root@aiker sbin]# cat option.sh
#! /bin/bash

sum=$[$1+$2]
echo "sum=$sum"

執行結果爲:

[root@aiker sbin]# sh -x option.sh 1 2
+ sum=3
+ echo sum=3
sum=3

在腳本中,你會不會奇怪,哪裏來的$1和$2,這其實就是shell腳本的預設變量,其中$1的值就是在執行的時候輸入的1,而$2的值就是執行的時候輸入的$2,當然一個shell腳本的預設變量是沒有限制的,這回你明白了吧。另外還有一個$0,不過它代表的是腳本本身的名字。不妨把腳本修改一下:

[root@aiker sbin]# cat option.sh
#! /bin/bash

echo "$1 $2 $0"

執行結果:

[root@aiker sbin]# sh option.sh  1 2
1 2 option.sh

shell腳本中的邏輯判斷

如果你學過C或者其他語言,相信你不會對 if 陌生,在shell腳本中我們同樣可以使用 if 邏輯判斷。在shell中if判斷的基本語法爲:

1)不帶else

if 判斷語句; then
command
fi

例如:

[root@aiker sbin]# cat if1.sh
#! /bin/bash

read -p "Please input your score: " a
if (($a<60)); then
    echo "You didn't pass the exam."
fi

在if1.sh中出現了 (($a<60)) 這樣的形式,這是shell腳本中特有的格式,用一個小括號或者不用都會報錯,請記住這個格式。執行結果爲:

[root@aiker sbin]# sh if1.sh
Please input your score: 90
[root@aiker sbin]# sh if1.sh
Please input your score: 33
You didn't pass the exam.

2)帶有else

if 判斷語句 ; then
command
else
command
fi

例如:

[root@aiker sbin]# cat if2.sh
#! /bin/bash

read -p "Please input your score: " a
if (($a<60)); then
     echo "You didn't pass the exam."
else
     echo "Good! You passed the exam."
fi

執行結果:

[root@aiker sbin]# sh if2.sh
Please input your score: 80
Good! You passed the exam.
[root@aiker sbin]# sh if2.sh
Please input your score: 25
You didn't pass the exam.

和上一例唯一區別的地方是,如果輸入大於等於60的數字會有所提示。

3)帶有elif

if 判斷語句一 ; then
command
elif 判斷語句二; then
command
else
command
fi

例如:

[root@aiker sbin]# cat if3.sh
#! /bin/bash

 read -p "Please input your score: " a
 if (($a<60)); then
         echo "You didn't pass the exam."
 elif (($a>=60)) && (($a<85)); then
         echo "Good! You pass the exam."
 else
         echo "very good! Your socre is very high!"
 fi

這裏的 && 表示 “並且” 的意思,當然也可以使用 || 表示 “或者” 執行結果爲:

[root@aiker sbin]# sh if3.sh
Please input your score: 90
very good! Your socre is very high!
[root@aiker sbin]# sh if3.sh
Please input your score: 60
Good! You pass the exam.

以上只是簡單的介紹了if語句的結構。在判斷數值大小除了可以用 (( )) 的形式外,還可以使用 [ ] 但是就不能使用>, < , = 這樣的符號了,要使用 -lt (小於),-gt (大於),-le (小於等於),-ge (大於等於),-eq (等於),-ne (不等於)。下面就以命令行的形式簡單比較一下,不再寫shell腳本了。

[root@aiker sbin]# a=10; if [ $a -lt 5 ]; then echo ok; fi
[root@aiker sbin]# a=10; if [ $a -gt 5 ]; then echo ok; fi
ok
[root@aiker sbin]# a=10; if [ $a -ge 10 ]; then echo ok; fi
ok
[root@aiker sbin]# a=10; if [ $a -eq 10 ]; then echo ok; fi
ok
[root@aiker sbin]# a=10; if [ $a -ne 10 ]; then echo ok; fi

再看看if中使用 && 和 ||的情況:

[root@aiker sbin]# a=10; if [ $a -lt 1 ] || [ $a -gt 5 ]; then echo ok; fi
ok
[root@aiker sbin]# a=10; if [ $a -gt 1 ] || [ $a -lt 10 ]; then echo ok; fi
ok

shell 腳本中if還經常判斷關於檔案屬性,比如判斷是普通文件還是目錄,判斷文件是否有讀寫執行權限等。常用的也就幾個選項:

-e :判斷文件或目錄是否存在

-d :判斷是不是目錄,並是否存在

-f :判斷是否是普通文件,並存在

-r :判斷文檔是否有讀權限

-w :判斷是否有寫權限

-x :判斷是否可執行

使用if判斷時,具體格式爲:

if [ -e filename ] ; then

例子:

[root@aiker sbin]# if [ -d /home/ ]; then echo ok; fi
ok
[root@aiker sbin]# if [ -f /home/ ]; then echo ok; fi

因爲 /home/ 爲目錄爲非文件,所以並不會顯示 “ok” .

[root@aiker sbin]# if [ -f /root/test.txt ]; then echo ok; fi
ok
[root@aiker sbin]# if [ -r /root/test.txt ]; then echo ok; fi
ok
[root@aiker sbin]# if [ -w /root/test.txt ]; then echo ok; fi
ok
[root@aiker sbin]# if [ -x /root/test.txt ]; then echo ok; fi
[root@aiker sbin]# if [ -e /root/test1.txt ]; then echo ok; fi

在shell 腳本中,除了用if來判斷邏輯外,還有一種常用的方式,那就是case了。具體格式爲:

case  變量  in
value1)
          command
          ;;
value2)
          command
          ;;
value3)
          command
          ;;
*)
          command
          ;;
esac

上面的結構中,不限制value的個數, * 則代表除了上面的value外的其他值。下面寫一個判斷輸入數值是奇數或者偶數的腳本:

[root@aiker sbin]# cat case.sh
#! /bin/bash

read -p "Input a number: " n
a=$[$n%2]
case $a in

    1)
        echo "The number is odd."
        ;;
    0)
        echo "The number is even."
        ;;
    *)
        echo "It's not a number!"
        ;;
esac

$a 的值或爲1或爲0,執行結果爲:

[root@aiker sbin]# sh case.sh
Input a number: 100
The number is even.
[root@aiker sbin]# sh case.sh
Input a number: 101
The number is odd.

case腳本常用於編寫系統服務的啓動腳本,例如/etc/init.d/iptables中就用到了,你不妨去查看一下。

shell腳本中的循環

Shell腳本中也算是一門簡易的編程語言了,當然循環是不能缺少的。常用到的循環有for循環和while循環。下面就分別介紹一下兩種循環的結構。

  1. for循環
[root@aiker sbin]# cat for.sh
#!  /bin/bash

for i in `seq 1 5`; do
    echo $i
done

腳本中的 seq 1 5 表示從1到5的一個序列。你可以直接運行這個命令試下。腳本執行結果爲:

[root@aiker sbin]# sh for.sh
1
2
3
4
5

通過這個腳本就可以看到for循環的基本結構:

for 變量名 in 循環的條件; do
command
done

這裏的 “循環的條件” 可以寫成一組字符串或者數字(用1個或者多個空格隔開), 也可以是一條命令的執行結果:

[root@aiker sbin]# for i in 1 2 3 a b; do echo $i; done
1
2
3
a
b

也可以寫引用系統命令的執行結果,就像那個 seq 1 5 但是需要用反引號括起來:

[root@aiker sbin]# for file in `ls`; do echo $file; done
case.sh
first.sh
for.sh
if1.sh
if2.sh
if3.sh
option.sh
read.sh
sum.sh
variable.sh
  1. while循環
[root@aiker sbin]# cat while.sh
#! /bin/bash

a=5
while [ $a -ge 1 ]; do
    echo $a
    a=$[$a-1]
done

while 循環格式也很簡單:

while 條件; do

      command

done

上例腳本的執行結果爲:

[root@aiker sbin]# sh while.sh
5
4
3
2
1

另外你可以把循環條件拿一個冒號替代,這樣可以做到死循環,常常這樣寫監控腳本:

while :; do
    command
    sleep 3
done

shell腳本中的函數

如果你學過開發,肯定知道函數的作用。如果你是剛剛接觸到這個概念的話,也沒有關係,其實很好理解的。函數就是把一段代碼整理到了一個小單元中,並給這個小單元起一個名字,當用到這段代碼時直接調用這個小單元的名字即可。有時候腳本中的某段代總是重複使用,如果寫成函數,每次用到時直接用函數名代替即可,這樣就節省了時間還節省了空間。

下面寫一個簡單的帶有函數功能的shell腳本:

[root@aiker sbin]# cat func.sh
#! /bin/bash

function sum()
{
    sum=$[$1+$2]
    echo $sum
}

sum $1 $2

執行結果如下:

[root@aiker sbin]# sh func.sh 1 2
3

func.sh中的 sum() 爲自定義的函數,在shell腳本函數的格式爲:

function 函數名() {

command

}

有一點要提醒你一下,在shell腳本中,函數一定要寫在最前面,不能出現在中間或者最後,因爲函數是要被調用的,如果還沒有出現就被調用,肯定是會出錯的。

shll腳本練習題

Shell腳本大體上就介紹這麼多了,所舉的例子都是最基礎的,所以即使你把所有例子完全掌握也不代表你的shell腳本編寫能力有多麼好。所以剩下的日子裏請你儘量要多練習,多寫腳本,寫的腳本越多,你的shell腳本能力就越強。希望你能夠找專門介紹shell腳本的書籍深入的去研究一下它。隨後將留幾個shell腳本的練習題,你最好不要偷懶。

  1. 編寫shell腳本,計算1-100的和;
  2. 編寫shell腳本,要求輸入一個數字,然後計算出從1到輸入數字的和,要求,如果輸入的數字小於1,則重新輸入,直到輸入正確的數字爲止;
  3. 編寫shell腳本,把/root/目錄下的所有目錄(只需要一級)拷貝到/tmp/目錄下;
  4. 編寫shell腳本,批量建立用戶user_00, user_01, ... user_100並且所有用戶同屬於users組;
  5. 編寫shell腳本,截取文件test.log中包含關鍵詞 ‘abc’ 的行中的第一列(假設分隔符爲 ”:” ),然後把截取的數字排序(假設第一列爲數字),然後打印出重複次數超過10次的列;
  6. 編寫shell腳本,判斷輸入的IP是否正確(IP的規則是,n1.n2.n3.n4,其中1<n1<255, 0<n2<255, 0<n3<255, 0<n4<255)。

習題答案:

1.

[root@aiker script]# vim !$
vim sum1.sh
#!/bin/bash
#Author: Aiker
sum=0
for i in $(seq 1 100)
do
    sum=$(($i+$sum))
done
echo $sum
[root@aiker script]# sh sum1.sh 
5050

或者:

[root@aiker script]# cat sum.sh 
#!/bin/bash
#Author: Aiker
sum=0
for i in $(seq 1 100)
do
    sum=$[$i+$sum]
done
echo $sum

[root@aiker script]# sh sum.sh 
5050

2、

#!/bin/bash
#Author: Aiker
read -p "please input a number:" i
while [[ $i -lt 1 ]]
do
  read -p "please input a number,it must be greater then "1":" i
done
sum=0
for n in $(seq 1 $i)
do
    sum=$(($n+$sum))
done
echo $sum 
[root@aiker script]# sh !$
sh inputd.sh
please input a number:q
please input a number,it must be greater then 1:11
66
[root@aiker script]# sh inputd.sh
please input a number:0
please input a number,it must be greater then 1:-1
please input a number,it must be greater then 1:-2
please input a number,it must be greater then 1:50
1275
[root@aiker script]# sh inputd.sh
please input a number:100
5050

3、

[root@aiker script]# vim cpd.sh
#!/bin/bash
#Author: Aiker
cd /root
for i in $(ls);do
    if [[ -d $i ]];then
        cp -r $i /tmp/
    fi
done
[root@aiker script]# sh -x cpd.sh 
+ cd /root
++ ls
+ for i in '$(ls)'
+ [[ -d 1 ]]
+ for i in '$(ls)'
+ [[ -d 1.cap ]]
+ for i in '$(ls)'
+ [[ -d 1.txt ]]
+ for i in '$(ls)'
+ [[ -d 2.txt ]]
+ for i in '$(ls)'
+ [[ -d 3.txt ]]
+ for i in '$(ls)'
+ [[ -d 4.txt ]]
...
[root@aiker script]# ls /tmp/
gitroot
pipework
pyscript
script

4、

#! /bin/bash

groupadd users

for i in `seq 0 9`; do
        useradd -g users user_0$i
done

for j in `seq 10 100`; do
        useradd -g users user_$j
done

5、

#! /bin/bash

awk -F':' '$0~/abc/ {print $1}' test.log >/tmp/n.txt
sort -n n.txt |uniq -c |sort -n >/tmp/n2.txt
awk '$1>10 {print $2}' /tmp/n2.txt

6、

#! /bin/bash

checkip() {

        if echo $1 |egrep -q '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$' ; then
                a=`echo $1 | awk -F. '{print $1}'`
                b=`echo $1 | awk -F. '{print $2}'`
                c=`echo $1 | awk -F. '{print $3}'`
                d=`echo $1 | awk -F. '{print $4}'`

                for n in $a $b $c $d; do
                        if [ $n -ge 255 ] || [ $n -le 0 ]; then
                                echo "the number of the IP should less than 255 and greate than 0"
                                return 2
                        fi
                done
        else

                echo "The IP you input is something wrong, the format is like 192.168.100.1"
                return 1
        fi

}

rs=1
while [ $rs -gt 0 ]; do
    read -p  "Please input the ip:" ip
    checkip $ip
    rs=`echo $?`
done

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