iT邦幫忙

2021 iThome 鐵人賽

DAY 8
0
自我挑戰組

[機派X] 無人機與樹莓派的相遇 Linux不只是過客系列 第 8

[機派X] Day 8 - 我是 Bash 我調皮,令人匪夷所思的 Bash 語法

引言

昨天介紹了套件管理軟體以及圖形化使用者界面的安裝,也是指令介紹的最後一篇文章。學習了這麼多指令後,如果能靈活運用,使用 Linux 來做一些基礎的軟體開發我想應該都還撐的住!

今天是機派X系列文章的第八天。
本篇將介紹 Bash 的變數操作、條件判斷與流程控制。學習過這些東西後,就能用 Bash 將數條指令組合起來依序執行,變成有用的小程式。

本篇大綱:

  • 引言
  • Bash 變數的設置與取用
  • Bash 讀取使用者的輸入
  • Bash 條件判斷介紹
  • Bash 迴圈介紹
  • 總結
  • 關於本文章系列

也許你會疑惑,為什麼要學習這些,這些很明顯都是在學程式啊,但是我的專案又不用 Bash 開發!為何要學這些?
原因很簡單:如果以 Linux 做為開發平台,學習 Bash 有助於你寫一些「小工具」,讓你在主要程式開發的過程中更方便。
我會在下一篇文章中用 Bash 寫一些實用的小程式,希望能解除大家的疑惑。

由於下一篇文章才會帶領大家開新檔案、撰寫腳本,所以建議將本篇與下一篇搭配著看。

Bash 變數的設置與取用

首先,來介紹變數這個東西。

可以想像變數(Variable)就是一個盒子,用於儲存資料。撰寫程式時,常常會需要很多不同的「盒子」來儲存不同資料。每個變數(或說盒子)都有一個名稱,該名稱由寫程式的人定義,以便撰寫程式時調用,透過呼叫變數名稱就可以取得或變更其中的資料內容。

在 Bash 中設定變數可以使用 export 來完成,若要取用變數則要透過 $ 。

以下是 export 的語法,並請注意 = 左右兩邊不可有空格!

export NAME[=DATA]

例如:

export my_name='Kevin Huang'

在變數名稱前方加上 $ 就可以取得該變數的值:

echo "My name is $my_name !"

Note :
此時,單引號與雙引號的使用就會出現差異。如果使用單引號,具有特殊意義的字元(例如:$ 符號)不會被解析,因此會「如實呈現」顯示「My name is $my_name !」。如果使用雙引號,具有特殊意義的字元(例如:$ 符號)才會被解析,因此會顯示「My name is Kevin Huang !」。

接下來,你可以嘗試以下範例:

export name='Kevin'
export name='$name Huang'
echo "$name"
export name='Kevin'
export name="$name Huang"
echo "$name"

Note :
如果你有學過其他程式語言,你可以能會好奇:怎麼沒有介紹變數的資料型態(Data Type)呢?
Answer:因為 Bash 只有一種資料型態,就是字串(String),所以其實沒有必要特別介紹,畢竟對於沒有學過其他程式語言的新手來說,資料型態的概念比較陌生,Bash 這種特殊的資料型態不說也罷,說了之後搞不好概念更亂了。

Bash 讀取使用者的輸入

可以使用 read 來讀取使用者輸入的資料,VARIABLE 則是儲存使用者輸入資料的變數名,-r 引數則是不解析輸入資料中的跳脫字元,-p 引數則是給使用者的提示文字。
每次執行 read 都是讀取一行,行中若有空白則會依照空白切割,並將切割出來的字串依序儲存在變數中。

read [-r] [-p PROMPT] VARIABLE......

舉例來說:

read -p 'Please input your name and address: ' name address

使用者輸入:

Bella Taipei, Taiwan

可以使用 echo 印出使用者輸入的文字:

echo "Hello $name. $address is a nice place."

Bash 條件判斷介紹

有時候會需要針對資料內容做條件判斷,依照判斷的結果執行不同的指令。
舉例來說:請使用者輸入年齡,若輸入的年齡大於等於 18 歲就 ...... ,否則就 ...... 。

以下是如果 TEST 條件判斷為真(True),就會執行 Block 中的指令。

if [[ TEST ]]
then
    # Block
fi

以下是如果 TEST 條件判斷為真(True),就會執行 Block 1 中的指令;
如果條件判斷為假(False),則會執行 Block 2 中的指令。

if [[ TEST ]]
then
    # Block 1
else
    # Block 2
fi

以下是如果 TEST1 條件判斷為真(True),就會執行 Block 1 中的指令,其餘測試則不再判斷;如果條件判斷為假(False),才會接續 TEST2 的判斷。
如果 TEST2 條件判斷為真(True),就會執行 Block 2 中的指令;如果條件判斷為假(False),就會直接執行 Block 3 的指令。

if [[ TEST1 ]]
then
    # Block 1
elif [[ TEST2 ]]
then
    # Block 2
else
    # Block 3
fi

