shell– 腳本編程

編程基礎

腳本基本格式

變量

運算

條件測試

流程控制

函數

數組

高級字符串操作

高級變量

配置用戶環境

編程基礎

程序:指令+數據

程序編程風格:
    過程式:以指令為中心,數據服務于指令
    對象式:以數據為中心,指令服務于數據
shell程序:提供了編程能力,解釋執行

程序的執行方式

計算機:運行二進制指令;

編程語言:

低級:匯編
高級:
編譯:高級語言-->編譯器-->目標代碼

解釋:高級語言-->解釋器-->機器代碼
    shell, perl, python

編程基本概念

編程邏輯處理方式:

    順序執行
    循環執行
    選擇執行
shell編程:過程式、解釋執行

編程語言的基本結構:

數據存儲:變量、數組
表達式: a + b
語句:if

shell腳本基礎

shell腳本是包含一些命令或聲明,并符合一定格式的文本文件

格式要求:首行shebang機制
    #!/bin/bash
    #!/usr/bin/python
    #!/usr/bin/perl

shell腳本的用途有:
    自動化常用命令
    執行系統管理和故障排除
    創建簡單的應用程序
    處理文本或文件

創建shell腳本

第一步:使用文本編輯器來創建文本文件
    第一行必須包括shell聲明序列:#!
    #!/bin/bash
    添加注釋
    注釋以#開頭
第二步:運行腳本
    給予執行權限,在命令行上指定腳本的絕對或相對路徑
        如果不輸入絕對路徑或相對路徑shell先找¥PATH,所以找不到就運行不了。
        用戶添加$PATH條目,家目錄修改.bash_profile文件加入要修改的路徑。 
    直接運行解釋器,將腳本作為解釋器程序的參數運行

系統為每個用戶在家目錄生成了以個bin的$PATH的環境變量。用戶可以自己在自己的家目錄建立一個bin的目錄,下面放可執行腳本即可在命令提示符下直接運行,不需要路徑就能執行。

shell腳本范例

#!/bin/bash
#author: wang
#Version: 1.0
#Description:Thisscriptdisplayssomeinformationaboutyour# environment
echo"Greetings.Thedateandtimeare$(date)" echo"Yourworkingdirectoryis:$(pwd)"

bash的配置文件

兩類:
    profile類:為交互式登錄的shell進程提供配置
    bashrc類:為非交互式登錄的shell進程提供配置

登陸類型

    交互式登陸shell進程:
        直接通過某終端輸入賬號和密碼后登錄打開的shell進程
        使用su命令:su - USERNAME ,或使用su -l USERNAME 執行的登錄切換
    非交互式登錄shell進程
        su USERNAME 執行的登錄切換
        圖形界面下打開的終端
        運行腳本

