五個(gè)絕妙的 Bash 字符串操作方法,每個(gè)開(kāi)發(fā)者都應(yīng)該掌握
?Bash 成為了每個(gè)類 Unix 或基于 Unix 的操作系統(tǒng)的默認(rèn)自動(dòng)化語(yǔ)言。每個(gè)系統(tǒng)管理員、DevOps 工程師和程序員通常使用 Bash 編寫(xiě)具有重復(fù)命令序列的 shell 腳本。Bash 腳本通常包含運(yùn)行其他程序二進(jìn)制文件的命令。在大多數(shù)情況下,我們可能需要在 shell 腳本中處理數(shù)據(jù)并創(chuàng)建邏輯流程。因此,我們經(jīng)常需要在 shell 腳本中添加條件語(yǔ)句和文本操作語(yǔ)句。
?傳統(tǒng)的 Bash 腳本和使用舊版本 Bash 解釋器的過(guò)去的程序員通常使用 awk、sed、tr 和 cut 命令進(jìn)行文本操作。這些是單獨(dú)的程序。盡管這些文本處理程序提供了良好的功能,但它們會(huì)減慢您的 Bash 腳本,因?yàn)槊總€(gè)特定命令都具有相當(dāng)?shù)倪M(jìn)程生成時(shí)間?,F(xiàn)代 Bash 版本通過(guò)著名的參數(shù)擴(kuò)展功能提供了內(nèi)置的文本處理功能。
在本文中,我將解釋一些內(nèi)置的字符串操作語(yǔ)法,您可以使用這些語(yǔ)法在 Bash 腳本中高效地處理文本。
子字符串提取和替換
子字符串是指特定字符串的連續(xù)片段或部分。在各種腳本編寫(xiě)場(chǎng)景中,我們需要從字符串片段中提取子字符串。例如,您可能需要僅從包含擴(kuò)展名的完整文件名中獲取文件名部分。此外,您可能需要使用特定字符串段替換子字符串(例如,更改文件名的文件擴(kuò)展名)。
提取子字符串非常容易,只需提供字符位置和長(zhǎng)度:
你甚至可以從右邊進(jìn)行子字符串計(jì)算,如下所示:
Bash 還提供了一種高效的內(nèi)置語(yǔ)法來(lái)進(jìn)行子字符串替換:
當(dāng)你處理一些字符串時(shí),例如文件名、路徑等,你可能需要替換字符串的前綴和后綴。將一個(gè)文件擴(kuò)展名替換為另一個(gè)擴(kuò)展名就是一個(gè)很好的例子??聪旅娴睦樱?/p>
在上面的子字符串替換示例中,我們使用了確切的子字符串段進(jìn)行匹配,但您還可以使用 * 通配符字符來(lái)使用子字符串的一部分,如下所示:
如果您不知道要搜索的確切子字符串,上述方法很有用。
正則表達(dá)式匹配、提取和替換
許多 Unix 或 GNU/Linux 用戶已經(jīng)知道,可以使用 grep 和 sed 進(jìn)行基于正則表達(dá)式的文本搜索。sed 幫助我們進(jìn)行正則表達(dá)式替換。你可以使用內(nèi)置的 Bash 正則表達(dá)式功能來(lái)處理文本,比使用這些外部二進(jìn)制文件更快。
你可以使用 if 條件和 =~ 操作符執(zhí)行正則表達(dá)式匹配,如下面的代碼片段所示:
如果你想的話,也可以用內(nèi)聯(lián)條件語(yǔ)句來(lái)替換 if 語(yǔ)句,如下所示:
一旦 Bash 解釋器執(zhí)行了一個(gè)正則表達(dá)式匹配,它通常會(huì)將所有匹配結(jié)果存儲(chǔ)在 BASH_REMATCH shell 變量中。這個(gè)變量是一個(gè)只讀數(shù)組,并將整個(gè)匹配的數(shù)據(jù)存儲(chǔ)在第一個(gè)索引中。如果使用子模式,則 Bash 會(huì)逐步將這些匹配項(xiàng)存儲(chǔ)在其他索引中:
記得我們之前在子字符串匹配中使用了通配符嗎?類似地,可以在參數(shù)擴(kuò)展中使用正則表達(dá)式定義,如下面的例子所示:
子字符串刪除技巧
我們?cè)谠S多文本處理需求中經(jīng)常需要預(yù)處理文本段,以刪除不需要的子字符串。例如,如果您提取了一個(gè)帶有 v 前綴和一些構(gòu)建編號(hào)的版本號(hào),并想找到主要版本號(hào),則必須刪除一些子字符串。您可以使用相同的子字符串替換語(yǔ)法,但省略替換字符串參數(shù)以進(jìn)行字符串刪除,如下所示:
在上面的示例中,我們使用了精確的子字符串和通配符進(jìn)行子字符串刪除,但是您還可以使用正則表達(dá)式??纯慈绾翁崛∫粋€(gè)不帶冗余字符的干凈版本號(hào):
大小寫(xiě)轉(zhuǎn)換和基于大小寫(xiě)的變量
即使是標(biāo)準(zhǔn)的 C 語(yǔ)言也提供了一個(gè)函數(shù)來(lái)轉(zhuǎn)換字符的大小寫(xiě)。幾乎所有現(xiàn)代編程語(yǔ)言都提供了內(nèi)置函數(shù)來(lái)進(jìn)行大小寫(xiě)轉(zhuǎn)換。作為一種命令語(yǔ)言,Bash 不提供大小寫(xiě)轉(zhuǎn)換的函數(shù),但它通過(guò)參數(shù)擴(kuò)展和變量聲明為我們提供了大小寫(xiě)轉(zhuǎn)換的功能。
請(qǐng)看下面的示例,它將字母的大小寫(xiě)進(jìn)行轉(zhuǎn)換:
你也可以只將字符串的第一個(gè)字符大寫(xiě)或小寫(xiě),如下所示:
如果您需要使特定變量嚴(yán)格大寫(xiě)或小寫(xiě),您不需要每次都運(yùn)行一個(gè)大小寫(xiě)轉(zhuǎn)換函數(shù)。相反,您可以使用內(nèi)置的declare命令為特定變量添加大小寫(xiě)屬性,如下面的示例所示:
上面的 ver1 和 ver2 變量在聲明時(shí)接收到了大小寫(xiě)屬性,因此每當(dāng)你為一個(gè)特定的變量分配一個(gè)值時(shí),Bash 會(huì)根據(jù)變量屬性轉(zhuǎn)換文本大小寫(xiě)。
拆分字符串(字符串到數(shù)組的轉(zhuǎn)換)
Bash 允許你使用 declare 內(nèi)置函數(shù)定義索引和關(guān)聯(lián)數(shù)組。大多數(shù)通用編程語(yǔ)言提供了在字符串對(duì)象中或通過(guò)標(biāo)準(zhǔn)庫(kù)函數(shù)中拆分方法(例如 Go 的 strings.Split 函數(shù))。在 Bash 中,你可以使用多種方法拆分一個(gè)字符串并創(chuàng)建一個(gè)數(shù)組。例如,我們可以將 IFS 更改為所需的分隔符并使用 read 內(nèi)置函數(shù),或者我們可以使用 tr 命令和循環(huán)構(gòu)建數(shù)組,另外使用內(nèi)置參數(shù)展開(kāi)也是一種方法。在 Bash 中有很多字符串拆分方法。
使用 IFS 和 read 是最簡(jiǎn)單和無(wú)誤的拆分字符串的方法之一:
上面的代碼片段使用,作為分隔符,并使用內(nèi)置的read命令基于IFS創(chuàng)建一個(gè)數(shù)組。
即使有最簡(jiǎn)單的方法可以在不使用read的情況下處理拆分,但要確保沒(méi)有隱藏的問(wèn)題。例如,以下拆分實(shí)現(xiàn)非常簡(jiǎn)單,但當(dāng)您將*(擴(kuò)展為當(dāng)前目錄的內(nèi)容)作為元素,空格作為分隔符時(shí),它會(huì)出現(xiàn)問(wèn)題: