2012-10-25 101 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