天天看點

Shell 程式設計入門

Shell 程式設計入門

Shell 是一個用 C 語言編寫的程式,它是使用者使用 Linux 的橋梁。它是作業系統最外層的接口,

負責直接面向使用者互動并提供核心服務。

一、變量

1、 定義

Shell

定義變量時,變量名不加美元符号,如:

content="hello world!"
           

變量名的命名須遵循如下規則:

  • 命名隻能使用英文字母,數字和下劃線,首個字元不能以數字開頭。
  • 中間不能有空格,可以使用下劃線 _。
  • 不能使用标點符号。
  • 不能使用bash裡的關鍵字(可用help指令檢視保留關鍵字)。

2、 使用

使用一個定義過的變量,隻要在變量名前面加美元符号即可,如:

content="hello world!"
echo $content
echo ${content}
           

變量名外面的花括号是可選的,加不加都行,加花括号是為了幫助解釋器識别變量的邊界。

content="hello world!"
echo "name:${content}!!!"
           

推薦給所有變量加上花括号,這是個好的程式設計習慣。

已定義的變量,可以被重新定義,如:

content="hello world!"
echo $content
content="hello shell!"
echo $content
           

3、 隻讀變量

使用

readonly

指令可以将變量定義為隻讀變量,隻讀變量的值不能被改變。

content="hello world!"
readonly content
content="hello shell!"
           

運作腳本,結果如下:

/bin/sh: NAME: This variable is read only.
           

4、 局部變量

Shell

中預設定義的變量是全局變量,可以使用

global

進行顯式聲明,其作用域從被定義的地方開始,一直到腳本結束或者被删除的地方。

local

可以定義局部變量,在函數内部使用。

#!/bin/bash

name="global variable"
function func(){
    local name="local variable"
    echo $name
}

func
echo $name

# local variable
# global variable
           

5、 變量類型

shell

中會同時存在三種變量:

  • 局部變量;
  • 環境變量;
  • shell 變量。

二、字元串

字元串是最常用最有用的資料類型,字元串可以用單引号,也可以用雙引号,也可以不用引号。

1、單引号

str='this is a string'
echo '$str'

# $str
           

單引号字元串的限制:

  • 單引号裡的任何字元都會原樣輸出,單引号字元串中的變量是無效的;
  • 單引号字串中不能出現單獨一個的單引号(對單引号使用轉義符後也不行),但可成對出現,作為字元串拼接使用。

2、 雙引号

name="shell"
str="Hello, I know you are \"$name\"! \n"
echo $str

# Hello, I know you are "shell"!
           

雙引号的優點:

  • 雙引号裡可以有變量;
  • 雙引号裡可以出現轉義字元。

3、 字元串長度

