2012-10-24 77 views
90

我已經看到了這個例子:搜索和在bash替換使用正則表達式

hello=ho02123ware38384you443d34o3434ingtod38384day 
echo ${hello//[0-9]/} 

哪些語法如下:${variable//pattern/replacement}

不幸的是,pattern領域似乎並不支持完整的regex語法(如我使用.\s,例如,它試圖匹配文字字符)。

如何使用完整正則表達式語法搜索/替換字符串?

+0

找到一個相關的問題在這裏:http://stackoverflow.com/questions/5658085/bash-script-regular-expressions-how-to-find-and-replace-all-matches – jheddings

+1

僅供參考,'\ s'不是標準POSIX定義的正則表達式語法(BRE或ERE)的一部分;這是一個PCRE擴展,並且大部分不可用於shell。 '[[:space:]]'是更通用的等價物。 –

+1

'\ s'可以用'[[:space:]]'替換,'''用'''替換',基線shell模式語言的extglob擴展可以用於像可選子組這樣的事物,重複團體等。 –

回答

101

使用sed

MYVAR=ho02123ware38384you443d34o3434ingtod38384day 
echo $MYVAR | sed -e 's/[a-zA-Z]/X/g' -e 's/[0-9]/N/g' 
# prints XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX 

注意,隨後-e的按順序處理。此外,該表達式的g標誌將匹配輸入中的所有匹配項。

您也可以使用這種方法,即Perl中,awk中挑選自己喜歡的工具,如:

echo $MYVAR | perl -pe 's/[a-zA-Z]/X/g and s/[0-9]/N/g' 

這可以讓你做更多的創意匹配...例如,在剪斷上述情況,除非第一個表達式匹配,否則不會使用數字替換(由於惰性and評估)。當然,你有充分的語言Perl支持聽從你的命令......

+0

據我所知,這隻能替代一個。有沒有辦法讓它像我發佈的代碼那樣代替模式的所有發生? – Lanaru

+0

我已更新我的答案,以演示多個替換以及全局模式匹配。讓我知道這是否有幫助。 – jheddings

+0

非常感謝!出於好奇,爲什麼你從一行版本(在你的原始答案中)切換到雙行版本? – Lanaru

52

這些例子在bash工作也沒有必要使用SED:

#!/bin/bash 
MYVAR=ho02123ware38384you443d34o3434ingtod38384day 
MYVAR=${MYVAR//[a-zA-Z]/X} 
echo ${MYVAR//[0-9]/N} 

你也可以使用字符類支架表達式

#!/bin/bash 
MYVAR=ho02123ware38384you443d34o3434ingtod38384day 
MYVAR=${MYVAR//[[:alpha:]]/X} 
echo ${MYVAR//[[:digit:]]/N} 

輸出

XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX 

@Lanaru想知道什麼豪ver,如果我正確地理解了這個問題,爲什麼「完整」或PCRE擴展\s\S\w\W\d\D等不能像php ruby​​ python等支持的那樣工作。這些擴展是來自Perl兼容的正則表達式(PCRE),可能不兼容其他形式的基於shell的正則表達式。

這些不工作:

所有文字 「d」 字
#!/bin/bash 
hello=ho02123ware38384you443d34o3434ingtod38384day 
echo ${hello//\d/} 


#!/bin/bash 
hello=ho02123ware38384you443d34o3434ingtod38384day 
echo $hello | sed 's/\d//g' 

輸出取出

ho02123ware38384you44334o3434ingto38384ay 

但下面確實如預期工作

#!/bin/bash 
hello=ho02123ware38384you443d34o3434ingtod38384day 
echo $hello | perl -pe 's/\d//g' 

輸出

howareyoudoingtodday 

。希望澄清的東西多一點,但如果你不感到困惑,爲什麼不試試這個Mac OS X上它啓用了REG_ENHANCED標誌:

#!/bin/bash 
MYVAR=ho02123ware38384you443d34o3434ingtod38384day; 
echo $MYVAR | grep -o -E '\d' 

在* nix你會的最口味僅看到以下輸出:

d 
d 
d 

nJoy!

+5

請原諒? '$ {foo // $ bar/$ baz}'是**不是** POSIX.2 BRE或ERE語法 - 它是fnmatch()風格的模式匹配。 –

+6

...所以,如果我們只想過濾出以字母「o」開頭的數字,那麼'$ {hello // [[:digit:]] /}'可行,'$ {hello // o [[ :digit:]] *}'會有一個完全不同的行爲,因爲在fnmatch模式中,'*'匹配所有字符,而不是修改之前的項目爲0或更多)。 –

+1

有關fnmatch的完整規範,請參閱http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_03(以及它通過引用合併的所有內容)。 –

95

這實際上可以以純bash中完成:

hello=ho02123ware38384you443d34o3434ingtod38384day 
re='(.*)[0-9]+(.*)' 
while [[ $hello =~ $re ]]; do 
    hello=${BASH_REMATCH[1]}${BASH_REMATCH[2]} 
done 
echo "$hello" 

... ...產量

howareyoudoingtodday 
+1

東西告訴我你會愛上這些:http://stackoverflow.com/questions/5624969/how-to-reference-captures-in-bash-regex-replacement#answer-22261643 =) –

+7

這是完全瘋了,真棒在同一時間... – wich

+5

BASH_REMATCH是真棒!感謝您指出! – ricovox

2

使用[[:digit:]](注意雙括號內)的模式:

$ hello=ho02123ware38384you443d34o3434ingtod38384day 
$ echo ${hello//[[:digit:]]/} 
howareyoudoingtodday 

只是想總結答案(特別是@ nickl-https://stackoverflow.com/a/22261334/2916086)。

3

如果您正在重複調用並關注性能,此測試表明BASH方法比分叉sed和其他外部進程可能快15倍。

hello=123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X 

P1=$(date +%s) 

for i in {1..10000} 
do 
    echo $hello | sed s/X//g > /dev/null 
done 

P2=$(date +%s) 
echo $[$P2-$P1] 

for i in {1..10000} 
do 
    echo ${hello//X/} > /dev/null 
done 

P3=$(date +%s) 
echo $[$P3-$P2] 
相關問題