作者:守望,Linux應用開發者,目前在公眾號【程式設計珠璣】 分享Linux/C/C++/資料結構與演演算法/工具等原創技術文章和學習資源。
前言
在linux下使用shell程式設計常常能夠極大簡化我們的工作。而下麵這些必備的知識你是否都掌握了呢?
入參和預設變數
對於shell指令碼而言,有些內容是專門用於處理引數的,它們都有特定的含義,例如:
/home/shouwang/test.sh para1 para2 para3
$0 $1 $2 $3
指令碼名 第一個引數 第三個引數
其中$0代表了執行的指令碼名,$1,$2分別代表了第一個,第二個引數。除此之外,還有一些其他的預設變數,例如:
# 代表指令碼後面跟的引數個數,前面的例子中有3個引數
$@ 代表了所有引數,並且可以被遍歷
$* 代表了所有引數,且作為整體,和$*很像,但是有區別
$$ 代表了當前指令碼的行程ID
$? 代表了上一條命令的退出狀態
變數
給變數賦值,使用等號即可,但是等號兩邊千萬不要有空格,等號右邊有空格的字串也必須用引號引起來:
para1="hello world" #字串直接賦給變數para1
unset用於取消變數。例如:
unset para1
如何使用變數呢?使用變數時,需要在變數前加$,例如要列印前面para1的內容:
echo "para1 is $para1"
#將會輸出 para1 is hello world
或者變數名兩邊新增大括號:
echo "para1 is ${para1}!"
#將會輸出 para1 is hello world!
命令執行
在shell中執行命令通常只需要像在終端一樣執行命令即可,不過,如果想要命令結果打印出來的時候,這樣的方式就行不通了。因此,shell的命令方式常有:
a=`ls` #`是左上角~鍵,不是單引號
或者使用$,後面括號內是執行的命令:
echo "current path is $(pwd)" #
另外,前面兩種方式對於計算運算式也是行不通的,而要採取下麵的方式:
echo "1+1=$((1+1))" #列印:1+1=2
即$後面用兩重括號將要計算的運算式包裹起來。
那如果要執行的命令儲存在變數中呢?前面的方法都不可行了,當然括號內的內容被當成命令執行還是成立的。要使用下麵的方式,例如:
a="ls"
echo "$($a)"
但是如果字串時多條命令的時候,上面的方式又不可行了,而要採用下麵的方式:
a="ls;pwd"
echo "$(eval $a)"
這是使用了eval,將a的內容都作為命令來執行。
條件分支
一般說明,如果命令執行成功,則其傳回值為0,否則為非0,因此,可以透過下麵的方式判斷上條命令的執行結果:
if [ $? -eq 0 ]
then
echo "success"
elif [ $? -eq 1 ]
then
echo "failed,code is 1"
else
echo "other code"
fi
case陳述句使用方法如下:
name="aa"
case $name in
"aa")
echo "name is $name"
;;
"")
echo "name is empty"
;;
"bb")
echo "name is $name"
;;
*)
echo "other name"
;;
esac
初學者特別需要註意以下幾點:
-
[]前面要有空格,它裡面是邏輯運算式
-
if elif後面要跟then,然後才是要執行的陳述句
-
如果想列印上一條命令的執行結果,最好的做法是將 $?賦給一個變數,因為一旦執行了一條命令,$?的值就可能會變。
-
case每個分支最後以兩個分號結尾,最後是case反過來寫,即esac。
多個條件如何使用呢,兩種方式,方式一:
if [ 10 -gt 5 -o 10 -gt 4 ];then
echo "10>5 or 10 >4"
fi
方式二:
if [ 10 -gt 5 ] || [ 10 -gt 4 ];then
echo "10>5 or 10 >4"
fi
其中-o或者||表示或。這裡也有一些常見的條件判定。
總結如下:
-
-o or 或者,同||
-
-a and 與,同&&
-
! 非
整數判斷:
-
-eq 兩數是否相等
-
-ne 兩數是否不等
-
-gt 前者是否大於後者(greater then)
-
-lt 前面是否小於後者(less than)
-
-ge 前者是否大於等於後者(greater then or equal)
-
-le 前者是否小於等於後者(less than or equal)
字串判斷str1 exp str2:
-
-z “$str1” str1是否為空字串
-
-n “$str1” str1是否不是空字串
-
“$str1” == “$str2” str1是否與str2相等
-
“$str1” != “$str2” str1是否與str2不等
-
“$str1” =~ “str2” str1是否包含str2
特別註意,字串變數最好用引號引起來,因為一旦字串中有空格,這個運算式就錯了,有興趣的可以嘗試當str1=”hello world”,而str2=”hello”的時候進行比較。
檔案目錄判斷:filename
-
-f $filename 是否為檔案
-
-e $filename 是否存在
-
-d $filename 是否為目錄
-
-s $filename 檔案存在且不為空
-
! -s $filename 檔案是否為空
迴圈
迴圈形式一,和Python的for in很像:
#遍歷輸出指令碼的引數
for i in $@; do
echo $i
done
迴圈形式二,和C語言風格很像:
for ((i = 0 ; i do
echo $i
done
迴圈列印0到9。
迴圈形式三:
for i in {1..5}; do
echo "Welcome $i"
done
迴圈列印1到5。
迴圈方式四:
while [ "$ans" != "yes" ]
do
read -p "please input yes to exit loop:" ans
done
只有當輸入yes時,迴圈才會退出。即條件滿足時,就進行迴圈。
迴圈方式五:
ans=yes
until [ $ans != "yes" ]
do
read -p "please input yes to exit loop:" ans
done
這裡表示,只有當ans不是yes時,迴圈就終止。
迴圈方式六:
for i in {5..15..3}; do
echo "number is $i"
done
每隔5列印一次,即列印5,8,11,14。
函式
定義函式方式如下:
myfunc()
{
echo "hello world $1"
}
或者:
function myfunc()
{
echo "hello world $1"
}
函式呼叫:
para1="shouwang"
myfunc $para1
傳回值
通常函式的return傳回值只支援0-255,因此想要獲得傳回值,可以透過下麵的方式。
function myfunc() {
local myresult='some value'
echo $myresult
}
val=$(myfunc) #val的值為some value
透過return的方式適用於判斷函式的執行是否成功:
function myfunc() {
#do something
return 0
}
if myfunc;then
echo "success"
else
echo "failed"
fi
註釋
shell透過#來註釋一行內容,前面我們已經看到過了:
#!/bin/bash
# 這是一行註釋
:'
這是
多行
註釋
'
ls
:<這也可以
達到
多行註釋
的目的
EOF
日誌儲存
指令碼執行後免不了要記錄日誌,最常用的方法就是重定向。以下麵的指令碼為例:
#!/bin/bash
#test.sh
lll #這個命令是沒有的,因此會報錯
date
方式一,將標準輸出儲存到檔案中,列印標準錯誤:
./test.sh > log.dat
這種情況下,如果命令執行出錯,錯誤將會列印到控制檯。所以如果你在程式中呼叫,這樣將不會講錯誤資訊儲存在日誌中。
方式二,標準輸出和標準錯誤都儲存到日誌檔案中:
./test.sh > log.dat 2>&1
2>&1的含義可以參考《如何理解linuxshell中的2>&1》
方式三,儲存日誌檔案的同時,也輸出到控制檯:
./test.sh |tee log.dat
指令碼執行
最常見的執行方式前面已經看到了:
./test.sh
其它執行方式:
sh test.sh #在子行程中執行
sh -x test.sh #會在終端列印執行到命令,適合除錯
source test.sh #test.sh在父行程中執行
. test.sh #不需要賦予執行許可權,臨時執行
指令碼退出碼
很多時候我們需要獲取指令碼的執行結果,即退出狀態,通常0表示執行成功,而非0表示失敗。為了獲得退出碼,我們需要使用exit。例如:
#!/bin/bash
function myfun()
{
if [ $# -lt 2 ]
then
echo "para num error"
exit 1
fi
echo "ok"
exit 2
}
if [ $# -lt 1 ]
then
echo "para num error"
exit 1
fi
returnVal=`myfun aa`
echo "end shell"
exit 0
這裡需要特別註意的一點是,使用
returnVal=`myfun aa`
這樣的句子執行函式,即便函式裡面有exit,它也不會退出指令碼執行,而只是會退出該函式,這是因為exit是退出當前行程,而這種方式執行函式,相當於fork了一個子行程,因此不會退出當前指令碼。最終結果就會看到,無論你的函式引數是什麼最後end shell都會列印。
./test.sh;echo $?
0
總結
以上就是shell程式設計最基本也是最關鍵的內容。當然這並非全部,例如陣列,字典,引數處理等都沒有詳細介紹,由於篇幅有限,將會在後面的文章中進行詳細介紹。學好shell,解放你的雙手。
朋友會在“發現-看一看”看到你“在看”的內容