profile類

    全局:對所有用戶都生效
        /etc/profile
        /etc/proflie.d/*.sh
    用戶個人:僅對當前用戶有效。
        ~/.bash_profile
    功用:
        1、用于定義環境變量
        2、運行命令或腳本

bashrc類

    全局:
        /etc/bashrc
    用戶個人:
        ~/.bashrc
    功用:
        1、定義本地變量
        2、定義命令別名

注意:僅管理員可以修改全局配置文件。 

交互式登錄shell進程:
    /etc/profile-->/etc/profile.d/*-->~/.bashrc-->/etc/bashrc
用戶配置可以覆蓋系統配置。例如$PATH這個變量。

非交互式登錄shell進程(腳本運行讀取配置文件的順序)
    ~/.bashrc-->/etc/bashrc-->/etc/profile.d/*

腳本調試

bash -n /path/to/some_script
檢測腳本中的語法錯誤

bash -x /path/to/some_script
調試執行
shell腳本時在當前終端開啟子shell后運行。

變量

變量:命名的內存空間

數據存儲方式:
    字符:
    數值:整型,浮點型

變量:變量類型
    變量類型決定了:變量的存儲格式,數據范圍,參與的運算

數值:整型、浮點型 bash不支持浮點型,除非借助工具。

編程程序語言分類

強類型:定義變量時必須指定類型、參與運算必須符合類型要求;調用未聲明變量會產生錯誤
    如java,python

弱類型:無須指定類型,默認均為字符型;參與運算會自動進行隱式類型轉換;變量無須事先定義可直接調用
    如:bash 不支持浮點數

變量命名法則:
    1、不能使程序中的保留字:例如if, for;
    2、只能使用數字、字母及下劃線,且不能以數字開頭
    3、見名知義
    4、統一命名規則:駝峰命名法,每個單詞的第一個字母大寫 
        大駝峰:每個單詞的第一字母大寫
        小駝峰:第一單詞的第一個字母小寫,以后的都大寫

bash中變量的種類

?根據變量的生效范圍等標準:
    本地變量:生效范圍為當前shell進程;對當前shell之外的其它shell進程,包括當前shell的子shell進程均無效

    環境變量:生效范圍為當前shell進程及其子進程

    局部變量:生效范圍為當前shell進程中某代碼片斷(通常指函數)

    位置變量:$1, $2, ...來表示,用于讓腳本在腳本代碼中調用通過命令行傳遞給它的參數

        注:腳本運行是在當前shell的子shell下運行的,如有(command)括號中的再開一個子shell,腳本shell之下開啟的子shell,不可以調用的其他腳本的本地變量

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

本地變量

變量賦值:name=‘value’,

可以使用引用value:
    (1) 可以是直接字串; name=“root"
            name=var
            name=“xxx xxx  xx”多個字符串中間有空格的需要用引號如果不引用其他變量或引用命令的話單雙引號都可以。
    (2) 變量引用:
        $name, ${name} 
                $a
                $b
                $ab
                ${a}b

        name="$USER" 通過引用起它變量的值賦值
    (3) 命令引用:name=`COMMAND`, name=$(COMMAND)

            注:可以多行內容賦值到一個變量。

變量引用:${name}, $name
    "":弱引用,其中的變量引用會被替換為變量值
    '':強引用,其中的變量引用不會被替換為變量值,而保持原字符串

            "弱引用----雙引號
            [root@localhost ~]# echo "echo $USER"
            echo root

            ''強引用------單引號
            [root@localhost ~]# echo 'ehco $USER'
            ehco $USER

            ``命令引用----~下的符號
            [root@localhost ~]# echo `echo $USER`
            Root

顯示已定義的所有變量:set

刪除變量:unset name 腳本運行完畢要釋放變量。養成良好習慣。

練習 1、編寫腳本/root/bin/systeminfo.sh,顯示當前主機系統信息,包括主機名,IPv4地址,操作系統版本,內核版本,CPU型號,內存大小,硬盤大小。

2、編寫腳本/root/bin/backup.sh,可實現每日將/etc/目錄備份到/root/etcYYYY-mm-dd中

3、編寫腳本/root/bin/disk.sh,顯示當前硬盤分區中空間利用率最大的值

4、編寫腳本/root/bin/links.sh,顯示正連接本主機的每個遠程主機的IPv4地址和連接數,并按連接數從大到小排序


scp:通過終端登錄其他遠程終端,復制內容到當前終端。

scp file ipadd:path
把file拷貝到path下

環境變量

環境變量的使用必須先聲明
變量聲明、賦值:
    export name=VALUE
    declare -x name=VALUE

    declare - name=number 聲明變量為數值型
變量引用:$name, ${name}

顯示所有環境變量:
    export
    env
    printenv

刪除:unset name

bash有許多內建的環境變量:PATH, SHELL, USRE,UID, HISTSIZE, HOME, PWD, OLDPWD, HISTFILE, PS1

    聲明環境變量后,shell的子進程及其子進程的子進程都可使用。
    新的終端后就不可以使用。

注
source 腳本 和 . 腳本 執行腳本效果是一樣的,都是在當前shell下執行。
當前shell下的用戶可調用該腳本的本地變量

通過 bash 腳本和直接執行 ./腳本 效果是一樣的,都是在當前shell下開啟子shell后運行的。
當前shell不可以調用該腳本定義的本地變量。

只讀和位置變量

只讀變量:只能聲時,但不能修改和刪除,不能使用unset撤銷。
    readonlyname=VLAUE
    declare -r name=VLAUE

        可以與-x一起使用 declare -rx name=VALUE 只讀的環境變量

位置變量:在腳本代碼中調用通過命令行傳遞給腳本的參數
    $1, $2, ...:對應第1、第2等參數,shift [n]換位置
    $0: 命令本身,腳本名稱。
    $*: 傳遞給腳本的所有參數,全部參數合為一個字符串
    $@: 傳遞給腳本的所有參數,每個參數為獨立字符串
    $#: 傳遞給腳本的參數的個數
        $@ $* 只在被雙引號包起來的時候才會有差異

            [root@yangyouwei ~]# ./test1.sh a b
            first a b
            secd 
            all a b
            ========
            first a
            secd b
            all a b

            [root@yangyouwei ~]# cat test1.sh 
            #!/bin/bash
            ./test2.sh "$*"
            echo ========
            ./test2.sh "$@"

            [root@yangyouwei ~]# cat test2.sh 
            #!/bin/bash
            echo first "$1"
            echo secd "$2"
            echo all "$*"

shift

位置參數可以用shift命令左移。比如shift 3表示原來的$4現在變成$1,原來的$5現在變成$2等等,原來的$1、$2、$3丟棄,$0不移動。不帶參數的shift命令相當于shift 1。

腳本test1加上參數a和b 調用腳本test2
    test1中
            test2運行兩次但是調用的 參數不同
            (雖然某些情況下$*和$@效果是一樣的,在這里這兩個變量的值作為test2的參數,參與test2的執行。)

示例:判斷給出的文件的行數

    linecount="$(wc-l $1| cut -d' ' -f1)"
    echo "$1 has $linecountlines."

declare

-i 聲明為整數

-a 聲明為數組

-f 聲明為函數

-r 聲明為只讀

算術運算

bash中的算術運算:help let
    +, -, *, /, %取模(取余), **(乘方)

實現算術運算:
    (1) let var=算術表達式
            let var+=5等于let var=var+5
            let var-=5等于let var=var-5
            let var*=5等于let var=var#5
            let var/=5等于let var=var/5
            let var%=5等于let var=var%5

                [root@yangyouwei bin]# var=5
                [root@yangyouwei bin]# let var=var+10
                [root@yangyouwei bin]# echo $var
                15

    (2) var=$[算術表達式]
            括號中的表達式中變量可以加$也可以不加

                [root@yangyouwei bin]# echo $var
                15
                [root@yangyouwei bin]# var=$[var+30]
                [root@yangyouwei bin]# echo $var
                45


    (3) var=$((算術表達式))
            括號中的表達式中變量可以加$也可以不加

                [root@yangyouwei bin]# echo $var
                75
                [root@yangyouwei bin]# var=$((var+30))
                [root@yangyouwei bin]# echo $var
                105

    (4) var=$(expr arg1 arg2 arg3 ...) 命令引用
            expr是命令
            expr 2 + 3  數字與運算符之間必須有空格,作為三個參數。

                [root@yangyouwei bin]# expr 5 + 6
                11

    (5) declare –i var=數值  聲明變量是整數

    (6) echo ‘算術表達式’ | bc 管道輸出給bc計算

乘法符號有些場景中需要轉義,如*
        expr 2 \* 3

$RANDOM隨機數

bash有內建的隨機數生成器:$RANDOM(1-32767)

    echo $[$RANDOM%50] :0-49之間隨機數

增強型賦值:let支持 不需要寫$符號

        +=, -=, *=, /=, %=

    let varOPERvalue
        例如:let count+=3
            自加3后自賦值
            等于 let count=count+3

自增,自減

    自增,自減:
        相同結果
            let var+=1
            let var++
            let ++var

        相同結果
            let var-=1
            let var--
            let --var


    還有有區別的 賦值和運算 的先后區別

        [root@yangyouwei ~]# i=100
        [root@yangyouwei ~]# let ++i
        [root@yangyouwei ~]# echo $i
        101
        [root@yangyouwei ~]# let i++
        [root@yangyouwei ~]# echo $i
        102
        [root@yangyouwei ~]# let v=i++
        [root@yangyouwei ~]# echo $i  
        103
        [root@yangyouwei ~]# echo $v
        102

練習

練習1:寫一個腳本/root/bin/sumid.sh,計算/etc/passwd文件中的第10個用戶和第20用戶的ID之和

練習2:寫一個腳本/root/bin/sumspace.sh,傳遞兩個文件路徑作為參數給腳本,計算這兩個文件中所有空白行之和

練習3:寫一個腳本/root/bin/sumfile.sh,統計/etc, /var, /usr目錄中共有多少個一級子目錄和文件

邏輯運算

true, false
    1, 0

與:
    1 與1 = 1
    1 與0 = 0
    0 與1 = 0
    0 與0 = 0

或:
    1 或1 = 1
    1 或0 = 1
    0 或1 = 1
    0 或0 = 0

非:!取反
    ! 1 = 0
    ! 0 = 1

短路運算:

    短路與:
        第一個為0,結果必定為0;
        第一個為1,第二個必須要參與運算;

    短路或:
        第一個為1,結果必定為1;
        第一個為0,第二個必須要參與運算;

異或:^
    異或的兩個值,相同為假,不同為真

聚集命令

有兩種聚集命令的方法:

    ?復合式:date; who | wc -l

        命令會一個接一個地運行
        (date; who) | wc -l
            相當于date和who開了一個子shell運行之后傳給wc

    ?子shell:(date; who | wc -l ) >>/tmp/trace
        所有的輸出都被發送給單個STDOUT和STDERR
            相當于標準輸出和標準錯誤輸出都是子shell的標準輸出

退出狀態

進程使用退出狀態來報告成功或失敗
    ?0 代表成功,1-255代表失敗
    ?$? 變量保存最近的命令退出狀態

例如:
    $ping-c1-W1hostdown&>/dev/null
    $echo$?

退出狀態碼

bash自定義退出狀態碼
    exit [n]:自定義退出狀態碼;

    定義了退出狀態碼
        腳本中一旦遇到exit命令,腳本會立即終止;終止退出狀態取決于exit命令后面的數字

    沒有定義退出狀態碼
        如果未給腳本指定退出狀態碼,整個腳本的退出狀態碼取決于腳本中執行的最后一條命令的狀態碼

    如果放在腳本的子shell中,則退出的是子shell腳本不會退出。

條件測試

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

評估布爾聲明,以便用在條件性執行中
    ?若真,則返回0
    ?若假,則返回1

測試命令:
    ?test EXPRESSION  test是個命令
            后面的都是參數。可用雙引號。個參數需要用空格隔開

    ?[ EXPRESSION ]
        [ $a = $b ]
        [ $a ] 可以比較變量是否定義過。
            [root@yangyouwei bin]# [ $kf ]
            [root@yangyouwei bin]# echo $?
            1
            [root@yangyouwei bin]# [ $a ]
            [root@yangyouwei bin]# echo $?
            0

    ?[[ EXPRESSION ]]
        有時候單括號和雙括號還是有差別的。
        雙括號比較保險。

注意:中括號內的EXPRESSION前后必須有空白字符
        表達式中兩個比較的內容要與中間的比較的符號用空格分開
            test $a = $b
            [ $a = $b ]
    字符串比較可以用兩個等號
        [ $a == $b ]

條件性的執行操作符

根據退出狀態而定,命令可以有條件地運行
    ?&& 代表條件性的AND THEN
    COMMAND1 && COMMAND2
        COMMAND1執行成功則執行COMMAND2

    ?|| 代表條件性的OR ELSE
    COMMAND1 || COMMAND2
        COMMAND1執行失敗才會執行COMMAND2

!取反

例如:
    $grep-qno_such_user/etc/passwd\
    ||echo'Nosuchuser'
    Nosuchuser
    $ping-c1-W2station1&>/dev/null\
    >&&echo"station1isup"\ 
    >||(echo'station1isunreachable';exit1)
    station1isup

test命令

長格式的例子:
    $test "$A" == "$B" && echo "Stringsareequal"
     注意空格
    $test “$A” -eq “$B”\
    &&echo"Integersareequal"
     注意空格
簡寫格式的例子:
    $["$A"=="$B"]&&echo"Stringsareequal"
    $["$A"-eq"$B"]&&echo"Integersareequal"


注:可以不加引號

bash的測試類型 man test 可以看見測試符號幫助文件。

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

字符串測試:

    ==:是否等于;
    >: ascii碼是否大于ascii碼
    <: 是否小于
    !=: 是否不等于
    =~: 左側字符串是否能夠被右側的PATTERN(擴展正則表達式)所匹配
        注意: 此表達式一般用于[[ ]]中;
    -z "STRING":字符串是否為空,空為真,不空為假
    -n "STRING":字符串是否不空,不空為真,空為假

    注意:用于字符串比較時的用到的操作數都應該使用引號

練習

1、寫一個腳本/root/bin/argsnum.sh,接受一個文件路徑作為參數;如果參數個數小于1,則提示用戶“至少應該給一個參數”,并立即退出;如果參數個數不小于1,則顯示第一個參數所指向的文件中的空白行數

2、寫一個腳本/root/bin/hostping.sh,接受一個主機的IPv4地址做為參數,測試是否可連通。如果能ping通,則提示用戶“該IP地址可訪問”;如果不可ping通,則提示用戶“該IP地址不可訪問”

文件測試

存在性測試
    -a FILE:同-e
    -e FILE: 文件存在性測試,存在為真,否則為假;

存在性及類別測試
    -b FILE:是否存在且為塊設備文件;
    -c FILE:是否存在且為字符設備文件;
    -d FILE:是否存在且為目錄文件;
    -f FILE:是否存在且為普通文件;
    -h FILE 或-L FILE:存在且為符號鏈接文件;
    -p FILE:是否存在且為命名管道文件;
    -S FILE:是否存在且為套接字文件;

文件權限測試:判斷的是用戶對文件的實際權限。
    -r FILE:是否存在且可讀
    -w FILE: 是否存在且可寫
    -x FILE: 是否存在且可執行

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

文件大小測試:
        -s FILE: 是否存在且非空;
        -z FILE: 文件是否為空

文件是否打開:
        -t fd: fd表示文件描述符是否已經打開且與某終端相關
        -N FILE:文件自動上一次被讀取之后是否被修改過
        -O FILE:當前有效用戶是否為文件屬主
        -G FILE:當前有效用戶是否為文件屬組


雙目測試:測試兩個文件的inod是否相同,硬鏈接

    FILE1 -ef FILE2 : FILE1與FILE2是否指向同一個設備上的相同inode
    FILE1 -nt FILE2 : FILE1是否新于FILE2;指的是mtime。
    FILE1 -ot FILE2 : FILE1是否舊于FILE2;指的是mtime。

示例:[ -f /bin/cat -a -x /bin/cat ]

組合測試條件

第一種方式:
    COMMAND1 && COMMAND2 并且
    COMMAND1 || COMMAND2 或者
    ! COMMAND 非
    如:[ -e FILE ] && [ -r FILE ]

第二種方式:
    EXPRESSION1 -a EXPRESSION2 并且
    EXPRESSION1 -o EXPRESSION2 或者
    ! EXPRESSION

        用于測試命令 的條件組合。;

    # [ -z “$HOSTNAME” -o $HOSTNAME " == "localhost.localdomain" ] && hostname www.magedu.com
注:可以不加引號

    # [ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab

練習

1、chmod -rw /tmp/file1,編寫腳本/root/bin/per.sh,判斷當前用戶對/tmp/fiile1文件是否不可讀且不可寫

2、編寫腳本/root/bin/nologin.sh和login.sh,實現禁止和充許普通用戶登錄系統。


預習練習

1、添加用戶,用戶存在提示推出,不存在創建用戶和密碼顯示創建成功。

[root@yangyouwei ~]# cat aduser.sh 
#!/bin/bash
if [[ $# -lt 1 ]] ;then
        echo "please give username."
        exit 2
fi
if grep "^$1\>" /etc/passwd &> /dev/null ;then
        echo "The user is exsit"
else
        useradd $1
        echo $1 | passwd --stdin $1 &> /dev/null
        echo -e "userad is finished.\n id $1"
fi

2、通過命令行參數給定兩個數字,顯示較大的數值

$# =2

3、通過命令行參數給定用戶名,判斷id是奇數還是偶數。

4、通過命令行參數給定兩個文本文件名,如果文件不存在則退出,如果都存在則,顯示每個文件的空行數,并顯示空行數多的。


腳本獲取變量的方式

可以自己定義變量 NAME=VLAUE

也可以是使用read 通過用戶鍵盤輸入來定

使用read命令來接受輸入

使用read來把輸入值分配給一個或多個shell變量:
    -p指定要顯示的提示
    -t TIMEOUT
    read從標準輸入中讀取值,給每個單詞分配一個變量
    所有剩余單詞都被分配給最后一個變量
    read -p “Enter a filename:“ FILE

流程控制

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

if 、 while 、 until 都是判定條件 的 $? true or falus
判定前可使用  ! 取反
return可直接設置返回值
return 0
return 1

條件選擇if語句

選擇執行:
注意:if語句可嵌套

單分支
    if 判斷條件:then
        條件為真的分支代碼
    fi

雙分支
    if 判斷條件; then
        條件為真的分支代碼
    else
        條件為假的分支代碼
    fi

多分支
if CONDITION1; then
    if-true
elifCONDITION2; then
    if-ture
elifCONDITION3; then
    if-ture
    ...
else
    all-false
fi
逐條件進行判斷,第一次遇為“真”條件時,執行其分支,而后結束整個if語句


根據命令的退出狀態來執行命令

if ping-c1-W2station1&>/dev/null;then
    echo'Station1isUP' 
elif grep"station1"~/maintenance.txt&>/dev/null
    then
    echo'Station1isundergoingmaintenance‘
else 
    echo'Station1isunexpectedlyDOWN!' 
    exit1
fi

條件判斷:case語句

case 變量引用in
PAT1)
    分支1
    ;;
PAT2)
    分支2
    ;;
...
*) #任意長度任意字符 (最終會匹配的條件)可以不寫這個條件
    默認分支
    ;;
esac

pat)通配模式
case支持glob風格的通配符:
    *: 任意長度任意字符
    ?: 任意單個字符
    []:指定范圍內的任意單個字符
    a|b: a或b

注case 分支語句要用雙分號結尾。

練習 1、寫一個腳本/root/bin/createuser.sh,實現如下功能:使用一個用戶名做為參數,如果指定參數的用戶存在,就顯示其存在,否則添加之;顯示添加的用戶的id號等信息

[root@yangyouwei ~]# cat aduser.sh 
#!/bin/bash
if [[ $# -lt 1 ]] ;then
        echo "please give username."
        exit 2
fi
if grep "^$1\>" /etc/passwd &> /dev/null ;then
        echo "The user is exsit"
else
        useradd $1
        echo $1 | passwd --stdin $1 &> /dev/null
        echo -e "userad is finished.\n id $1"
fi

2、寫一個腳本/root/bin/yesorno.sh,提示用戶輸入yes或no,并判斷用戶輸入的是yes還是no,或是其它信息

#!/bin/bash

read -p "please enter yes or not:" chosice
if [[ $chosice == [yY][Ee][sS] ]] ;then
        echo "you chose yes"
elif [[ $chosice == [nN][oO] ]] ; then
        echo "you chose no"
else
        echo "please check you enter "
fi

還可以用case寫

3、寫一個腳本/root/bin/filetype.sh,判斷用戶輸入文件路徑,顯示其文件類型(普通,目錄,鏈接,其它文件類型)

read -p "please enter file path" files
if [[ -f $files ]] ; then
    echo "file is comment file"
elif [[ -d $files ]] ; then
    echo "file is dir"
elif [[ -h $files ]] ; then
    echo "file is link"
else
    echo "unknow file"
fi

應該先判斷是否是軟連接再判斷其他
也可以用case寫

4、寫一個腳本/root/bin/checkint.sh,判斷用戶輸入的參數是否為正整數

[[ $1 -ge 0 ]]

循環

循環執行
    將某代碼段重復運行多次
    重復運行多少次:
        循環次數事先已知
        循環次數事先未知
有進入條件和退出條件

for, while, until

for循環

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

執行機制:
    依次將列表中的元素賦值給“變量名”; 每次賦值后即執行一次循環體; 直到列表中的元素耗盡,循環結束

for循環

列表生成方式:
    (1) 直接給出列表
    (2) 整數列表:
        (a) {start..end}
        (b) $(seq[start [step]] end)
    (3) 返回列表的命令
        $(COMMAND)
    (4) 使用glob,如:*.sh
    (5) 變量引用;
        $@, $*
    (6)可以使用路徑放在in后面,for直接使用路徑下的文件名稱作為列表

練習:用for實現

1、判斷/var/目錄下所有文件的類型

#!/bin/bash
#author yangyouwei
read -p "give a path:" path_file

for type in `ls -A ${path_file}`;do
        file "${path_file}/${type}"
done
~

2、添加10個用戶user1-user10,密碼同用戶名

#!/bin/bash
#author yangyouwei
for user_name in user{1..10};do
    if grep "^$user_name\>" /etc/passwd &> /dev/null ;then
            echo "The user is exsit"
    else
          useradd $user_name
          echo $user_name | passwd --stdin $user_name &> /dev/null
      echo -e "userad is finished.\n "
    fi  
done

3、/etc/rc.d/rc3.d目錄下分別有多個以K開頭和以S開頭的文件;分別讀取每個文件,以K開頭的文件輸出為文件加stop,以S開頭的文件輸出為文件名加start; “K34filename stop” “S66filename start”

#!/bin/bash
for files in $(ls -A /etc/rc.d/rc3.d) ; do
        if echo $files | grep -i "^k.*$" > /dev/null ; then
                echo "$files stop"
        elif echo $files | grep -i "^s.*$" > /dev/null ; then
                echo "$files start"
        fi
done

4、寫一個腳本,提示輸入正整數n的值,計算1+2+3+…n的總和

#!/bin/bash
read -p "please enter a int_number>0:" sum_num
[[ $sum_num -gt 0 ]] || echo "please enter a int_number>0" ; exit
for add_num in $(seq -s "+" 1 "$sum_num" ) ; do
        echo $add_num | bc
done

5、寫一個腳本,提示請輸入網絡地址,如192.168.0.0,判斷輸入的網段中主機在線狀態

#!/bin/bash
for ipp_add in {0..254} ; do
        if ping -c1 -w0.5 172.18.19.$ipp_add > /dev/null ; then
                echo "$ipp_add is up"
        else
                echo "$ipp_add is down"
        fi
done

6、打印九九乘法表

#!/bin/bash
for mast_num in $(seq 1 9); do
        for sec_num in $(seq 1 $mast_num);do
                echo -n "${mast_num}x${sec_num}=$[ mast_num * sec_num ] "
        done
        echo -e "\n"
done

while循環

while CONDITION; do

循環體
done
CONDITION:循環控制條件;進入循環之前,先做一次判斷;每一次循環之后會再次做判斷;條件為“true”,則執行一次循環;直到條件測試狀態為“false”終止循環

因此:CONDTION一般應該有循環控制變量;而此變量的值會在循環體不斷地被修正

進入條件:CONDITION為true;

退出條件:CONDITION為false

示例

[root@localhost ~]# i=1 ;while [ $i -le 9 ] ;do echo $i; let i++ ; done
1
2
3
4
5
6
7
8
9

死循環

while ture ; do
COMMAND
done

練習:用while實現

1、求100以內所有正整數之和

2、通過ping命令探測172.16.250.1-254范圍內的所有主機的在線狀態,統計在線主機和離線主機各多少。

3、打印九九乘法表

4、利用變量RANDOM生成10個隨機數字,輸出這個10數字,并顯示其中的最大者和最小者

5、打印國際象棋棋盤

until循環

until CONDITION; do
    循環體
done

進入條件:CONDITION 為false
退出條件:CONDITION 為true

循環控制語句continue—停止本次循環的執行或本次循環最后的某段代碼的執行,但是以后的循環繼續執行 默認是1

用于循環體中
continue [N]:提前結束第N層的本輪循環,而直接進入下一輪判斷;最內層為第1層

while CONDTIITON1; do
    CMD1
    ...
    if CONDITION2; then
    continue
    fi
    CMDn
    ...
done

循環控制語句break—跳出循環 ,并停止整個循環。默認是1

用于循環體中

break [N]:提前結束第N層循環,最內層為第1層

    while CONDTIITON1; do
    CMD1
    ...
    if CONDITION2; then
    break
    fi
    CMDn
    ...
done

創建無限循環

while true; do5
    循環體
done

until false; do
    循環體
Done

練習 1、每隔3秒鐘到系統上獲取已經登錄的用戶的信息;如果發現用戶hacker登錄,則將登錄時間和主機記錄于日志/var/log/login.log中,并提示該用戶退出系統。

#!/bin/bash

lg=none

while [[ $lg != hacker ]] ; do
        sleep 3
        logname=$(w | grep -o "^hacker\>")
        tervar=$(  w | sed -n '/^hacker\>/p' | tr -s " "  | cut -d" " -f2 )
        lg=$logname
done
echo "warrning please quit!" > "/dev/$tervar"

2、隨機生成10以內的數字,實現猜字游戲,提示比較大或小,相等則退出。

!/bin/bash

read -p "please enter a nmuber(1-10): " gamnum random1=$[RANDOM%10] while [[ $gamnum -ne $random1 ]] ; do [[ $gamnum -lt $random1 ]] && echo "小了" || echo "大了" read -p "again !please enter a nmuber(1-10): " gamnum done

echo $[$RANDOM%20]

特殊用法

while循環的特殊用法(遍歷文件的每一行):
    while read line; do
    循環體
    done < /PATH/FROM/SOMEFILE
依次讀取/PATH/FROM/SOMEFILE文件中的每一行,且將行賦值給變量line
練習

掃描/etc/passwd文件每一行,如發現GECOS字段為空,則填充用戶名和單位電話為62985600,并提示該用戶的GECOS信息修改成功。

特殊用法 雙小括號方法,即((…))格式,也可以用于算術運算等于 let EXPRESSION

雙小括號方法也可以使bash Shell實現C語言風格的變量操作
    #I=10
    #((I++))

for循環的特殊格式:  help for

for ((控制變量初始化;條件判斷表達式;控制變量的修正表達式))
do
    循環體
done

控制變量初始化:僅在運行到循環代碼段時執行一次

控制變量的修正表達式:每輪循環結束會先進行控制變量修正運算,而后再做條件判斷





for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done
Arithmetic for loop.

Equivalent to
    (( EXP1 ))
    while (( EXP2 )); do
            COMMANDS
            (( EXP3 ))
    done
EXP1, EXP2, and EXP3 are arithmetic expressions.  If any expression is
omitted, it behaves as if it evaluates to 1.

Exit Status:
Returns the status of the last command executed.

select 循環與菜單

select variable in list
    do
        循環體命令
    done

?elect 循環主要用于創建菜單,按數字順序排列的菜單項將顯示在標準錯誤上,并顯示PS3 提示符,等待用戶輸入

用戶輸入菜單列表中的某個數字,執行相應的命令

用戶的輸入 被保存在內置變量REPLY 中。

select 與case

select 是個無限循環,因此要記住用break 命令退出循環,或用exit 命令終止腳本。也可以按ctrl+c 退出循環。

select 經常和case 聯合使用

與for 循環類似,可以省略in list ,此時使用位置參量

函數介紹

函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程。

它與shell程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell程序的一部分。

函數和shell程序比較相似,區別在于:

Shell程序在子Shell中運行
而Shell函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改

定義函數

函數由兩部分組成:函數名和函數體。
語法一:

    function f_name{
    ...函數體...
    }

語法二:

    f_name() {
    ...函數體...
    }

函數使用

函數的定義和使用:
    可在交互式環境下定義函數
    可將函數放在腳本文件中作為它的一部分
    可放在只包含函數的單獨文件中

調用:函數只有被調用才會執行;
    調用:給定函數名
    函數名出現的地方,會被自動替換為函數代碼

函數的生命周期:被調用時創建,返回時終止

函數返回值

函數有兩種返回值:

函數的執行結果返回值:

    (1) 使用echo或printf命令進行輸出
    (2) 函數體中調用命令的輸出結果

函數的退出狀態碼:
    (1) 默認取決于函數中執行的最后一條命令的退出狀態碼
    (2) 自定義退出狀態碼,其格式為:
    return 從函數中返回,用最后狀態命令決定返回值
    return 0 無錯誤返回。
    return 1-255 有錯誤返回

交互式環境下定義和使用函數

示例:
    $dir() {
    > ls-l
    > }


定義該函數后,若在$后面鍵入dir,其顯示結果同ls-l的作用相同。
$dir

該dir函數將一直保留到用戶從系統退出,或執行了如下所示的unset命令:
$ unsetdir

在腳本中定義及使用函數

?函數在使用前必須定義,因此應將函數定義放在腳本開始部分,直至shell首次發現它后才能使用
?調用函數僅使用其函數名即可。
?示例:
    $cat func1
    #!/bin/bash
    # func1
    hello()
    {
    echo "Hello there today's date is `date +%F`"
    }
    echo "now going to the function hello"
    hello
    echo "back from the function

使用函數文件

可以將經常使用的函數存入函數文件,然后將函數文件載入shell。

文件名可任意選取,但最好與相關任務有某種聯系。例如:functions.main

一旦函數文件載入shell,就可以在命令行或腳本中調用函數??梢允褂胹et命令查看所有定義的函數,其輸出列表包括已經載入shell的所有函數。

若要改動函數,首先用unset命令從shell中刪除函數。改動完畢后,再重新載入此文件。

創建函數文件

函數文件示例:

    $cat functions.main
    #!/bin/bash
    #functions.main
    findit()
    {
        if [ $# -lt1 ] ; then
            echo "Usage:finditfile"
            return 1
        fi
    find / -name $1 –print
    }

載入函數

函數文件已創建好后,要將它載入shell

定位函數文件并載入shell的格式:

    . filename 或source filename
注意:此即<點> <空格> <文件名>
    這里的文件名要帶正確路徑

示例:上例中的函數,可使用如下命令:
    $ . functions.main

檢查載入函數

使用set命令檢查函數是否已載入。set命令將在shell中顯示所有的載入函數。

示例:
    $set
    findit=()
    {
        if [ $# -lt1 ]; then
            echo "usage :finditfile";
            return 1
        fi
    find / -name $1 -print
    }

執行shell函數

要執行函數,簡單地鍵入函數名即可:
示例:
    $finditgroups
    /usr/bin/groups
    /usr/local/backups/groups.bak

刪除shell函數

現在對函數做一些改動。首先刪除函數,使其對shell不可用。使用unset命令完成此功能.

命令格式為:
unset function_name

實例:
    $unset findit
    再鍵入set命令,函數將不再顯示

函數參數

函數可以接受參數:

傳遞參數給函數:調用函數時,在函數名后面以空白分隔給定參數列表即可;例如“testfuncarg1 arg2 …”

在函數體中當中,可使用$1, $2, …調用這些參數;還可以使用$@, $*, $#等特殊變量

函數變量

變量作用域:
    環境變量:當前shell和子shell有效
    本地變量:只在當前shell進程有效,為執行腳本會啟動專用子shell進程;因此,本地變量的作用范圍是當前shell腳本程序文件,包括腳本中的函數。
    局部變量:函數的生命周期;函數結束時變量被自動銷毀

注意:如果函數中有局部變量,如果其名稱同本地變量,使用局部變量。

在函數中定義局部變量的方法
    local NAME=VALUE

函數遞歸實例

函數遞歸:
    函數直接或間接調用自身
    注意遞歸層數

遞歸實例:
    階乘是基斯頓·卡曼于1808 年發明的運算符號,是數學術語
    一個正整數的階乘(factorial)是所有小于及等于該數的正整數的積,并且有0的階乘為1。自然數n的階乘寫作n!。
    n!=1×2×3×...×n。
    階乘亦可以遞歸方式定義:0!=1,n!=(n-1)!×n。
    n!=n(n-1)(n-2)...1
    n(n-1)! = n(n-1)(n-2)!

函數遞歸示例

    示例:fact.sh

        #!/bin/bash
        #
        fact() {
        if [ $1 -eq0 -o $1 -eq1 ]; then
        echo 1
        else
        echo $[$1*$(fact $[$1-1])]
        fi
        }
        fact 5

練習

1、寫一個服務腳本/root/bin/testsrv.sh,完成如下要求

(1) 腳本可接受參數:start, stop, restart, status (2) 如果參數非此四者之一,提示使用格式后報錯退出 (3) 如是start:則創建/var/lock/subsys/SCRIPTNAME, 并顯示“啟動成功” 考慮:如果事先已經啟動過一次,該如何處理? (4) 如是stop:則刪除/var/lock/subsys/SCRIPTNAME, 并顯示“停止完成” 考慮:如果事先已然停止過了,該如何處理? (5) 如是restart,則先stop, 再start 考慮:如果本來沒有start,如何處理? (6) 如是status, 則如果/var/lock/subsys/SCRIPTNAME文件存在,則顯示“SCRIPTNAMEis running…” 如果/var/lock/subsys/SCRIPTNAME文件不存在,則顯示“SCRIPTNAME is stopped…” 其中:SCRIPT_NAME為當前腳本名 (7)在所有模式下禁止啟動該服務,可用chkconfig和service命令管理

2、編寫一個腳本/root/bin/copycmd.sh

(1) 提示用戶輸入一個可執行命令名稱; (2) 獲取此命令所依賴到的所有庫文件列表 (3) 復制命令至某目標目錄(例如/mnt/sysroot)下的對應路徑下; 如:/bin/bash ==> /mnt/sysroot/bin/bash /usr/bin/passwd==> /mnt/sysroot/usr/bin/passwd (4) 復制此命令依賴到的所有庫文件至目標目錄下的對應路徑下: 如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2 (5)每次復制完成一個命令后,不要退出,而是提示用戶鍵入新的要復制的命令,并重復完成上述功能;直到用戶輸入quit退出

3、寫一個函數實現兩個數字做為參數,返回最大值

4、寫一個函數實現數字的加減乘除運算,例如輸入1 + 2,,將得出正確結果

5、斐波那契數列又稱黃金分割數列,因數學家列昂納多·斐波那契以兔子繁殖為例子而引入,故又稱為“兔子數列”,指的是這樣一個數列:0、1、1、2、3、5、8、13、21、34、……,斐波納契數列以如下被以遞歸的方法定義:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2) 寫一個函數,求n階斐波那契數列

6、漢諾塔(又稱河內塔)問題是源于印度一個古老傳說。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。并且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。 利用函數,實現N片盤的漢諾塔的移動步驟

函數可以定義到交互式環境下。類似于變量

可以單獨執行,使用set可以查看到。

優先級 : 別名 函數 內部命令 外部命令

函數放在調用函數的代碼之前

return 退出函數 制定退出狀態碼 0 1-255

在函數中使用exit的話直接退出腳本

腳本中調用函數,而函數存為文本格式放在其他地方。 在腳本中調用,用source 或點 調用該函數

lld commadn

原創文章,作者:yyw,如若轉載,請注明出處:http://www.www58058.com/37884

(0)
yywyyw
上一篇 2016-08-21
下一篇 2016-08-21

相關推薦

  • find文件查找

    文件查找      在文件系統上查找符合條件的文件:      實現工具:locate,find locate:      構建于實現構建好的索引庫:/var/lib/mlocate/mlocate.db   &nbsp…

    Linux干貨 2016-08-22
  • Linux 系統架構

    1.內核     1.1.組成部分         1.1.1.系統調用接口         1.1.2.進程管理 &n…

    Linux干貨 2016-06-04
  • 正則表達式

    1、復制/etc/skel目錄為/home/tuser1,要求/home/tuser1及其內部文件的屬組和其他用戶都沒有任何訪問權限 [root@localhost ~]#? mkdir /home/tuser1======>創建/home/tuser1目錄 [root@localhost ~]#? ?cp? -a? /etc/skel/? ?/hom…

    2017-10-10
  • N25第一周博客作業

    本文主要闡述了以下幾個知識點: l  計算機的組成及功能; l  介紹各不同發行版的linux及其聯系; l  描述linux的哲學思想,及其本人對其個人理解; l  介紹一些linux基本命令及相關選項; l  如何在linux上使用幫助命令; l  羅列出發行版linux上的一些基礎目錄及其功用…

    Linux干貨 2016-11-29
  • lvs-dr 原理與配置

    Lvs-dr 原理與配置 目錄     1、ARP介紹     2、DR模式數據包請求響應流程     3、配置過程    4、總結 1、ARP協議介紹 功能:局域網內,根據IP地址獲取MAC地址。 互聯網上面…

    Linux干貨 2015-09-16
  • 馬哥教育21期網絡班—第8周課程+練習—-成長進行時–不退步

    1、請描述網橋、集線器、二層交換機、三層交換機、路由器的功能、使用場景與區別。 網橋:一種網路裝置,負責網路橋接,將網絡的多個網段在數據鏈路層連接起來。 集線器:將多條以太網線或光纖集合連接在同一段物理介質下的裝置。工作在物理層。 二層交換機:工作在數據鏈路層,交換機內部的CPU會在每個端口成功連接時,通過將MAC地址和端口對應,形成一張MAC表。在今后的通…

    Linux干貨 2016-08-24

評論列表(1條)

  • 馬哥教育
    馬哥教育 2016-08-22 09:33

    文章結構層次清晰,分類明確,課堂練習應該主動去找資料完成,或者請教其它同學盡量完成,而不要留空。

欧美性久久久久