Bash 提供了強大的陣列和正規表示式功能,能有效處理資料。一般陣列適用於順序存取,而關聯式陣列則以鍵值對儲存資料。遍歷陣列時,使用 for 迴圈搭配特定語法即可存取每個元素。正規表示式則提供更精確的模式比對,常用於資料驗證、提取和搜尋替換。grep 指令結合正規表示式,能快速篩選符合條件的文字行,例如使用 . 比對任意單一字元、* 比對前一個元素零到多次、+ 比對一次到多次,以及 ? 表示可選元素。^$ 分別比對行首和行尾,括弧表示式 [] 可比對指定字元集或範圍。=~ 運算元則用於判斷字串是否符合正規表示式,結合條件判斷和迴圈,能實作更複雜的資料處理邏輯。角色類別如 [:alpha:][:digit:] 等簡化了正規表示式撰寫,而標誌如 i(忽略大小寫)、g(全域搜尋)則提供更多搜尋選項。捕捉群組能提取比對的子字串,方便資料分析。

陣列遍歷與高效資料處理

在 Bash 指令碼編寫中,遍歷陣列是一個常見的操作。以下將介紹如何使用迴圈來遍歷每個元素,並說明關聯式陣列(associative arrays)的用法。這些技巧將幫助你在指令碼中更高效地處理資料。

遍歷一般陣列

遍歷一般陣列是一個基本但非常有用的操作。以下是一個簡單的範例,展示如何遍歷並列印陣列中的每個元素:

#!/usr/bin/env bash
my_array=(apple banana cherry)
for fruit in "${my_array[@]}"
do
    echo "Fruit: $fruit"
done

內容解密:

  • my_array=(apple banana cherry):這行宣告了一個包含三個元素的一般陣列。
  • for fruit in "${my_array[@]}":這是一個 for 迴圈,它會遍歷 my_array 中的每個元素,並將當前元素指定給變數 fruit
  • echo "Fruit: $fruit":這行會將當前元素的值列印預出來。

這段指令碼會依序列印每個水果的名稱:

Fruit: apple
Fruit: banana
Fruit: cherry

關聯式陣列

Bash 支援關聯式陣列(associative arrays),這種陣列中的每個元素都由一個鍵值對來標識,而不是數字索引。宣告關聯式陣列需要使用 declare -A 命令。

以下是如何宣告和遍歷關聯式陣列的範例:

#!/usr/bin/env bash
# 宣告關聯式陣列
declare -A my_assoc_array
# 為關聯式陣列指定
my_assoc_array[apple]="green"
my_assoc_array[banana]="yellow"
my_assoc_array[cherry]="red"
# 遍歷整個關聯式陣列
for key in "${!my_assoc_array[@]}"
do
    echo "$key: ${my_assoc_array[$key]}"
done

內容解密:

  • declare -A my_assoc_array:這行宣告了一個名為 my_assoc_array 的關聯式陣列。
  • my_assoc_array[apple]="green":這行為關聯式陣列增加了一個鍵值對,鍵為 apple,值為 green
  • for key in "${!my_assoc_array[@]}":這行開始一個 for 迴圈,它會遍歷關聯式陣列中的所有鍵,並將當前鍵指定給變數 key
  • echo "$key: ${my_assoc_array[$key]}":這行列印當前鍵及其對應的值。

這段指令碼會輸出每個水果及其顏色:

cherry: red
apple: green
banana: yellow

需要注意的是,Bash 中的關聯式陣列不保留順序,所以輸出的順序可能與新增的順序不同。

存取特定元素

你也可以使用特定鍵來存取關聯式陣列中的元素。以下範例展示瞭如何存取特定鍵值對:

#!/usr/bin/env bash
# 宣告關聯式陣列
declare -A my_assoc_array
# 為關聯式陣列指定
my_assoc_array[apple]="green"
my_assoc_array[banana]="yellow"
my_assoc_array[cherry]="red"
# 遍歷整個關聯式陣列
for key in "${!my_assoc_array[@]}"
do
    echo "$key: ${my_assoc_array[$key]}"
