歡迎光臨
每天分享高質量文章

這些必備的Linux shell知識你都掌握了嗎

作者:守望,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,解放你的雙手。

已同步到看一看
贊(0)

分享創造快樂