2012-10-25 279 views
4

我有一個明文文件,其中包含模式$$DATABASE_*$$的多個實例,星號可以是任何字符串。我想用星號部分中的任何內容替換整個實例,但使用小寫。使用sed/awk/tr/perl以小寫字符串替換字符串?

下面是測試文件:

$$DATABASE_GIBSON$$ 

test me $$DATABASE_GIBSON$$ test me 

$$DATABASE_GIBSON$$ test $$DATABASE_GIBSON$$ test 

$$DATABASE_GIBSON$$ $$DATABASE_GIBSON$$$$DATABASE_GIBSON$$ 

下面是所需的輸出:

gibson 

test me gibson test me 

gibson test gibson test 

gibson gibsongibson 

如何做到這一點與SED/AWK/TR/perl的?

+0

http://stackoverflow.com/q/4569825/318716 –

+0

http://stackoverflow.com/q/689495/318716 –

回答

3

這是我最終使用的perl版本。

perl -p -i.bak -e 's/\$\$DATABASE_(.*?)\$\$/lc($1)/eg' inputFile 
+0

的確很好的解決方案。請注意,如果'*'包含換行符,它將不起作用。 – mschilli

1

這是一個複雜的例子。

perl -ple 's/\$\$DATABASE_(.*?)\$\$/lc($1)/eg' filename.txt 

而對於簡單的例子:

echo '$$DATABASE_GIBSON$$' | sed '[email protected]$$DATABASE_\(.*\)\$\[email protected]\L\[email protected]' 

\L意味着更低的情況下(\E停止,如果需要的話)

+0

'\ L'並不在我的Mac即的Mac OS 10.6。 8 – anubhava

+0

不完全。我正在使用此測試文件:http://pastebin.com/Q6RvvdcD 輸出如下所示:http://pastebin.com/CBe0Mehb – DynamiteReed

+0

添加了perl便攜式解決方案。 –

-1

echo $$DATABASE_WOOLY$$ | awk '{print tolower($0)}'

awk將採取什麼樣的不斷投入,這種情況下的第一個agurment,並使用tolower函數並返回結果。

爲了您的bash腳本,你可以做這樣的事情,並使用可變DBLOWER

DBLOWER=$(echo $$DATABASE_WOOLY$$ | awk '{print tolower($0)}'); 
+0

根據OP的要求,這並不是通過'*'替代'$$ DATABASE _ * $$'。它也會將全部*輸入轉換爲小寫。 – mschilli

0

使用單獨的awk:

> echo '$$DATABASE_AWESOME$$' | awk '{sub(/.*_/,"");sub(/\$\$$/,"");print tolower($0);}' 
awesome 

請注意,我在FreeBSD下我,所以這不是GNU AWK。

但這可以單獨使用bash來完成:

