4

我有一个包含多个模式实例的纯文本文件$$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 做到这一点?

4

9 回答 9

3

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

perl -p -i.bak -e 's/\$\$DATABASE_(.*?)\$\$/lc($1)/eg' inputFile
于 2012-10-25T19:25:49.093 回答
1

这适用于复杂的示例。

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

对于更简单的示例:

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

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

于 2012-10-25T17:16:44.293 回答
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 匹配如果相同的模式在一个输入行上出现两次,则匹配字符串将从第一个模式的开头延伸到第二个模式的结尾。

于 2012-10-25T19:47:35.773 回答
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/pcreFreeBSD 端口),你可以使用它来代替,带有前瞻:

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

(对于阅读本文的 Linux 用户:这相当于使用grep -P.)

在纯 bash 中:

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

请注意,这三个更新的解决方案中没有一个可以处理在同一行输入中存在多个标记数据库名称的情况。这也不是问题中的要求,但我只是说......

于 2012-10-25T17:48:38.547 回答
0

您可以使用 supercool 命令 cut 以非常简单的方式执行此操作 :)

echo '$$DATABASE_AWESOME$$' | cut -d'$' -f3 | cut -d_ -f2 | tr 'A-Z' 'a-z'
于 2012-10-25T19:59:15.523 回答
0

这可能对您有用(GNU sed):

sed 's/$\$/\n/g;s/\nDATABASE_\([^\n]*\)\n/\L\1/g;s/\n/$$/g' file
于 2012-10-26T08:29:14.363 回答
0

这是我能想到的最短(GNU)awk解决方案,它可以满足 OP 的要求:

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

即使用星号 ( ) 指示的字符串*包含一个或多个美元符号 ( $) 和/或换行符,这个灵魂仍然应该工作。

于 2013-08-28T10:10:31.737 回答
0
awk '{gsub(/\$\$DATABASE_GIBSON\$\$/,"gibson")}1' file
gibson

test me gibson test me

gibson test gibson test

gibson gibsongibson
于 2016-05-01T00:08:55.410 回答
-1

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

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

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

DBLOWER=$(echo $$DATABASE_WOOLY$$ | awk '{print tolower($0)}');
于 2012-10-25T17:22:47.983 回答