done
# 存取特定鍵的值
echo "The color of an apple is: ${my_assoc_array[apple]}"

內容解密:

  • ${my_assoc_array[key]}:這是存取特定鍵值對的語法。例如,${my_assoc_array[apple]} 會傳回 green
  • echo "The color of an apple is: ${my_assoc_array[apple]}":這行會列印特定鍵值對的值。

執行這段指令碼後,輸出如下:

cherry: red
apple: green
banana: yellow
The color of an apple is: green

Bash 指令碼中的實務應用

Bash 的陣列功能強大且靈活,無論是處理簡單的專案清單還是複雜的資料結構(如關聯式陣列),掌握如何使用陣列都能顯著提升指令碼編寫能力。實務上,你可以利用陣列來處理多種任務,例如批次處理檔案、管理組態設定或進行資料分析。

常見問題解答

  1. 如何在 Bash 中宣告一般陣列?

    • 在 Bash 中宣告一般陣列可以使用括號和空格分隔各元素,例如:my_array=(element1 element2 element3)
  2. 如何宣告並初始化一個空的關聯式陣列?

    • 使用 declare -A my_assoc_array 命令宣告並初始化一個空的關聯式陣列。
  3. 如何存取並修改關聯式陣列中的特定元素?

    • 使用 ${array_name[key]} 語法來存取和修改特定鍵值對,例如:${my_assoc_array[apple]}
  4. Bash 的陣列是否保留順序?

    • 對於一般陣列來說,Bash 陣列保留順序;而對於關聯式陣列來說,不保留順序。

總結來說,Bash 的變數、條件判斷、迴圈和陣列是強大且靈活的工具,可以幫助你高效地編寫指令碼。透過實踐和探索更多應用場景,你將能夠進一步提升指令碼編寫能力。

正規表示式基礎

在資料處理及程式設計中,正規表示式(Regular Expressions, Regex)是一種強大的工具,能夠用來搜尋、比對及操作文字。正規表示式遠超過一般文字編輯器或文書處理軟體中的搜尋功能,能夠定義文字模式並進行複雜的搜尋和編輯。

正規表示式的應用

正規表示式非常多功能,以下是幾個常見的應用範例:

  • 資料驗證:確保使用者輸入符合特定格式,例如電子郵件地址或電話號碼。
  • 資料提取:從大量資料中提取特定資訊,例如從網頁中提取所有網址。
  • 搜尋和替換:根據模式而非精確比對來查詢並替換檔案中的文字。

正規表示式的基本組成

正規表示式由字元和元字元組成。字元是指我們要在文字中查詢的字母、數字和符號;而元字元則是具有特殊意義的符號,用來定義模式。常見的元字元包括 .*+?^$[]{n}{n,m}(a|b) 等。

示例:使用 grep 指令

以下部分將展示如何使用 grep 指令來展示一些正規表示式的基本概念。grep 是一個用來在檔案或管道輸入中搜尋模式的指令。如果想要了解更多關於 grep 的資訊,可以執行 man grep 指令。

元字元:句點 .

句點 . 元字元能夠比對除了換行符(表示行結束)之外的任何單一字元。一個常見的應用是當我們解析程式輸出時,想要移除空白行。由於 . 可以比對任何字元,所以在正規表示式中單獨使用時可以移除空白行,因為沒有任何東西與之比對。以下圖示展示了 . 元字元的用法:

此圖示展示了 . 元字元如何比對任何單一字元並幫助我們移除空白行。

元字元:星號 *

星號 * 元字元能夠比對前一個元素出現零次或多次。假設我們有一個名為 sample.txt 的檔案,裡麵包含各種文字行,我們想找到與模式 ho*p 比對的行。這個模式應該能夠比對 hop, hoop, hooooop 等。以下是 sample.txt 的內容:

hop
hoop
hooooop

要使用 grep 搜尋這些模式,需要加上 -E 選項來啟用擴充套件正規表示式:

grep -E 'ho*p' sample.txt

