1. 程式人生 > >Linux系統——shell指令碼

Linux系統——shell指令碼

 

 

 

shell指令碼程式設計

作用:通過命令列解析的方式,自動執行設定好的程式或命令程式碼。(若將指令碼掛到定時任務中,就會自動在非工作時間裡自動觸發執行程式)

 

Shell指令碼檔案.sh”結尾

規範的Shell指令碼第一行會指出由哪個程式(直譯器)來執行指令碼中的內容。在linux bash程式設計中一般為:#!/bin/bash (表示該指令碼運用/bin/bash命令進行解析)

 

Shell的輸出用echo命令;

Python的輸出用print命令

 

執行指令碼的方式:

方法一:/bin/sh

bash的軟連結,也就是說我麼既可以用sh執行也可以用bash執行指令碼

# sh yunjisuan.sh

# bash yunjisuan.sh

方法二:以絕對路徑的方式執行指令碼(前提:將該指令碼新增x執行許可權 )

# chmod +x yunjisuan.sh

# /root/benet/yunjisuan.sh

 

方法三:以source指令碼路徑的方式執行指令碼(等同於“.指令碼路徑” )只在當前環境生效

# source yunjisuan.sh

# . yunjisuan.sh

 

方法四:以相對路徑的方式執行指令碼(前提:將該指令碼新增

x執行許可權 )

# ./yunjisuan.sh

 

 

寫指令碼

(1)vim編輯yunjisuan.sh

(2)檢視yunjisuan.sh指令碼

 

(3)執行yunjisuan.sh指令碼

指令碼中書寫的命令必須是非互動式的命令!!!

 

 

雖然指令碼可以自動化執行,但指令碼不會判斷命令是否執行成功,因此需要進行邏輯判斷

方法一:

test命令

判斷該字串表示為檔案還是目錄

-d 測試是否為目錄

-e 測試目錄或檔案是否存在

-f 測試是否為檔案

-l 測試是否為連結檔案

-r 測試當前使用者是否可讀

-w 測試當前使用者是否可寫

-x 測試當前使用者是否可執行

-s 測試檔案大小非0時為真(若為非空檔案,則為真)

-u 測試是否具有SUID屬性

-g 測試是否具有SGID屬性

-k 測試是否具有sticky bit 粘滯位屬性

-z 測試字串是否為空(zero

file1 -nt file2測試第一個檔案是否比第二個檔案新(new then

file1 -ot file2 測試第一個檔案是否比第二個檔案舊(old then

file1 -ef file2 測試第一個檔案是否與第二個檔案為同一個檔案(link之類的檔案)

 

# test -d 目標路徑

# echo $?判斷是否是目錄,若顯示0表示真(是目錄),若顯示非0則假(不是目錄)

 

# test -f 目標路徑

判斷目標是不是檔案

# echo $?

 

$?返回值參考

0 表示執行成功

2 許可權拒絕

1125 表示執行失敗,指令碼命令,系統命令錯誤或引數傳遞錯誤;

126 找到該命令,但無法執行

127 未找到要執行的命令

128 命令被系統強制結束

 

方法二:

使用[]”,表示開啟判斷條件(“[]”兩邊須有空格)

# xx=”welcome”

# [ $xx == welcome] && echo “0” || echo “1”

0

[]”應用於整數 (格式:整數1 操作符 整數2

-gtgreat than)表示大於

-ltless than)表示小於

-eqequal)表示等於

-ne not equal)表示不等於

-ge 表示大於等於

-le表示小於等於

&& 邏輯與,表示前一指令為真,執行後一指令,否則不執行

|| 邏輯或,表示前一指令為假,執行後一指令,否則不執行

# xx=”44”

# if [ -f "$file1" ];then echo 1;else echo 0;fi

相當於# [ -f "file1" ] && echo 1 ||echo 0

 

# [ $xx -eq 34 ] && echo “0” || echo “1”

1

字串測試

[ -z “字串” ]  字串內容為空

[ -n “字串” ]  字串內容不為空

 