Note :

  1. [[ 與 ]] 的左右兩邊都要有空格。
  2. 除了 [[ ]] 之外,也可以使用 [ ] ,請參考 這篇文章 的說明。
  3. 區塊的縮排(Indent)並不是語法的一部分,因此可有可無,但是為了讓 coding style 看起來舒服一點,建議要縮排。

TEST 是要執行的測試(test),依照測試結果會返回真(True)或假(False)。

測試 測試說明
!TEST 反轉 TEST 的測試結果。
-z STRING 判斷 STRING 是否為空字串。
-n STRING 判斷 STRING 是否不為空字串。
STRING1 = STRING2 判斷 STRING1 是否與 STRING2 相同。
STRING1 != STRING2 判斷 STRING1 是否與 STRING2 不同。
STRING1 > STRING2 判斷 STRING1 的 ASCII 序列是否大於 STRING2。
STRING1 < STRING2 判斷 STRING1 的 ASCII 序列是否小於 STRING2。
INTEGER1 -eq INTEGER2 判斷 INTEGER1 是否等於 INTEGER2。
INTEGER1 -ne INTEGER2 判斷 INTEGER1 是否不等於 INTEGER2。
INTEGER1 -gt INTEGER2 判斷 INTEGER1 是否大於 INTEGER2。
INTEGER1 -lt INTEGER2 判斷 INTEGER1 是否小於 INTEGER2。
INTEGER1 -ge INTEGER2 判斷 INTEGER1 是否大於或等於 INTEGER2。
INTEGER1 -le INTEGER2 判斷 INTEGER1 是否小於或等於 INTEGER2。
TEST1 && TEST2 將 TEST1 與 TEST2 的測試結果做 AND 邏輯運算。
TEST1 || TEST2 將 TEST1 與 TEST2 的測試結果做 OR 邏輯運算。
-f PATH 判斷 PATH 是否存在,而且是一個檔案。
-d PATH 判斷 PATH 是否存在,而且是一個目錄。
-s PATH 判斷 PATH 是否存在,而且檔案內容不為空。
-r PATH 判斷 PATH 是否存在,而且當前使用者對該檔案具有讀取權限。
-w PATH 判斷 PATH 是否存在,而且當前使用者對該檔案具有寫入權限。
-x PATH 判斷 PATH 是否存在,而且當前使用者對該檔案具有執行權限。

舉幾個例子來看看吧:

read -p 'How old are you? ' age

if [[ $age -ge 18 ]]
then
    echo 'Let us do something interesting ......'
fi
read -p 'How old are you? ' age

if [[ $age -ge 18 ]]
then
    echo 'Let us do something interesting ......'
else
    echo 'Go to the basement and sweep the floor!'
fi
read -p 'How many books on the shelf? ' numBook

if [[ $numBook -lt 0 ]]
then
    echo '??????????????????????????????????'
elif [[ $numBook -eq 0 ]]
then
    echo 'You can buy a book from www.books.com.tw .'
elif [[ $numBook -gt 10 ]]
then
    echo 'You are a man or woman who likes to read!'
else
    echo 'You can buy more books from www.books.com.tw .'
fi

Bash 迴圈介紹

有時候也會需要重複執行某些指令,因此會需要用到迴圈(loop)。
迴圈有很多種寫法,以下將一一舉例介紹。

for i in {0..10}
do
    echo "There is/are $i rabbit(s)."
done
for i in {0..15..3}
do
    echo "There is/are $i rabbit(s)."
done
for i in {1..9}
do
    for j in {1..9}
    do
        printf "    %1d * %1d = %2d" $i $j $(($i * $j))
    done

    echo ''
done
for i in {1..10}
do
    for ((j=$i; j<=10; ++j))
    do
        echo -n '*'
    done

    echo ''
done
export TRUTH=(49 20 4C 4F 56 45 20 43 53 49 45)

for letter in ${TRUTH[@]}
do
    printf "\x$letter"
done

echo ''
export name=''

while [[ -z $name ]]
do
    read -p 'Your name? ' name
done

echo "Hello $name."
export number=2

while :
do
    echo "There are $number dogs."

    if [[ $number -eq 10 ]]
    then
        break
    fi

    export number=$(( $number + 1 ))
done

總結

因為今天的文章內容有點乾,所以只好補張圖化解尷尬氣氛。
我找不到這文的梗在哪裡,所以幫你種了一個
圖源傳送門

氣氛現在從尷尬變成非常尷尬。
情況從糟糕變成難以理解
圖源傳送門

關於本文章系列

如果對於文章內容有建議、糾錯或圖源標示不正確的問題,歡迎參考 [機派X] Day 1 嘗試與文章作者聯絡。
想看更多本系列的文章,請連結至 [機派X] Day 1 查看大綱。


上一篇
[機派X] Day 7 - 啊我就怕 Nvidia 顯卡驅動裝不起來啊
下一篇
[機派X] Day 9 - 玩轉 Bash:原來 Bash 還有這些妙用
系列文
[機派X] 無人機與樹莓派的相遇 Linux不只是過客15

尚未有邦友留言

立即登入留言