-E 選項啟用了擴充套件正規表示式,支援使用 * 元字元等功能。否則,在非正規表示式模式下,星號被稱為 glob 字元。

元字元:加號 +

加號 + 元字元能夠比對前一個元素出現一次或多次。例如,當我們分析記錄檔案時,模式 Error: + 可以幫助我們找到以 Error: 單詞開始且後面有空白的錯誤訊息行。如果沒有這個元字元,我們可能會錯過有多個空白的情況或浪費時間檢視無關資料。

元字元:問號 ?

問號 ? 元字元使前一個元素成為可選項。簡單來說,它告訴正規表示式引擎前一個元素可以出現零次或一次。這意味著該元素可能存在也可能不存在。

例如,假設我們要處理日誌檔案,這些日誌檔名稱通常是 app-log-2024.txt ,但有時會包含額外標識如 app-log-2024-debug.txt 。使用問號元字元可以讓我們的指令碼更靈活:模式 app-log-2024(-debug)?.txt 就可以比對兩種檔名稱。

元字元:插入號 ^

插入號 ^ 元字元能夠比對行首。這對精確查詢特別重要。如果我們沒有使用這個元字元並在文字中搜尋 “DONE” ,我們可能會得到包含 “DONE” 的任何行而不是僅僅在開頭有 “DONE” 的行。

元字元:美元 $

美元 $ 元字元能夠比對行尾。

此圖示展示了 $ 元字元如何用來確保我們僅比對完整單詞而不是包含該單詞的一部分。

括弧表達 [ ]

括弧表達 [ ] 能夠比對方括弧內任何單一字元。邏輯 NOT 操作可以透過將 ^ 放在列表第一個位置來實作。例如:

  • 想要比對母音時可以使用 [aeiou]
  • 想要比對子音時可以使用 [^aeiou]

範圍表示法通常在方括弧內使用以節省時間並避免輸入所有連續的文字或數值範圍內的每個專案:

  • [a-z] 用於表示小寫英文 a 到 z。
  • [1-10] 用於表示數值 1 到 10。
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Bash 指令碼:陣列遍歷與正規表示式高效資料處理

package "NumPy 陣列操作" {
    package "陣列建立" {
        component [ndarray] as arr
        component [zeros/ones] as init
        component [arange/linspace] as range
    }

    package "陣列操作" {
        component [索引切片] as slice
        component [形狀變換 reshape] as reshape
        component [堆疊 stack/concat] as stack
        component [廣播 broadcasting] as broadcast
    }

    package "數學運算" {
        component [元素運算] as element
        component [矩陣運算] as matrix
        component [統計函數] as stats
        component [線性代數] as linalg
    }
}

arr --> slice : 存取元素
arr --> reshape : 改變形狀
arr --> broadcast : 自動擴展
arr --> element : +, -, *, /
arr --> matrix : dot, matmul
arr --> stats : mean, std, sum
arr --> linalg : inv, eig, svd

note right of broadcast
  不同形狀陣列
  自動對齊運算
end note

@enduml

此圖示展示了方括弧 [ ] 的基本功能及其如何實作邏輯 NOT 操作和範圍表示法。

正規表示式基礎

正規表示式的基本概念

正規表示式(regex)是一種強大的工具,能夠用來比對和處理文字。它在搜尋、替換、驗證等多種情境中都有廣泛的應用。熟練使用正規表示式能夠大幅提升效率,並且能夠處理複雜的文字操作需求。

括號表示式與元字元

括號表示式是正規表示式中非常有價值且節省時間的功能。元字元 {n} 指定前一個元素必須恰好重複 n 次。它也可以寫成 {n, m}{n,},分別表示前一個元素重複 nm 次,或者至少重複 n 次。

括號表示式範例

假設我們要比對字母 o 出現至少三次的情況:

grep -E 'o\{3,\}' example.txt

在此範例中,我們使用 -E 選項來啟用擴充套件正規表示式功能,並且使用反斜槓來轉義括號。

或運算元與比對運算元

