1. 程式人生 > >【Shell編程】Shell基本語法

【Shell編程】Shell基本語法

執行命令 find 命令 引號 替換 unset 腳本 comm 程序 logged in

Shell 語法

??Shell程序設計作為一種腳本語言,在Linux系統中有廣泛的應用,本文記錄了關於Shell程序設計的基礎語法知識和常用命令,方便查詢,熟練使用shell也需要經常實踐,這對於完成一些較簡單的編程任務很有幫助。


(1)變量

??在shell裏,使用變量之前並不需要事先做出聲明,可以通過使用直接創建。默認情況下,所有的變量都被當做字符串進行存儲,變量名區分大小寫。變量名之前加一個$符號可以訪問它的內容。

??輸入和輸出:可以使用echo命令將一個變量的內容輸出到終端,使用read命令(停下來等待用戶輸入)可以將用戶的輸入賦值給一個變量。

??單引號和雙引號:shell腳本的文件參數以空白符進行分割,如果一個參數包含空白符,就需要放到引號中(防止被解讀為不同的參數)。雙引號通常可以用來做字符串定界符。而像$salutation這樣的變量放到引號中,有這幾種情況:不加引號引用變量的值,放到雙引號中也引用變量的值,放到單引號中就不引用,而是看作一個字符串。

$ salutation=Hello
$ echo $salutation  # 輸出 Hello
$ salutation="Yes Sir"
$ echo $salutation  # 輸出 Yes Sir
$ salutation=7+2
$ echo $salutation  # 輸出 7+2 (作為字符串看待)
# 使用引號:
$ myvar="Hello World"
$ echo $myvar  # 輸出 Hello World
$ echo "$myvar" # 輸出 Hello World
$ echo '$myvar' # 輸出 $myvar
$ echo \$myvar  # 轉義字符,輸出 $myvar

??除了自定義變量外,shell還有一些環境變量和參數變量。

??所謂環境變量,就是指腳本開始執行時,一些變量會根據當前環境設置中的值進行初始化。使用export命令可以設置環境變量。下面的示例展示了常用的環境變量:

$ echo $HOME # 當前用戶的home目錄,在我的機器上輸出 /home/gzshan
$ echo $PATH # 以冒號分割的用來搜索命令的目錄列表
$ echo $0 # shell腳本的名字
$ echo $# # 傳遞給腳本的參數數量,默認是0
$ echo $$ # shell腳本的進程號

??所謂參數變量,就是指執行腳本程序如果帶有參數,一些額外的變量會被創建,如下所示:

如:運行一個腳本的命令:./first.sh foo bar baz
$1 $2 $3 #分別指的就是參數foo,bar和baz
$# # 此時為3
[email protected] # 用於列出所有的參數變量,($* 也有相同功能,但受空白符影響,一般用[email protected])

(2)條件