string="abcd"
echo ${#string} 

# 4
           

4、 提取子字元串

以下執行個體從字元串第 2 個字元開始截取 4 個字元:

string="huawei is a great compan"
echo ${string:1:4} 

# uawe
           

5、 查找子字元串

查找字元 i 或 o 的位置(哪個字母先出現就計算哪個):

string="huawei is a great compan"
echo `expr index "$string" io`  

# 6
           

注意: 以上腳本中 ` 是反引号,而不是單引号 ',不要看錯了哦。

三、數組

Shell

隻支援一維數組(不支援多元數組),并且沒有限定數組的大小。類似于

C

語言,數組元素的下标由 0 開始編号。

1、 定義數組

shell

中,用括号來表示數組,數組元素用"空格"符号分割開。

array=("value0" "value1" "value2" "value3")
           

還可以單獨定義數組的各個元素:

array[0]="value0"
array[1]="value1"
array[n]="valuen"
           

2、 讀取數組

讀取數組元素值的一般格式是:

value=${array_name[n]}
           

@

符号可以擷取數組中的所有元素,例如:

echo ${array_name[@]}

# value0 value1 value2 value3
           

3、 擷取長度

擷取數組長度的方法與擷取字元串長度的方法相同,例如:

# 取得數組元素的個數
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得數組單個元素的長度
lengthn=${#array_name[n]}
           

注意:數組不可以進行切割,錯誤用法

${array[1:2]}

四、流程控制

1、 if 判斷

文法示例判斷兩個變量是否相等。

#!/bin/bash

a=10
b=20
if [ $a == $b ]; then
    echo "a 等于 b"
elif [ $a -gt $b ]; then
    echo "a 大于 b"
elif [ $a -lt $b ]; then
    echo "a 小于 b"
else
    echo "沒有符合的條件"
fi

# a 小于 b
           

不能使用

if [ $a > $b ]

,正确的方式是

if (( $a > $b ))

2、 for 循環

for

循環即執行一次所有指令,空格進行元素分割,使用變量名擷取清單中的目前取值。

示例,順序輸出目前清單中的數字:

#!/bin/bash

for loop in 1 2 3; do
    echo "The value is: $loop"
done

#The value is: 1
#The value is: 2
#The value is: 3
           

循環字元串内容:

#!/bin/bash

for str in This is a string; do
    echo $str
done

# This
# is
# a
# string
           

循環數組中元素:

#!/bin/bash

array=("value0" "value1" "value2" "value3")
# array[*]與array[@]兩者皆可
for loop in ${array[*]}; do    
    echo ${loop}
done

# value0
# value1
# value2
# value3
           

3、 while 循環

while

循環用于不斷執行一系列指令,也用于從輸入檔案中讀取資料。

以下是一個基本的

while

循環,測試條件是:如果 int 小于等于 5,那麼條件傳回真。int 從 1 開始,每次循環處理時,int 加 1。運作上述腳本,傳回數字 1 到 5,然後終止。

int=1
while [ $int -le 5 ]; do
    echo $int
    let "int++"
done
           

無限循環

# 方式一
while :
do
    command
done

# 方式二
while true
do
    command
done
           

4、 break 終止

在循環語句中,可以使用

break

指令,允許跳出所有循環(終止執行後面的所有循環)。

#!/bin/bash

while :; do
    echo -n "輸入 1 到 5 之間的數字:"
    read aNum
    case $aNum in
    1 | 2 | 3 | 4 | 5)
        echo "你輸入的數字為 $aNum!"
        ;;
    *)
        echo "你輸入的數字不是 1 到 5 之間的! 遊戲結束"
        break
        ;;
    esac
done
           

5、 continue 繼續

continue指令與break指令類似,隻有一點差别,它不會跳出所有循環,僅僅跳出目前循環。

#!/bin/bash

while :; do
    echo -n "輸入 1 到 5 之間的數字: "
    read aNum
    case $aNum in
    1 | 2 | 3 | 4 | 5)
        echo "你輸入的數字為 $aNum!"
        ;;
    *)
        echo "你輸入的數字不是 1 到 5 之間的!"
        continue
        echo "遊戲結束"
        ;;
    esac
done
           

運作代碼發現,當輸入大于5的數字時,該例中的循環不會結束,語句 echo "遊戲結束" 永遠不會被執行。

五、函數

1、 函數定義

Shell

中可以使用者定義函數,然後在

shell

腳本中可以随便調用。

下面的例子定義了一個函數并進行調用:

#!/bin/bash

function demo(){
     echo "這是我的第一個 shell 函數!"
}
echo "-----函數開始執行-----"
demo
echo "-----函數執行完畢-----"
           

可以帶

function fun()

定義,也可以直接

fun()

定義,不帶任何參數。

參數傳回,可以顯示加:

return

傳回,如果不加,将以最後一條指令運作結果,作為傳回值。

return

後跟數值n(0-255)。

函數腳本執行結果:

-----函數開始執行-----
這是我的第一個 shell 函數!
-----函數執行完畢-----
           

2、 函數參數

shell

中,調用函數時可以向其傳遞參數。在函數體内部,通過 $n 的形式來擷取參數的值,例如,$1 表示第一個參數,$2 表示第二個參數...

帶參數的函數示例:

#!/bin/bash

function funWithParam(){
     echo "第一個參數為 $1 !"
     echo "第十個參數為 $10 !"
     echo "第十個參數為 ${10} !"
     echo "第十一個參數為 ${11} !"
     echo "參數總數有 $# 個!"
     echo "作為一個字元串輸出所有參數 $* !"
}
funWithParam 11 22 3 4 5 6 7 8 9 34 73
           

輸出結果:

第一個參數為 11 !
第十個參數為 110 !
第十個參數為 34 !
第十一個參數為 73 !
參數總數有 11 個!
作為一個字元串輸出所有參數 11 22 3 4 5 6 7 8 9 34 73 !
           

參數擷取時

$n

${n}

還是有差別的,特别是第二行的列印。

$10

不能擷取第十個參數,擷取第十個參數需要

${10}

。當n>=10時,需要使用

${n}

來擷取參數。

另外,還有幾個特殊字元用來處理參數:

$#	傳遞到腳本或函數的參數個數
$*	以一個單字元串顯示所有向腳本傳遞的參數
$$	腳本運作的目前程序ID号
$!	背景運作的最後一個程序的ID号
$@	與$*相同,但是使用時加引号,并在引号中傳回每個參數。
$-	顯示Shell使用的目前選項,與set指令功能相同。
$?	顯示最後指令的退出狀态。0表示沒有錯誤,其他任何值表明有錯誤。
           

6、運算符

1、算術運算符

下表列出了常用的算術運算符。

+	加法	
-	減法	
*	乘法	
/	除法
%	取餘	
=	指派	
==	相等
!=	不相等
           

注意:條件表達式要放在方括号之間,并且要有空格,例如:

[$a==$b]

是錯誤的,必須寫成

[ $a == $b ]

使用示例如下:

#!/bin/bash

a=10
b=20

val=$(expr $a + $b)
echo "a + b : $val"

val=$(expr $a - $b)
echo "a - b : $val"

val=$(expr $a \* $b)
echo "a * b : $val"

val=$(expr $b / $a)
echo "b / a : $val"

val=$(expr $b % $a)
echo "b % a : $val"

if [ $a == $b ]; then
    echo "a 等于 b"
fi
if [ $a != $b ]; then
    echo "a 不等于 b"
fi
           

還可以使用下面的運算符替換,結果都一緻:

#!/bin/bash

a=10
b=20

val=$(expr $a + $b)
echo "a + b : $val"

var=$(($a + $b))
echo "a + b : $val"

var=$[$a + $b]
echo "a + b : $val"
           

注意:

  • 乘号(*)前邊必須加反斜杠()才能實作乘法運算;
  • $((表達式)) 此處表達式中的 "*" 不需要轉義符号 ""。

2、關系運算符

關系運算符隻支援數字,不支援字元串,除非字元串的值是數字。

下表列出了常用的關系運算符。

-eq	檢測兩個數是否相等,相等傳回 true。
-ne	檢測兩個數是否不相等,不相等傳回 true。	
-gt	檢測左邊的數是否大于右邊的,如果是,則傳回 true。	
-lt	檢測左邊的數是否小于右邊的,如果是,則傳回 true。	
-ge	檢測左邊的數是否大于等于右邊的,如果是,則傳回 true。	
-le	檢測左邊的數是否小于等于右邊的,如果是,則傳回 true。
           
#!/bin/bash

a=10
b=20

if [ $a -eq $b ]; then
    echo "$a -eq $b : a 等于 b"
else
    echo "$a -eq $b: a 不等于 b"
fi
if [ $a -gt $b ]; then
    echo "$a -gt $b: a 大于 b"
else
    echo "$a -gt $b: a 不大于 b"
fi
           

運算符可以使用"=="、"! ="、">"替換:

#!/bin/bash

a=10
b=20

if [ $a == $b ]; then
    echo "$a -eq $b : a 等于 b"
else
    echo "$a -eq $b: a 不等于 b"
fi
if (($a > $b)); then
    echo "$a -gt $b: a 大于 b"
else
    echo "$a -gt $b: a 不大于 b"
fi
           

注意:">"、"> =" 、"<" 、"< =" 不能使用"[]"。

3、邏輯運算符

常用的邏輯運算符。

&&	邏輯的 AND	[[ $a -lt 100 && $b -gt 100 ]] 傳回 false
||	邏輯的 OR	[[ $a -lt 100 || $b -gt 100 ]] 傳回 true
           
#!/bin/bash

a=10
b=20

if [[ $a -lt 100 && $b -gt 100 ]]; then
    echo "傳回 true"
else
    echo "傳回 false"
fi

if [[ $a -lt 100 || $b -gt 100 ]]; then
    echo "傳回 true"
else
    echo "傳回 false"
fi
           

執行腳本,輸出結果如下所示:

傳回 false
傳回 true
           

4、字元串運算符

下表列出了常用的字元串運算符。

=	檢測兩個字元串是否相等,相等傳回 true。	
!=	檢測兩個字元串是否不相等,不相等傳回 true。	
-z	檢測字元串長度是否為0,為0傳回 true。	
-n	檢測字元串長度是否不為 0,不為 0 傳回 true。
$	檢測字元串是否為空,不為空傳回 true。	
           

字元串運算符執行個體如下:

#!/bin/bash

if [ -z $a ]
then
   echo "-z $a : 字元串長度為 0"
else
   echo "-z $a : 字元串長度不為 0"
fi
if [ -n "$a" ]
then
   echo "-n $a : 字元串長度不為 0"
else
   echo "-n $a : 字元串長度為 0"
fi
if [ $a ]
then
   echo "$a : 字元串不為空"
else
   echo "$a : 字元串為空"
fi
           

5、檔案測試運算符

檔案測試運算符用于檢測 Unix 檔案的各種屬性。

-b file	檢測檔案是否是塊裝置檔案。	
-c file	檢測檔案是否是字元裝置檔案。	
-d file	檢測檔案是否是目錄。
-f file	檢測檔案是否是普通檔案。
-g file	檢測檔案是否設定了 SGID 位。
-k file	檢測檔案是否設定了粘着位(Sticky Bit)。
-p file	檢測檔案是否是有名管道。
-u file	檢測檔案是否設定了 SUID 位。
-r file	檢測檔案是否可讀。
-w file	檢測檔案是否可寫。
-x file	檢測檔案是否可執行。
-s file	檢測檔案是否為空。
-e file	檢測檔案。
           

七、輸入/輸出重定向

1、 輸出重定向

将指令的完整的輸出重定向在使用者檔案中。

# 覆寫
$ echo "hello world" >./test.file

# 追加
$ echo "hello world" >>./test.file
           

2、 輸入重定向

從使用者檔案中的内容輸出到指令行。

$ wc -l  < ./test.file
1
           

可以與 while 語句結合,周遊檔案内容,按行列印:

while read line; do
    echo $line
done < ./test.file
           

3、 标準輸入輸出

一般情況下,每個

Unix/Linux

指令運作時都會打開三個檔案:

  • 标準輸入檔案(stdin):stdin的檔案描述符為0,Unix程式預設從stdin讀取資料。
  • 标準輸出檔案(stdout):stdout 的檔案描述符為1,Unix程式預設向stdout輸出資料。
  • 标準錯誤檔案(stderr):stderr的檔案描述符為2,Unix程式會向stderr流中寫入錯誤資訊。

預設情況下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。

如果希望 stderr 重定向到 file,可以這樣寫:

$ command 2>file
           

如果希望 stderr 追加到 file 檔案末尾,可以這樣寫:

$ command 2>>file
           

2 表示标準錯誤檔案(stderr)。

如果希望将 stdout 和 stderr 合并後重定向到 file,可以這樣寫:

$ command > file 2>&1

或者

$ command >> file 2>&1
           

如果希望對 stdin 和 stdout 都重定向,可以這樣寫:

$ command < file1 >file2
           

command 指令将 stdin 重定向到 file1,将 stdout 重定向到 file2。

八、eval 函數

當我們在指令行前加上

eval

時,

shell

就會在執行指令之前掃描它兩次。

eval

指令将首先會先掃描指令行進行所有的置換,然後再執行該指令。該指令适用于那些一次掃描無法實作其功能的變量。該指令對變量進行兩次掃描。

常見的使用場景如下:

1、普通情況

$ var=100
$ echo $var
100
$ eval echo $var
           

這樣和普通的沒有加

eval

關鍵字的指令的作用一樣。

2、字元串轉換指令

$ cat file
helle shell
it is a test of eval

$ tfile="cat file"
$ eval $tfile
helle shell
it is a test of eval
           

從上面可以看出 eval 經曆了兩次掃描,第一次掃描替換了變量為字元串,第二次掃描執行了字元串内容。

3、擷取參數

$ cat t.sh
#!/bin/bash

eval echo \$$#

$ ./t.sh a b c
c
$ ./t.sh 1 2 3
3
           

通過轉義符 “|” 與 $# 結合,可以動态的擷取最後一個參數。

4、 修改指針

$ var=100
$ ptr=var
$ eval echo \$$ptr
100
$ eval $ptr=50
$ echo $val
50
           

推薦閱讀:

《Linux指令行與shell腳本程式設計大全》

《谷歌shell編碼規範》