[[email protected] ~]$ foo='$$DATABASE_AWESOME$$' 
[[email protected] ~]$ foo=${foo##*_} 
[[email protected] ~]$ foo=${foo%\$\$} 
[[email protected] ~]$ foo=${foo,,} 
[[email protected] ~]$ echo $foo 
awesome 

上述換人,所有除了最後一個(${foo,,})將在標準Bourne shell中運行。如果你沒有bash中,你可以做,而不是使用tr此步驟:

$ echo $foo 
AWESOME 
$ foo=$(echo "$foo" | tr '[:upper:]' '[:lower:]') 
$ echo $foo 
awesome 
$ 

UPDATE

每評論,似乎什麼OP 真的想要的是剝除在之外的任何文字中包含的子串 - 也就是說,我們的解決方案需要考慮在他的問題中提供的字符串之前或之後的前導或尾隨空格的可能性。

> echo 'foo $$DATABASE_KITTENS$$ bar' | sed -nE '/\$\$[^$]+\$\$/{;s/.*\$\$DATABASE_//;s/\$\$.*//;p;}' | tr '[:upper:]' '[:lower:]' 
kittens 

如果你碰巧有pcregrep您的路徑(從devel/pcre FreeBSD的端口)上,您可以使用替代,以向前看符號:

> echo 'foo $$DATABASE_KITTENS$$ bar' | pcregrep -o '(?!\$\$DATABASE_)[A-Z]+(?=\$\$)' | tr '[:upper:]' '[:lower:]' 
kittens 

(對於Linux用戶閱讀本:這是相當於使用grep -P。)

而且在純擊:

$ shopt -s extglob 
$ foo='foo $$DATABASE_KITTENS$$ bar' 
$ foo=${foo##*(?)\$\$DATABASE_} 
$ foo=${foo%%\$\$*(?)} 
$ foo=${foo,,} 
$ echo $foo 
kittens 

注意,這三個更新解決方案都不會處理,其中多個標記在同一線路輸入存在數據庫名稱的情況。這不是在這個問題中的要求,但我只是在說'...

+0

關閉,但不完全與awk。輸入:http://pastebin.com/Q6RvvdcD 輸出:http://pastebin.com/66HLeqgt – DynamiteReed

+0

這些樣本不包括在您的問題中。我回答了發佈的問題。 – ghoti

+0

@ BlueJ774 - 用您的新要求更新了我的答案。你可能想更明確[在你的問題](http://stackoverflow.com/posts/13073727/edit),以避免混淆。 – ghoti

1

不幸的是使用awk不容易的,萬無一失的方法,但這裏有一個方法:

$ cat tst.awk 
{ 
    gsub(/[$][$]/,"\n") 

    head = "" 
    tail = $0 

    while (match(tail, "\nDATABASE_[^\n]+\n")) { 
     head = head substr(tail,1,RSTART-1) 
     trgt = substr(tail,RSTART,RLENGTH) 
     tail = substr(tail,RSTART+RLENGTH) 

     gsub(/\n(DATABASE_)?/,"",trgt) 

     head = head tolower(trgt) 

    } 

    $0 = head tail 

    gsub("\n","$$") 

    print 
} 

$ cat file 
The quick brown $$DATABASE_FOX$$ jumped over the lazy $$DATABASE_DOG$$s back. 
The grey $$DATABASE_SQUIRREL$$ ate $$DATABASE_NUT$$s under a $$DATABASE_TREE$$. 
Put a dollar $$DATABASE_DOL$LAR$$ in the $$ string. 

$ awk -f tst.awk file 
The quick brown fox jumped over the lazy dogs back. 
The grey squirrel ate nuts under a tree. 
Put a dollar dol$lar in the $$ string. 

注意轉換$$到一個新行字符,所以我們可以否定該字符在比賽的訣竅( RE),沒有那個(即如果我們使用「。+」而不是「[^ \ n] +」),那麼由於貪婪的RE匹配,如果相同的模式在一個輸入行上出現兩次,匹配字符串將從第一種模式到第二種模式結束。

+0

不錯的代碼。你介意評論[我的解決方案](http://stackoverflow.com/a/18484993/2451238)?我想我很少用('g')'awk'解決這個問題。它甚至應該在'*'字符串中使用換行符。但也許我錯了。在這種情況下,我想從這個角度出發。 :) – mschilli

+0

它不會從問題中的樣本輸入產生預期的輸出。 –

+0

對我而言它確實如此。你用過GNU'awk''gawk'嗎? IIRC,POSIX'awk'不支持正則表達式(RE)記錄分隔符(RS)。如果你使用'gawk'測試它,你有什麼輸出和你使用了哪個版本? – mschilli

0

您可以在一個漂亮的萬無一失的方法與過冷切命令:)

echo '$$DATABASE_AWESOME$$' | cut -d'$' -f3 | cut -d_ -f2 | tr 'A-Z' 'a-z' 
0

這可能爲你工作(GNU SED)做到這一點:

sed 's/$\$/\n/g;s/\nDATABASE_\([^\n]*\)\n/\L\1/g;s/\n/$$/g' file 
0

這裏是最短的(GNU )awk解決方案,我可以拿出,做一切由OP要求:

awk -vRS='[$][$]DATABASE_([^$]+[$])+[$]' '{ORS=tolower(substr(RT,12,length(RT)-13))}1' 

即使用星號(*)表示的字符串包含一個或多個單一美元符號($)和/或換行符,此靈魂提示仍應起作用。

0
awk '{gsub(/\$\$DATABASE_GIBSON\$\$/,"gibson")}1' file 
gibson 

test me gibson test me 

gibson test gibson test 

gibson gibsongibson