Shell 腳本程式中的條件語句和高級特性 | Shell 結構化 &判斷

Shell 腳本程式中的條件語句和高級特性 | Shell 結構化 &判斷

Overview of Content

在這篇文章中,我們將探討 Shell 腳本中的條件語句及其高級特性

從基本的 if-then 語句到複雜的條件測試和進階的 if-then 高級特性,我們將一一介紹各種條件語句的用法。你將了解如何使用 if-then 語句來執行不同的操作,以及如何使用 test 命令進行條件測試

此外,我們還將探討 if-then 高級特性和 case-in 命令的使用方法,幫助您更好地理解並運用 Shell 腳本中的條件語句。無論您是初學者還是有經驗的開發者,這篇文章都將為您提供全面且易於理解的概觀,幫助您提升 Shell 腳本編程的能力。

腳本結構化:可以 依照邏輯判斷去執行;這一類命令會根據條件去跳躍執行,這樣的命令通就稱為 結構化命令 (structured command)

寫文章分享不易,如有引用參考請詳註出處,如有指導、意見歡迎留言(如果覺得寫得好也請給我一些支持),感謝 😀

個人程式分享時比較注重「縮排」,所以可能不適合手機的排版閱讀,建議切換至「電腦版」、「平板版」視窗看


if-then 語句

如同在多個程式語言中使用的 if 判斷,其格式如下

if command
then
commands
fi

# 使用 `;` 符號的另一種寫法 -----------------
if command ; then
commands
fi

if-then 的判斷

if-then 語句的是用來判斷 command 的執行結果,這個 執行結果就是退出碼 (exit status code),而退出碼成功的定義則是 0;所以 command 執行完返回 0 則這個 if-then 就會判斷成立

● 這與一般程式的 if 語句判斷稍有不同,如果使用一般程式語言來說那我們就可改寫成如下


// Kotlin, Java or C...

if(command() == 0) {
    // do some commands
}

對應 Shell 判斷(上下範例是相同的判斷)


# Shell

if command ; then
    
fi

if-then 範例

A. 判斷運行的指令成功、失敗

#!/bin/bash

# 成功,exit code 0
if ls -laF ; then
echo "command done."
fi

# 失敗,exit code 非 0
if ABC ; then
echo "command done2."
fi

運行結果

B. 使用多個指令 + 區域變數

#!/bin/bash

userName=alien

# 多個命令 (cat, grep
# 使用 `$` 引用區域變數
if cat /etc/passwd | grep -i $userName ; then
echo "command done."
fi

運行結果

C. 嵌套 if-then 判斷

#!/bin/bash

if ls -laF ; then
echo "command done."
if ABC ; then
echo "command done2."
fi
fi

運行結果

if-then-else 語句

if-then-else 語句重點是多了 else 處理,當 if 命令執行後的退出碼不為 0 時,else 內容就會執行;格式如下


if command ; then
    commands
else 
    commands
fi

if-then-else 語句使用範例:

#!/bin/bash

if Yoyo ; then
echo "command done."
else
# 上面指令返回錯誤碼不為 0 時就執行
echo "command fail=($?)"
fi

if-then-elif-then 語句:多判斷

if-then-elif-then 語句可以解決 if-then 多嵌套導致腳本程式看起來不優雅的問題(多嵌套會導致波動拳);格式如下

case 命令也可以解決 (下面小節會介紹)

if command1 ; then
commands1

elif command2 ; then
commands2

elif command3 ; then
command3

fi

if-then-elif-then 使用範例:

以下還多了 else 處理所有情況都不符合的狀況

#!/bin/bash

if grep "ALIEN" /etc/passwd ; then
echo "ALIEN exists"

elif grep "Alien" /etc/passwd ; then
echo "Alien exists"

elif grep "alien" /etc/passwd ; then
echo "alien exists"

else
echo "No relate with alien"
fi


複合執行:&& 前方必須成立

&& 執行:多個條件 都必須符合才算成立(退出碼為 0 代表成功),格式如下

<前方指令> && <後方指令>

也就是說前方指令成功,後方指令才會運行,範例如下:

#!/bin/bash

if ls -laF && pwd; then
echo "command done."
else
echo "command failure."
fi

複合執行:|| 前方不能成立

|| 執行:與 && 執行相反,只有當前方指令執行失敗時(退出碼為非 0 代表失敗),後方指令才會執行,格式如下

<前方指令> || <後方指令>

範例如下:


cd e || echo "cd failed"

● 善用這個特性,甚至不需要使用 if 判斷句,也可以控制結構流程


## --------------------------------------- 
# cd e || echo "cd failed"
# 相當於如下程式