元字元 (a|b) 用來比對 ab 中的一個。而 =~ 比對運算元通常在指令碼中使用,用來判斷字串是否比對正規表示式。

比對運算元範例

以下是一個簡單的範例,展示如何使用 =~ 運算元:

if [[ "Hello World" =~ "World" ]]; then
    echo "Match found!"
else
    echo "No match"
fi

如果字串左側的內容比對到右側的正規表示式,則條件為真,並且離開狀態為 0(成功)。反之,條件為假,離開狀態為 1(失敗)。

離散邏輯運算元

在 Bash 指令碼中,&&|| 是邏輯運算元,用來結合多個命令或條件。它們的使用依賴於命令的離開狀態。

離散邏輯運算元範例

假設我們要檢查字串是否包含特定模式:

[[ "Hello World" =~ "World" ]] && echo "Match found!" || echo "No match"

如果模式比對成功,則執行 echo "Match found!";否則執行 echo "No match"

角色類別與簡化正規表示式

角色類別在括號表示式中提供了方便的簡寫方式,能夠大幅簡化正規表示式的撰寫:

  • :alpha::字母字元
  • :alnum::字母數字字元
  • :digit::數字 0 到 9
  • :blank::空格和縮排
  • :cntrl::控制字元
  • :lower::小寫字母
  • :upper::大寫字母
  • :punct::標點符號
  • :space::包括空格、縮排、換行等空白字元

角色類別必須放在括號中使用,例如 [[:alpha:]]

標誌與搜尋修飾器

標誌範例

  • i:忽略大小寫
  • g:全域搜尋(找到所有比對)
  • m:多行模式(改變 ^$ 的行為)

例如:

grep -i 'example' example.txt    # 忽略大小寫搜尋 'example'

這些標誌可以單獨或組合使用,具體使用方式取決於工具。

基本正規表示式範例

以下是一些簡單的正規表示式範例:

  1. 搜尋字母 t

    grep 't' example.txt    # 預設全域搜尋
    
  2. 比對所有母音:

    grep '[aeiou]' example.txt    # 比對 a, e, i, o, u 中的任意一個
    
  3. 比對所有子音:

    grep '[^aeiou]' example.txt    # 比對非 a, e, i, o, u 的任意一個字母
    
  4. 搜尋特定模式:

    grep 't[^w]*[[:alpha:]]*' example.txt    # 搜尋 t 後面不包含 w 的任何字母序列
    

高階正規表示式技巧

捕捉群組

捕捉群組允許我們將部分模式視為單一單元。這些群組可以用來應用量詞、查詢重複或提取資訊。

捕捉群組範例

假設我們要從日誌檔案中提取時間戳:

(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})

這個模式會比對年月日時分秒的格式。

提取資料範例

以下是一個實務中的範例,從清單中提取使用者名稱和電子郵件地址:

john_doe: john.doe@example.com
jane_smith: jane.smith@example.com

regex_pattern='([^:]+): ([^@]+@[^ ]+)'
while IFS= read -r line; do
    if [[ $line =~ $regex_pattern ]]; then
        echo "Username: ${BASH_REMATCH[1]}"
        echo "Email: ${BASH_REMATCH[2]}"
    fi
done < user_list.txt

這樣我們就可以提取清單中的使用者名稱和電子郵件地址了。此圖示展示瞭如何利用捕捉群組來分離資料。

內容解密:

這段程式碼中的主要概念是利用捕捉群組來提取特定格式中的資料。首先我們定義了一個正規表示式模式來比對清單中的每一行。在這個模式中,([^:]+) 比對冒號之前的所有字元(即使用者名稱),而 ([^@]+@[^ ]+) 比對電子郵件地址。當找到比對時,我們使用 ${BASH_REMATCH[1]}${BASH_REMATCH[2]} 來提取這些群組中的內容。最後我們將這些內容印出來。

這些技巧和範例展示瞭如何利用正規表示式來處理和提取文字資料。透過理解和應用這些概念,我們可以更高效地進行文字操作和資料處理。