??條件判斷是程序設計語言控制結構的基礎,程序需要對條件進行測試和判斷,從而執行不同的命令,完成不同的功能和任務。在shell腳本中,完成條件測試有兩個命令:test 和 布爾判斷命令 [ ]

  • test 命令

    ??以下的例子展示了test命令的用法,註意如果then和if放在同一行則需要一個分號。

    if test -f fred.c # 如果then寫在這一行需要一個分號,這條語句檢查一個文件是否存在
    then
      echo "Hello"
    fi
  • 布爾判斷命令[ ]

    ??把 [ 當作一條命令看起來有些奇怪,但是會使得程序變得簡單。註意:使用 [ 時,後面必須有空格,還應該使用 ] 來結尾。

    if [ -f fred.c ] ; then # 同樣必須有分號,必須有空格
    echo "test"
    fi

    ??當使用以上兩個命令時,可以使用的條件類型歸結為三類,用下表歸納。

    第一類:字符串的比較 比較結果
    string1 = string2 兩個字符串相同結果為真
    string1 != string2 兩個字符串不相同結果為真
    -n string 字符串不為空則結果為真
    -z string 字符串為null則結果為真
    第二類:算術比較 比較結果
    expression1 -eq expression2 兩個表達式結果相等為真
    expression1 -nq expression2 兩個表達式結果不相等為真
    expression1 -gt expression2 expression1 大於 expression2 為真
    expression1 -ge expression2 expression1 大於等於 expression2 為真
    expression1 -lt expression2 expression1 小於 expression2 為真
    expression1 -le expression2 expression1 小於等於 expression2 為真
    !expression expression為假則結果為真
    第三類:與文件有關的條件測試 比較結果
    -d file 文件是一個目錄,則結果為真
    -f file 文件是一個普通文件,則結果為真
    -g file 文件set-group-id 被設置,則結果為真
    -u file 文件set-user-id 被設置,則結果為真
    -s file 文件大小不為0,則結果為真
    -r / -w / -x file 文件可讀 / 可寫 / 可執行,則結果為真
    -a file 文件存在,則結果為為真
    -c file 文件存在並且為字符特殊文件,則結果為真

(3)控制結構

  • if 語句和elif 語句

    ??if 語句比較簡單,下面的例子展示了if 語句和elif語句的用法。

    #!/bin/sh
    echo "Is it morning? Please answer yes or no"
    read timeofday
    if [ $timeofday = "yes" ] # if語句,後面跟條件測試
    then
      echo "Good morning"
    elif [ $timeofday = "no" ]; then # then放同一行,加分號
      echo "Good afternoon"
    else
      echo "Sorry,$timeofday not recognized. Enter yes or no"
      exit 1
    fi
    exit 0
  • for 語句

    ??for語句用以循環處理一組值,這組值可以是任意字符串的集合,也可以是其他命令的輸出結果,下面用兩個例子展示其用法。

    #!/bin/sh
    for foo in bar fud 43 # 循環處理一組字符串
    do
      echo $foo
    done
    exit 0
    #!/bin/sh
    for file in $(ls f*.sh); do # 使用通配符擴展for循環,列出所有以f開頭,擴展名為.sh的腳本文件
      # 說明:$()是執行該命令得到的輸出結果
      lpr $file # lpr是打印命令
    done
    exit 0
  • while 語句

    ??如果事先不知道循環次數,for循環不太好使用的情況下,可以使用while循環。whie語句的do和done之間的語句反復執行,直到條件不再真為止。

    #!/bin/sh
    echo "Enter password"
    read trythis
    while [ $trythis != "secret" ]; do # 反復執行,直到條件不再真為止
      echo "sorry,try again"
      read trythis
    done
    exit 0
  • until 語句

    ??until語句與while循環類似,所不同的是,until反復執行循環直到條件為真。

    #!/bin/sh
    until who | grep "$1" > /dev/null
    do
      sleep 60
    done
    echo -e '\a'
    echo "$1 has just logged in"
    exit 0
  • case 語句

    ??case語句相比其他結構較為復雜,用下面的例子來介紹他的用法,需要特別註意的是:case按順序查找第一個匹配的模式,而不是最佳匹配。

    #!/bin/sh
    echo "Is it morning? Please answer yes or no"
    read timeofday
    case "$timeofday" in
      yes | y | Yes | YES )   echo "good morning";; # 註意每個模式末尾是兩個分號
      n* | N* ) echo "good afternoon";;
      * ) echo "sorry,answer not recognized";;
    esac
    exit 0
  • 命令列表

    ??有時需要將多個命令連接成一個序列,shell提供了命令列表,也就是and列表和or列表,類似於其他程序設計語言,它們也采用的是短路求值。

    if [ -f file1 ] && echo "hello" && [ -f file2 ] && echo "here";then
      echo "in if"
    fi
    
    if [ -f file ] || echo "hello" || echo "here" ; then
      echo "in if"
    fi
  • 語句塊

    ??在某些只允許使用單個語句的地方,要想使用多條語句,可以放到花括號中構建一個語句塊。

    get_confirm && { # and列表中使用語句塊
        echo "hello"
        cat test.txt
    }