${#變數名}   求變數的字串位數,判斷字串是否為空

# xx=”123411”

# echo ${#xx}

6

# xx=””

# echo ${#xx}

0

指令碼的互動式輸出模式

方法一:

執行指令碼過程中,存在需要使用者輸入內容的情況,通過read命令進行互動式輸入

添加註釋給使用者:

輸入格式:read -p “文字提示” 變數名

0出格式:echo $變數名

 

 

建立使用者名稱和密碼

#!/bin/bash

 

User="yunjisuan"

Passwd="123456"

read -p "請輸入使用者名稱" user

read -p "請輸入密碼" passwd

[ $User -eq $user ] && echo "YOU ARE RIGHT" || echo YOU ARE WRONG

[ $Passwd -eq $user ] && echo "YOU ARE RIGHT" || echo "YOU ARE WRONG"

邏輯測試

[]”中,-a&&) 表示並且;

-o ||)表示或者

!邏輯否

方法二:通過引數傳遞的方式進行互動式輸入

/etc/init.d/ 服務啟動指令碼

服務啟動指令碼/etc/init.d/後面寫的內容就是引數,通過這個引數進行互動式輸入

 

$# 表示指令碼要處理的的引數個數

$? 表示命令或指令碼執行狀態碼

$* 表示橫向羅列指令碼要處理的所有引數(把所有引數視為整體)

[email protected] 表示橫向羅列指令碼要處理的所有引數(把所有引數視為單個引數的組合)

$0 表示指令碼檔案的絕對路徑或相對路徑(指令碼檔案的執行路徑)

$1 第一個引數

$n n個引數

 

指令碼中反引號``”、“$()”的應用

 

變數的算數運算

變數的數值運算多用於指令碼程式的過程控制,只能進行簡單的整數運算,不支援小數運算,整數值得運算主要通過內部命令expr進行。

 

# x=11

# y=22

# expr $x \* $y

格式: expr 變數1 運算子 變數2...

++ --   增加及減少,可前置也可放在結尾

 一元運算的正負號,非,邏輯與位的取反

\*   乘法

/   除法

%  取餘

**  冪運算

+ 加法

-  減法

<<=>>= 比較符號

== 1+= 相等,不相等

<<  向左移動

>>  向右移動

>>>  0右移

& 位的與AND

^  位的異或

|  位的或

&&  位的AND

||   位的OR

?:  條件表示式

=+= -= *=等 賦值運算子

 

(())用法(常用於簡單的整數運算)

(())”在命令列執行時不需要$符號,但是輸出需要$符號

“(())”裡所有字元之間有無或多個空格沒有任何影響

 

# b=$((1+2**3-4%3))

# echo $b

8

# echo $((1+2**3-4%3))

8

 

a++a--,++a,--a區別

變數a在前,表示式的值為a,然後a自增或自減,變數a在符號後,表示式值自增或自減,然後a值自增或自減。

# a=8

# echo $a

8a賦值為8

# echo $((a+=1)) #相當於a=a+1

9a賦值為a+1=9

# echo $((a++)) #a在前,先輸出a的值,在加1

9a賦值為上一值a9

# echo $a

10a賦值上一值的9++9+110

# echo $((a--))

10a取上一值a10

# echo $a

9a取上一值10--10-19

# echo $((++a))

