Text-Processing

awk 對 gsub 不區分大小寫

  • September 11, 2021

我已經"|"分隔了文本數據,並且想要轉換列值

$ cat infile
Mark|father
Jason|SOn
Jose|son
Steffy|daugHter

我想不區分大小寫地搜尋 (father|son|daughter) 並將任何情況下的父親替換為父親,任何情況下的兒子替換為兒子,任何情況下的女兒替換為女兒

所以outfile應該是這樣的

$ cat outfile
Mark Father
Jason Son
Jose Son
Steffy Daughter

我正在嘗試 IGNORECASE 與 sub 或 gsub 的不同組合,但它會按 infile 中的方式列印所有條目

我會使用雜湊查找而不是正則表達式比較和 *sub() 來提高效率和穩健性(如果您決定使用包含正則表達式元字元或反向引用的字元串,或者可以是其他字元串的子字元串):

$ cat tst.awk
BEGIN {
   FS = "|"
   split("Father|Son|Daughter",tmp)
   for (i in tmp) {
       map[tolower(tmp[i])] = tmp[i]
   }
}
{ lc = tolower($2) }
lc in map {
   $2 = map[lc]
}
{ print }
$ awk -f tst.awk file
Mark Father
Jason Son
Jose Son
Steffy Daughter

這是嘗試回答問題的原始版本。從那以後,要求發生了變化。

這是 GNU 實現sed擅長的一件事:

$ sed -E 's/(^|\s)(son|daughter|father)(\s|$)/\1\L\u\2\3/i' < file
Mark Father
Jason Son
Jose Son
Steffy Daughter

正則表達式匹配這 3 個單詞中的任何一個,但前提是它們既沒有前後也沒有非空格。

\L將整個單詞變成小寫,\u只有第一個字元變成大寫(那些來自ex/vi來自 70 年代,但不幸的是沒有達到標準sed)。

這同樣適用於perl -pe代替sed -E(使其可能perl比 GNU更多的系統更可移植sed),但perl您可以將其簡化為:

perl -pe 's/(?<!\S)(son|daughter|father)(?!\S)/\L\u$&/i'

也就是說,使用否定的環視運算符來確保這些字元串不是較長的以空格分隔的單詞的一部分(例如Jason在您的輸入中)。另請參見\binperl\<, \>in sed ,單詞邊界運算符,但這些運算符更像(?!\w)是會將 grand-son 轉換為 grand-Son 例如,因為-它不是單片語成字元。

那些只替換每行最多一次。要替換所有出現,您可以將g標誌添加到perl上面的標誌。將其添加到sed其中可能會錯過一些,例如 a Mark son SON sOn,第一個匹配項將替換" son "" Son ",然後sed將繼續搜尋"SON sOn",因此找不到\sbefore的匹配項SON。這可以通過事先將所有空白字元加倍並在之後恢復來解決:

sed -E 's/\s/&&/g
       s/(^|\s)(son|daughter|father)(\s|$)/\1\L\u\2\3/ig
       s/(\s)\1/\1/g'

雖然這開始有點太複雜了。

引用自:https://unix.stackexchange.com/questions/664357