#!/bin/bash

cd e 2> /dev/null
if [ $? -ne 0 ]; then
    echo "cd failed"
fi

test 命令:比較數值、字符串、文件

前面我們說到 if-then 語句都是判斷退出碼(exit status code),如果 要判斷退出碼之外的條件就必須使用 test 命令;test 命令之後是 判斷,格式如下:

# 單獨使用 test 命令

test condition

if-then 語句使用 test 命令時,格式如下:

# 用法 1
if test condition ; then
commands
fi

# 用法 2
if [ condition ] ; then
commands
fi

如果 test 命令中的判斷成立,則 test 會退出並返回狀態碼 0


#!/bin/bash

if test ; then
    echo "empty test done."
else 
    echo "empty test fail=($?)"
fi

test 命令後面的 condition 不寫時,則退出碼為「非零」(代表失敗);

if-then 語句除了使用 test 命令之外,還可以使用中括號 [ ] 替代 test 命令


#!/bin/bash

if [ "Hello" ] ; then
    echo "test done."
else 
    echo "test fail=($?)"
fi

Condition:「數值」比較

● 如果需要對於數字作比較(比大小、是否等於... 等等);下表列出了列出 test 命令比較數值的 options

功能options使用
相等-eqn1 -eq n2
不等於-nen1 -ne n2
大於等於-gen1 -ge n2
大於-gtn1 -gt n2
小於等於-len1 - n2
小於-ltn1 - n2

● test condition 只能處理整數比較,不能比較浮點數否則會失敗


#!/bin/bash

varA=100
varB=1

if [ $varA -eq $varB ] ; then
    echo "A == B"

elif [ $varA -lt $varB ] ; then
    echo "A <= B"

elif [ $varA -le $varB ] ; then
    echo "A < B"

elif [ $varA -gt $varB ] ; then
    echo "A >= B"

elif [ $varA -ge $varB ] ; then
    echo "A > B"

fi

Condition:「字符串」比較

String 字串的比較是透過 ASCII Code 值比較;下表列出了列出 test 命令比較數值的 options

功能options使用
相同=n1 = n2
不相同!=n1 != n2
小於<n1 < n2
大於\>n1 > n2
字串長度非 0-n-n n1
字串長度 0-z-z n1

● 字串 大於 需要加跳脫字元 \避免腳本誤認為重新導向符號 (會變成判斷輸出一個文件是否成功)

A. 字串相等性比較:字串的比較有大小寫之分


#!/bin/bash

if [ $USER = "alien" ] ; then
    echo "Account is alien."
fi

if [ $USER != "Alien" ] ; then
    echo "Account is not Alien."
fi

B. 字串大小比較:其比較的其實也是 ASCII Code 的數值


#!/bin/bash

# 注意跳脫符號
if [ "Alien" /> "alien" ] ; then 
    echo "Alien > alien"
else 
    echo "Alien < alien"
fi

ASCII Code 值:a -> 96, A -> 65

> 要使用跳過符號(\>),否則會被認成重新導向 (你執行腳本後就產生一個 alien 檔案,並且比較還錯誤)

如果認成重新導向,那 test 命令就會判斷重新導向的退出碼是否為 0,如果為 0 則 test 命令也會給外部退出碼 0

C. 字符串是否存在:判斷字串的操作符要放置在字串之前


#!/bin/bash

var1="Apple"
var2=""

if [ -n $var1 ] ; then 
    echo "var1 not empty."
fi

if [ -z $var2 ] ; then
    echo "var2 is empty."
fi

# 未定義的變量也可以判斷
if [ -z $var3 ] ; then 
    echo "var3 is empty."
fi

未被定義的變量也可以判斷,不過會被判斷為空字串

Condition:「文件」比較

● test 命令的判斷,在腳本中最常用的就是 測試 Linux 文件系統上文件、目錄的狀態;下表為常用的文件判斷相關 test 命令

功能options使用
是否是一個目錄 (directory)-d-d file
是否是一個檔案 (file)-f-f file
檔案是否存在 (exist)-e-e file
檔案是否可讀(readable)-r-r file
檔案是否可寫(writeable)-r-w file
檔案是否可執行 (exec)-x-x file
檔案是否非空-s-s file
檔案是否屬於當前用戶-O-O file
檔案是否與當前用戶相同群組-G-G file
檔案新舊比較(新為主)-ntfile1 -nt file 2 (檢查 file1 是否比 file2 新)
檔案新舊比較(舊為主)-otfile1 -ot file 2 (檢查 file1 是否比 file2 舊)