10(先+1,在取上一值9,1+9=10

# echo $a

10(取上一值10

# echo $((--a))

9(先-1,取上一值1010-1=9

# echo $a

9(取上一值9

 

Shell指令碼中不支援“i++”表達,可以用C語言的((i++))shell中表示

或者“let h++”

 

 

If條件語句

格式(fi 表示結束)

(一)單分支條件判斷語句

1

if    條件1

then

動作1

else

動作2

fi

2)(用;分隔)

if 條件1;then

動作1

else

動作2

fi

 

vim中編輯yunjisuan.sh指令碼

#!/bin/bash

read -p "請輸入一個數字:" num

if [ $num == 60 ];then

echo "猜對了"

fi

# sh yunjisuan.sh

請輸入一個數字:60

猜對了

vim中編輯yunjisuan.sh指令碼

#!/bin/bash

read -p "請輸入一個數字:" num

if [ $num == 60 ];then

echo "猜對了"

else

echo "猜錯了"

fi

# sh yunjisuan.sh

請輸入一個數字:45

猜錯了

 

(二)多分支條件判斷語句(elif就是else if

 if 條件1;then

動作1

elif 條件2;then

動作2

else

動作3

fi

檢視當前檔案的絕對目錄

# dirname 目標檔案的絕對路徑

 

檢視當前檔案的基本檔名稱

# basename 目標檔案的絕對路徑

 

自定義搭建本地yum倉庫指令碼

(1)移除光碟,再掛載

(2)判斷光碟掛載

(3)掛載本地yum倉庫指令碼

(4)判斷一級目錄、二級目錄是否存在

(5)配置檔案手動生成,避免之前被修改過(重定向或echo -e

#!/bin/bash

umount /dev/sr0&>/dev/null

[ -d /media/cdrom ]|| mkdir -p /media/cdrom(還可以用testif的方法)

mount /dev/sr0 /media/cdrom &>/dev/null

if [ $? -ne 0 ];then

        echo "請插入光碟"

        exit

fi

[ -d /etc/yum.repos.d ] || mkdir -p /etc/yum.repos.d

cd /etc/yum.repos.d

mv * /tmp/  

echo -e “[local]\nname=local\nbaseurl=file:///media/cdrom/\ngpgcheck=0\nenabled=1”>/etc/

yum.repos.d/localyum.repo

(或)

cat > /etc/yum.repos.d/localyum.repo << FOF

[local]

name=local

baseurl=file:///media/cdrom

gpgcheck=0

enabled=1

F0F

yum -y clean all &>/dev/null

yum makecache &>/dev/null

[ $? -eq 0 ] && echo "yum倉庫搭建完畢" || echo "快取建立失敗!"

配置檔案手動生成

用指令碼手動生成配置檔案,一般用重定向

輸入重定向通過FOF標識(任意定義,但成對出現),將FOF之間的內容輸入重定向到catcat再輸出重定向給/etc/yum.repos.d/yum/repo檔案

 

while迴圈語句

格式:

while 條件

do

迴圈體(指令)

done

 

 

休息命令:sleep 1 休息1秒,usleep 1000000 休息1秒,單位微妙

 

1100求和

#!/bin/bash

 

i=1

sum=0

while [ $i -lt 100 ]

do

((sum=sum+i))

((i++))

done

echo $sum

守護程序

#!/bin/bash

 

while true

do

        uptime >> /var/log/uptime.log

        sleep 2

done

倒計時

#!/bin/bash

 

i=10

while [ $i -gt 0 ]

do

        echo $i

        ((i--))

done

無限迴圈

1

#!/bin/bash

 

read -p "輸入:" i

while [ $i -gt 0 ]

do

        echo $i

        let i++

done

2

#!/bin/bash

 

read -p "輸入:" i

while :

do

        echo $i

        let i++

done

強行中止

# exit

 

防止指令碼執行中斷的方法

1sh while01.sh & #放在後臺執行
2screen 分離 ctrl+a+d 檢視screen -ls進入screen -r num
3nohup while01.sh &

 

for迴圈語句

格式:

for 變數名 in 變數取值列表

do

迴圈體(指令)

done

示例:迴圈

for ((i=0;i<10:i++))

do

echo $i

done

 

列印列表元素

for的三種輸出方式

1

#!/bin/bash

 

for i in 1 2 3 4 5

do

        echo $i

done

(2)

#!/bin/bash

 

for i in {1..5}   

do

        echo $i

done

(3)

#!/bin/bash

 

for i in `seq 5`

do

        echo $i

done

#!/bin/bash

 

h=0

for i in {1..10}

do

        echo $h

        let h++

done

開機啟動項優化

#!/bin/bash

for i in `chkconfig | grep "3:on" | awk '{print $1}'`

do

        chkconfig $i off

done

if [ -e sysstat ]

        then echo "sysstat exit"

        else mkdir -p /media/cdrom

        mount /dev/sr0 /media/cdrom

        if [ $? -ne 0 ];then

                echo "FAILED"

                exit

        else yum -y install sysstat

                if [ $? -ne 0 ];then

                        echo "install error"

                        exit

                else echo "install successed"

                fi

        fi

fi

for h in sshd network crond rsyslog sysstat

do

        chkconfig $h on

done

[ $? == 0 ]&& echo "successed" || echo "failed"

/yunjisuan目錄批量建立檔案

#!/bin/bash

Path=/yunjisuan

[ -d "$Path" ] || mkdir -p $Path

for i in `seq 10`

do

        touch $Path/yunjisuan_$i.html

done

批量改名

#!/bin/bash

Path=/yunjisuan

[ -d "$Path" ] || mkdir -p $Path

for file in `ls $Path`

do

        mv $Path/$file "$Path/"`echo $file | sed -r 's#yunjisuan(.*).html#linux\1.HTML#g'`

done

批量建立使用者並設定密碼

#!/bin/bash

 

User=yunjisuan

Path=/tmp

 

for user in ${User}{01..10}

do

        useradd $user > /dev/null 2>&1

                if [ $user -ne 0 ];then

                echo "$user created failed"

                echo "scripts begin to rollback"

                        for i in ${User}{01..10}

                        do

                                userder -r $i >/dev/null 2>&1

                                [ $? -eq 0 ] || exit 1

                        done

                echo >$Path/usr_passwd

                exit 1

                else

                passWD=`echo $RANDOM | md5sum | cut -c1-8` (表示取一大串隨機數,從這串隨機數前擷取1-8位)

                [ -d $Path ] || mkdir $Path

                echo $passWD | passwd --stdin $user

                echo "$user:$passWD">> $Path/user_passwd

                fi

done

 

exit 0 表示正常執行程式並退出程式

exit 1 exit -1 表示非正常執行導致退出程式

 

echo -n 不換行

 

獲取當前目錄下的目錄名做為變數列表列印輸出

#!/bin/bash

Path=`pwd`

echo $Path

for filename in `ls`

do

        [ -d ${Path}/${filename} ] && echo $filename

done

九九乘法表

#!/bin/bash

 

for ((i=1;i<10;i++))

do

        for ((j=1;j<=i;j++))

        do

                echo -n "$j * $j =$((i*j))"

                echo -n " "

        done

        echo " "

done

 

顯示出1-100的偶數

#!/bin/bash

 

for i in {1..100}

do

        [ $(($i%2)) -eq 0 ] && echo $i

done

 

顯示出1000-2000的質數

#!/bin/bash

 

for i in {1000..2000}

do

        [ $(factor $i | awk '{print NF}') -le 2 ] && echo $i

done

 

factor命令:分解因數

 

Case語句

用途:選單;啟動指令碼

case語句適合變數的值少,且為固定的數字或字串集合。 系統服務啟動指令碼傳參的判斷多用case語句

格式:

case "字串變數" in

    1

        指令1

        ;;

    2

        指令2

        ;;

    *)

        指令

esac

 

注意:case語句相當於一個if的多分支結構語句

 

1的選項

apple

    echo -e "@RED_COLOR apple $RES"

    ;;

也可以這樣寫,輸入2種格式找同一個選項

apple|APPLE)

    echo -e "$RED_COLOR apple $RES"

    ;;

 

服務指令碼框架

#!/bin/bash

. /etc/init.d/functions

case $1 in

        start)

                action "服務開始啟動"   /bin/true

                ;;

        stop)

                action "服務準備停止"   /bin/false

                ;;

        restart)

                action "服務準備停止"   /bin/true

                action "服務來時啟動"   /bin/true

                ;;

        *)

                echo "請輸入正確引數"

                ;;