(4)函數

??要在shell腳本中使用函數,只需要寫出函數名,然後跟一對括號,再把函數中的語句放在一對花括號中,並且把函數定義放到函數調用之前。函數體內可以用local關鍵字聲明局部變量。

??函數參數:當一個函數被調用時,腳本程序的位置參數($*,[email protected],$#,$1,$2等等)會被替換為函數的參數,當函數執行完畢後,這些參數會恢復為先前的值。下面的例子展示了函數的用法。

#!/bin/sh
yes_or_no(){    # 函數定義
    echo "Is your name $* ?"
    while true
    do
        echo -n "Enter yes or no: "
        read x
        case "$x" in
            y | yes ) return 0;;
            n | no ) return 1;;
            * ) echo "Answer yes or no"
        esac
    done
}
# 以下是主程序部分:
echo  "Original parameters are $*"
if yes_or_no "$1"
then
    echo "Hi $1,nice name"
else
    echo "Never mind"
fi
exit 0

(5)命令

  • break命令和continue命令

    ??這兩條命令比較簡單,break應用於跳出for、while、until循環,continue命令用於跳過當前這一次循環。

  • : 命令

    ??:相當於一個空命令,或者相當於true的別名,例如 while : 就代表一個死循環

  • . 命令

    ??. 命令用於在當前shell中執行命令,如前面用到的./first.sh

  • echo 命令和read命令

    ??echo命令用於輸出結尾帶有換行符的字符串,前面已多次用到,它還有兩個常用的參數,如下所示。現在最新版本的shell常常用printf來代替echo。read命令用於將用戶的輸入賦給一個變量。

    $ echo -n "string to output" # 去掉換行符
    $ echo -e "string to output\c" # -e確保啟用了反斜杠轉移字符
  • eval 命令

    ??eval命令用於對參數進行求值,它是shell的內置命令,通常不會以單獨命令的形式存在,以下的例子展示eval的用法。eval命令就像一個額外的$,它給出一個變量的值的值。

    foo=10
    x=foo
    y='$'$x # $x就是foo
    echo $y # 輸出的是$foo
    eval z='$'$x
    echo $z # 輸出的是10
  • exec 命令

    ??exec命令時執行一個shell程序,也就是將當前shell替換為一個不同的程序,當前腳本程序exec命令之後的代碼都不會執行。還有一種用法是,它可以用於修改文件描述符,比較少見。

  • exit n 命令

    ??exit命令使腳本程序以退出碼n結束運行。退出碼0表示成功,1~125是錯誤代碼,126代表文件不可執行,127代表命令未找到。

  • export 命令

    ??export命令將自己的參數建為一個環境變量,而這個環境變量可以被當前程序調用的其他腳本或者程序看到。

    # 以下是export2.sh
    #!/bin/sh
    echo $foo
    echo $bar
    
    # 以下是export1.sh
    #!/bin/sh
    foo = "foo foo foo"
    export bar = "bar bar bar"
    export2 # 調用腳本2
    # 此時如果執行腳本1,我們會看到輸出bar的值,因為被聲明為環境變量,在腳本2中可見,而foo不會被輸出
  • expr 命令和$( )$(( ))

    ??expr命令將它的參數作為表達式來求值,最常見用法就是進行數學運算。$(command)的結果就是執行command的 輸出結果,和兩個反引號的功能相同。

    ??對於表達式求值,一種更新的方法是使用$(( ))命令,將求值的表達式放到$(( ))中,可以很簡單的完成數學運算,常見的表達式求值有:加+、減-、乘*、除/、取模%、與&、或|、等於=、不等!=、大於>、小於<等等。

    ??以下的例子展示這幾個命令的用法:

    $ x=1
    $ x=`expr $x + 1` # 反引號是x取值為xpr $x + 1的結果
    $ x=$(expr $x + 1)  # $(command)具有和反引號相同的功能
    $ x=$(($x + 1)) # 一種更新的方法是使用$(( ))命令,代替expr命令
  • set 命令和unset 命令

    ??set命令的作用是為shell設置參數變量。unset命令的作用是從環境中刪除變量或者函數。

    #!/bin/sh
    echo the date is $(date)
    set $(date) # 將date設置為參數變量
    echo the month is $2 # 輸出第二個位置參數,月份
    foo = 0
    unset foo # 刪除變量foo
    exit 0
  • shift 命令

    ??shift命令把所有的參數變量左移一個位置,使得$2變為$1$3變為$2,依次類推,原來的$1被丟棄,而$0仍將保持不變。如果指定數值參數,可以左移相應的次數。該命令的一個主要作用是用來掃描參數,如下所示。

    #!/bin/sh
    while [ "$1" != "" ]; do
      echo "$1"
      shift
    done
    exit 0
  • trap 命令

    ??trap命令用於指定在接收到相應的信號後將要采取的行動,trap命令的常見形式如下:

    trap command signal
    # 第一個參數是接收到信號時將要采取的行動
    # 第二個參數是要處理的信號名
  • find 命令

    ??find命令時一個很有用的命令,主要用於在系統中找文件,也就是文件搜索。其基本語法格式如下:

    find [path] [options] [tests] [actions]

    ??其中,path部分很好理解,是要搜索的路徑,可以是相對路徑,也可以是絕對路徑,也可以是多個路徑。

    ??options部分是一些可用選項,主要有-depth(在查看目錄本身之前先搜索目錄的內容),-follow(跟隨符號鏈接),-maxdepths N (最多搜索N層目錄),-mount(或者-xdev,指不搜索其他文件系統中的目錄)

    ??tests是測試部分,每種測試的返回結果是true或false,主要有以下幾種:-atime N (文件在N天前被最後訪問過),-mtime N (文件在N天前被最後修改過),-name(文件名匹配),-newer otherfile(文件比otherfile要新),-type c(文件類型是c、d、f,分別對應特殊字符文件、目錄、普通文件),-user username(文件的擁有者是username)。

    ??註意:tests測試可以組合使用,有三個組合命令:-and,-or,-not

    ??actions部分是匹配之後要執行的動作,比如:-exec command(執行一條命令,最常見),-print(打印)等等。

    $ find / -name test -print
    $ find / -mount -name test -print
    $ find . -newer while2 -print
    $ find . -newer while2 -type f -print
  • grep 命令

    ??grep命令是通用正則表達式解析器,通俗的說,find命令在系統中找文件,grep命令在文件中找字符串,一種常用的做法是將grep作為傳遞給-exec的一條命令。

    ??grep命令的基本語法如下:

    grep [options] PATTERN [FILES]

    ??options是一些主要選項,常用的有-c (輸出匹配行的數目)、-E(啟用擴展表達式)、-h(取消每個輸出行的普通前綴)、-i(忽略大小寫)、-l(只列出包含匹配行的文件名)、-v(取反,搜索不匹配行)。

    ??PATTERN主要是一些匹配模式,常用正則表達式來表示,關於正則表達式的內容參照另一篇博文正則表達式。

    grep in words.txt
    grep -c in words.txt words2.txt
    grep -c -v in words.txt words2.txt
    grep "e$" words2.txt
    grep "a[[:blank:]]" words2.txt
    grep -E [a-z]\{10\} words2.txt

總結

??Shell程序設計作為一種腳本語言,在Linux系統中有廣泛的應用,本文記錄了關於Shell程序設計的基礎語法知識和常用命令,方便查詢,熟練使用shell也需要經常實踐,這對於完成一些較簡單的編程任務很有幫助。另外,shell程序在Linux中海油一個可視化工具:dialog,由於不常使用,這裏不再介紹。

【Shell編程】Shell基本語法