這裡只寫幾個比較特別的檔案判斷

A. 文件所屬、群組判斷


#!/bin/bash

if [ -O /etc/passwd ] ; then
    echo "$USER is the onwer of /etc/passwd file."
else 
    echo "$USER is not the onwer of /etc/passwd file."
fi

if [ -G $HOME ] ; then
    echo "$HOME group is same as $USER."
else 
    echo "$HOME group is not same as $USER."
fi

B. 文件新舊比較:直接寫文件名就可相互比較

選項 -nt-ot 不會判斷檔案是否存在,如果檔案不存在它 -可能會返回錯的結果!!


#!/bin/bash

if [ str_3.sh -nt str_4.sh ] ; then
       echo "The file of 'str_3.sh' is new than 'str_4.sh'"
else 
       echo "The file of 'str_3.sh' is not new than 'str_4.sh'"
fi


if [ str_4.sh -ot str_3.sh ] ; then
    echo "The file of 'str_4.sh' is old than 'str_3.sh'"
else 
    echo "The file of 'str_4.sh' is not old than 'str_3.sh'"
fi

非 Condition:「!」符號

● 非 Condition 判斷只需要 在 test 命令之前添加 ! 即可進行非的判斷


#!/bin/bash

# 使用 `!` 進行非判斷
if [ ! -e $PWD/str_100.sh ] ; then
    echo "The file of '$PWD/str_100.sh' is not exist."
fi

! 之間必須使用空格格開

複合 test 條件

● 使用 test 命令也可以允許多個邏輯條件判斷,其使用方式如下

A. OR 運算:其中一個成立,就算成立 [ condition1 ] || [ condition2 ]

|| 可以對應 test 命令的 option -o,也就是 [ condition1 -o condition2 ]

B. AND 運算:全部成立,就算成立 [ condition1 ] && [ condition2 ]

&& 可以對應 test 命令的 option -a,也就是 [ condition1 -a condition2 ]

● 複合條件範例:


#!/bin/bash

if [ -G $HOME ] && [ -O $HOME ] ; then
    echo "The $HOME is same group and owner of $USER."
fi

if [ -w /etc/group ] || [ -w /etc/passwd ] ; then
    echo "The /etc/group or /etc/passwd is writeable."
fi


if-then 高級特性

bash shell 提供了幾個高級特性可以使用:1. 數學表達式的雙括號、2. 高級字串處理功能的雙方括號

雙括號:針對計算 (( ))

● 使用雙括號時的 if 判斷,就可以依照原先程式中的 if 判斷,也就是數值非 0 時才進入條件句(類似於開發用的程式的判斷),概念程式如下


if (( 1 )); then            
    echo "Hello 1."            # 數值非 0, 所以會進入判斷
fi

雙括號可以提供更高級的數學運算式(很像高級編程中的運算),其格式為:(( experssion ));experssion 如下表

雙括號內不用使用 $ 符號 !! 就可引用外部變數~

雙括號內部會假設變數內容皆為「數值」!可以用來判斷使用者輸入的數值,避免「字符的數字」與「數」搞混,可以用這個特性在比較判斷上

功能符號
後增val++
後減val--
先增++val
先減--val
Not 邏輯~val
反位!val
幂運算val **
左位移val >>
右位移val <<
與運算val1 & val2
或運算val1 | val2
邏輯與val1 && val2
邏輯或val1 || val2





#!/bin/bash

var=10

if (( var ** 2 > 99 )) ; then
    echo "var squre big then 99."
fi

(( --var ))

echo "var:$var"

echo "var: $(( var >> 1 ))"

雙方括號:針對字串 [[ ]]

● 雙方括號則是針對字符串的高級特性,它除了 test 命令字串比較,還 拓展了匹配模式,其格式:[[ expression ]]

● 並不是每個 Shell 都支持雙方括號(bash shell 支援)


#!/bin/bash

if [[ $USER == al* ]] ; then
    echo "Hello, $USER"
else
    echo "Who are you?"
fi


case-in 命令

如果有一組固定的答案,那使用 case 命令可以讓 Shell 看起來更整潔(大多是用在字符串配對時使用);其格式如下

使用 3 個符號 );;case/esac


case variable in

    pattern1) commands1;;
    
    pattern2 | patter3) command2;;
    
    patter*) command3;
    
    *) default commands;;

esac

case-in 使用

● 使用 case-in 命令取代 if-then 命令!(注意結尾使用雙分號 ;;

#!/bin/bash

case $USER in

Apple)
echo "I got the Fruit";;
Testing)
echo "In Testing account";;
alien | Alien)
echo "Hello $USER";;

