使用sed / awk / tr / perl将字符串替换为小写的子字符串?

时间:2012-10-25 17:10:05

标签: perl sed awk tr

我有一个包含多个模式$$DATABASE_*$$实例的纯文本文件,星号可以是任何字符串。我想用星号部分中的任何内容替换整个实例,但是小写。

这是一个测试文件:

$$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执行此操作?

9 个答案:

答案 0 :(得分:3)

这是我最终使用的perl版本。

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

答案 1 :(得分:1)

这个用复杂的例子。

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

对于更简单的例子:

echo '$$DATABASE_GIBSON$$' | sed 's@$$DATABASE_\(.*\)\$\$@\L\1@'
中的

\L表示小写(如果需要,\E停止)

答案 2 :(得分: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)中否定该char,如果没有(即如果我们使用“。+”而不是“[^ \ n] +”)那么由于贪婪的RE匹配,如果相同的模式在一个输入行上出现两次,匹配的字符串将从第一个模式的开头延伸到第二个模式的结尾。

答案 3 :(得分:0)

单独使用awk:

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

请注意我在FreeBSD中,所以这不是GNU awk。

但这可以单独使用bash来完成:

[ghoti@pc ~]$ foo='$$DATABASE_AWESOME$$'
[ghoti@pc ~]$ foo=${foo##*_}
[ghoti@pc ~]$ foo=${foo%\$\$}
[ghoti@pc ~]$ foo=${foo,,}
[ghoti@pc ~]$ echo $foo
awesome

在上述替换中,除了最后一个(${foo,,})之外的所有替换都将在标准Bourne shell中起作用。如果您没有bash,则可以使用tr执行此步骤:

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

<强>更新

根据评论,似乎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

请注意,这三个更新的解决方案中的任何一个都不会处理输入中的多个标记数据库名称的情况。这也不是问题的要求,但我只是说'....

答案 4 :(得分:0)

你可以通过supercool命令cut以非常简单的方式执行此操作:)

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

答案 5 :(得分:0)

这可能适合你(GNU sed):

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

答案 6 :(得分:0)

这是我能提出的最短(GNU)awk解决方案,可以完成OP所要求的一切:

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

即使用星号(*)表示的字符串包含一个或多个单个美元符号($)和/或换行符,这个灵魂分子仍应有效。

答案 7 :(得分:0)

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

test me gibson test me

gibson test gibson test

gibson gibsongibson

答案 8 :(得分:-1)

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

awk会接受任何输入,在这种情况下是第一次agurment,并使用tolower函数并返回结果。

对于您的bash脚本,您可以执行类似的操作并使用变量DBLOWER

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