esac

 

在當前指令碼引用函式庫(絕對路徑)

Function對應的是action “ ” 路徑

/bin/true 表示一個標誌

  

 

 

設定指令碼配置啟動級別

(1)為指令碼設定啟動級別

將指令碼複製到/etc/init.d/下,vim編輯/etc/init.d/cash.sh指令碼

# chkconfig: 35 90 10

 

(2)chkconfig中新增指令碼

# chkconfig --add case.sh

# chkconfig --list case.sh

 

(3)設定關閉cash.sh

# chkconfig case.sh off

 

Shell函式

不論什麼程式語言,基本只有三種程式設計的方法(指如何去編寫程式碼的方法論)

1、面向過程

2、面向物件

3、函數語言程式設計

shell語言中,只能支援面向過程這種程式設計方法。

 

shell中,function表示函式(functionreturn可以不寫)

格式:

function 函式名(){

命令序列

[return x]

}

 

函式名

 

函式體只有被呼叫時才會啟動,若要重複啟動該函式,只需將函式名重複n

獲取隨機數的幾種方法

(1)通過系統環境變數$RANDOM

# echo $RANDOM

6178

 

# echo $RANDOM

30890

 

# echo $((RANDOM%9)) #輸出09之間隨機數

2

# echo $((RANDOM%9))

 

# echo $((RANDOM%9))$((RANDOM%9)) #輸出0099 隨機數

64

 

# echo $RANDOM|md5sum #隨機數長短不一,可以用md5sum命令統一格式化

599e328a94329684ce5c92b850d32f26 -

 

 

(2)通過openssl產生

# openssl rand -base64 8