*)
echo "Other account...?";;

esac

● 其他非預設 case 可以使用 *) 來擷取


更多 Linux Shell 知識

探索 Debian 系統上的套件管理系統(PKMS 概念)| 套件倉庫

Linux 環境、服務:用戶、權限管理

Linux 環境、服務、管理

在這個主題中,你可以探索各種 Linux 環境、服務和管理相關的重要概念…

了解 Linux 組成:內核責任、GNU 工具、桌面環境 | 基礎版到發行版

在這裡,你將了解 Linux 系統的基本組成部分,包括內核、GNU 工具和桌面環境,並了解不同 Linux 發行版之間的差異

探索 X Window 服務與桌面環境:Linux 視窗 XWindow 服務、D-Bus 機制

這裡探討了 Linux 系統中 X Window 服務和 D-Bus 機制的工作原理,讓你深入了解桌面環境的背後運作方式

Linux 系統管理入門:安全性、用戶管理與權限設定指南

在這個指南中,你將學習如何管理 Linux 系統,包括安全性、用戶管理和權限設定,以確保系統的安全和有效運作

認識身份驗證與權限管理:UNIX UID、使用者驗證與 PAM | PAM 設定

這裡介紹了 UNIX 系統中的身份驗證機制和權限管理方式,包括 UNIX UID、使用者驗證和 PAM(Pluggable Authentication Modules)設定

Linux Shell 相關知識

Linux Shell 相關知識

這個主題涵蓋了各種與 Linux Shell 相關的知識,從基礎到進階都有…

Shell 基礎知識:包括 Shell 的基礎使用以及差異,還有變數以及計算

認識命令行:Shell 類型、命令差異 | Sub Shell 關係 | Builtin 命令

這裡介紹了命令行中不同類型的 Shell,以及各種 Shell 命令之間的區別和內建命令的用法

Shell 全局及區域變數、特殊變數及環境變數 | Shell 啟動順序 | Array 變數

學習如何在 Shell 中使用全局變數、特殊變數和環境變數,以及如何設置和管理這些變數

探索腳本與命令:Shell 腳本的必備相關知識 | 腳本的數學運算

這裡介紹了 Shell 腳本的基本知識和常用技巧,包括如何執行腳本和進行數學運算

Shell 結構化

Shell 腳本程式中的條件語句和高級特性 | Shell 結構化 & 判斷

該篇文章是介紹結構化腳本的基礎以及特殊的高級技巧,轉著於腳本中的邏輯判斷

掌握 Bash 腳本中的迴圈與循環控制技巧 | Shell 結構化 & 循環

接著是腳本中的循環與控制的技巧,它可以讓我們在 Shell 腳本中有更多的判斷與技巧

探索 Shell 函數與腳本庫 | Shell 結構化 & 函數

這裡你將會學在到結構化腳本的重要技巧「函數」,它可以讓你建立可重複利用的腳本,以節省我們之後開發的時間

Shell 掌握參數處理與用戶輸入技巧 | Shell 結構化 &輸入互動

不可互動的腳本有時候相對無趣,這篇文章有分享如何讓腳本與使用者產生交互互動,並依照使用者輸入的參數做讀取

Shell script 進階

Shell 命令輸入與輸出指南:掌握標準文件描述符與重定向 | 臨時文件

掌控 Shell、指令的輸出輸入是成為進階使用 CLI 之人必經的一課,透過這篇文章可以了解到輸出輸入、臨時文件等等資訊

探索 Linux 訊號與後台進程管理:安排定期啟動腳本 | 運行時啟動腳本

信號可以算是 Shell 腳本與 Linux 系統之間的通訊方式,透過這個文章,我們可以了解到信號與 Shell 腳本的安排時間

Linux 硬體規劃:檔案系統、分區

Linux 硬體規劃:檔案系統、分區

最後,這個主題涵蓋了 Linux 系統中硬體結構相關的重要概念…

Shell 文件和目錄操作的常用命令和技巧 | 尋找檔案

這裡介紹了 Shell 中常用的文件和目錄操作命令,包括如何尋找文件和管理文件系統

深入探索 Linux 文件系統與硬體管理 | 分區、檔案系統、邏輯卷 | inode

學習如何管理 Linux 文件系統,包括分區、文件系統和邏輯卷等相關概念,以及文件系統中的 inode 機制

理解 Unix/Linux 設備偵測、建立 | 認識與使用 udevd | SCSI 與 Linux 核心

這裡介紹了 Unix/Linux 系統中的設備偵測和管理,包括如何使用 udevd 和 SCSI 相關的 Linux 核心知識


Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

發表迴響