aND8WMRM6vQ=

 

# openssl rand -base64 8

RsRdRq/9vi4=

 

# openssl rand -base64 8|md5sum

b1108cafbc2291392e41d2c914360138 -

 

# openssl rand -base64 10

1frkA2kIJODxqQ==

 

 

(3)通過時間獲得隨機數(date命令詳解見下頁文件)

# echo $(date +%N)

361599138

 

# echo $(date +%t%N)

950526316

 

(4)Urandom

# head /dev/urandom | cksum

621330951 2535

 

# head /dev/urandom | cksum

404398617 2470

 

 

(5)UUID

# cat /proc/sys/kernel/random/uuid

8a6c5bbe-2d42-44ac-9ef1-3e7683a613e3

 

# cat /proc/sys/kernel/random/uuid

c828c209-5b5f-4bc7-917c-678ed4215988

 

# uuidgen

961dc354-81b2-4564-9b85-6095ed4bc7b5

 

迴圈控制語句breakcontinueexitreturn

作用:用於迴圈結構中控制迴圈語句

 

break nn表示跳出迴圈的層數,如果省略n表示跳出整個迴圈(只跳出所在位置的迴圈)

continue nn表示退出到第n層繼續迴圈,如果省略n表示跳過本次迴圈,忽略本次迴圈剩餘程式碼,進入迴圈的下一次迴圈exit n:退出當前shell程式,n為返回值,n也可以省略,在下一個shell裡通過$?接收這個n

return n用在函式裡,做為函式的返回值,用於判斷函式執行是否正確。和exit一樣,如果函式裡有迴圈,也會直接退出迴圈,退出函式

 

break nn表示跳出迴圈的層數,如果省略n表示跳出整個迴圈

#!/bin/bash

for ((i=0;i<=5;i++))

do

        [ $i -eq 3 ] && break

        echo $i

done

echo "ok"

break只跳出所在位置的一個整個迴圈迴圈

 

continue nn表示退出到第n層繼續迴圈,如果省略n表示跳過本次迴圈,忽略本次迴圈剩餘程式碼,進入迴圈的下一次迴圈

 

#!/bin/bash

 

for ((i=0;i<=5;i++))

do

        [ $i -eq 3 ] && continue

        echo $i

done

echo "ok"

exit n:退出當前shell程式,n為返回值,n也可以省略,在下一個shell裡通過$?接收這個n

 

#!/bin/bash

 

for ((i=0;i<=5;i++))

do

        [ $i -eq 3 ] && exit 2

        echo $i

done

echo "ok"

return n:用在函式裡,做為函式的返回值,用於判斷函式執行是否正確。和exit一樣,如果函式裡有迴圈,也會直接退出迴圈,退出函式

 

#!/bin/bash

 

function xxx() {

        for ((i=0;i<=5;i++))

        do

                [ $i -eq 3 ] && return 7

                        echo $i

        done

        echo "ok"

}

 

xxx

echo $?

shell指令碼的除錯

(1)使用dos2unix處理指令碼

windows編輯的指令碼到Linux下需要使用這個命令
dos2unix windows.sh

(2)使用echo命令除錯

在變數讀取或修改的前後假如echo $變數,也可在後面使用exit退出指令碼,這樣可以不用註釋後邊程式碼

3)利用bash的引數除錯

sh [-nvx]
-n:不會執行該指令碼,僅查詢指令碼語法是否有問題,並給出錯誤提示。可用於生產伺服器那些只能執行一次不可逆的指令碼。
-v:在執行指令碼時,先將指令碼的內容輸出到螢幕上然後執行指令碼,如果有錯誤,也會給出錯誤提示。(一般不用)
-x:將執行的指令碼內容及輸出顯示到螢幕上,常用

 

1)要記得首先用dos2unix對指令碼格式化
2)直接執行指令碼根據報錯來除錯,有時報錯不準確。
3sh -x除錯整個指令碼,顯示執行過程。
4set -xset +x除錯部分指令碼(在指令碼中設定)
5echo輸出變數及相關內容,然後緊跟著exit退出,不執行後面程式的方式,一步步跟蹤指令碼,對於邏輯錯誤比較好用。

 

ping -c 1 ip   ping一次

#!/bin/bash

for ip in 192.168.214.{1..254}

do

        ping -c 1 $ip &> /dev/null

        if [$? -eq 0 ];then

                echo "$ip正常狀態"

        fi

done