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

使用sed/awk/tr/perl将字符串替换为小写的子字符串?,perl,sed,awk,tr,Perl,Sed,Awk,Tr,我有一个纯文本文件,其中包含模式$$DATABASE\u*$$的多个实例,星号可以是任何字符串。我想用星号部分中的任何内容替换整个实例,但要小写 下面是一个测试文件: $$DATABASE_GIBSON$$ test me $$DATABASE_GIBSON$$ test me $$DATABASE_GIBSON$$ test $$DATABASE_GIBSON$$ test $$DATABASE_GIBSON$$ $$DATABASE_GIBSON$$$$DATABASE_GIBSON$

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

下面是一个测试文件:

$$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实现这一点?

这一点适用于复杂的示例

perl -ple 's/\$\$DATABASE_(.*?)\$\$/lc($1)/eg' filename.txt
更简单的例子是:

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

在中,
\L
表示小写(
\E
在需要时停止)

echo$$DATABASE\u WOOLY$$awk'{print tolower($0)}'

awk将获取任何输入,在本例中为第一次输入,并使用
tolower
函数返回结果

对于bash脚本,您可以这样做,并使用变量
DBLOWER

DBLOWER=$(echo $$DATABASE_WOOLY$$ | awk '{print tolower($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
如果您的路径(从
devel/pcre
FreeBSD端口)上碰巧有
pcregrep
,您可以使用它来代替lookaheads:

> 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

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

这是我最终使用的perl版本

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

不幸的是,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.

请注意将$$转换为换行符的技巧,以便我们可以在不使用该字符的情况下(即,如果我们使用“+”而不是“[^\n]+”)的情况下,在匹配(RE)中否定该字符)然后,由于贪婪的重新匹配,如果同一个模式在一个输入行上出现两次,则匹配字符串将从第一个模式的开头延伸到第二个模式的结尾。

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

这可能适用于您(GNU-sed):

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

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

即使用星号(
*
)表示的字符串包含一个或多个单美元符号(
$
)和/或换行符,此解决方案仍应有效。

不完全有效。我正在使用这个测试文件:输出结果如下:添加了perl可移植解决方案。使用与上面相同的输入文件,使用perl,我得到了这样的结果:这个文件严重干扰了格式化和删除内容。@anubhava-它在OSX上不起作用,因为\L和\E是GNU-sed isms。这个答案适用于大多数Linux环境,但不可移植。仅供参考,我在FreeBSD环境中。接近,但不完全适用于awk。输入:输出:这些样本不包括在您的问题中。我回答了张贴的问题。@BlueJ774-用您的新要求更新了我的答案。您可能希望更加明确以避免混淆。回答不错,但即使是您的更新版本也不符合问题(当前版本)的要求:它将删除所有不转换为小写的输入,而不是按原样输出。代码不错。你介意评论一下吗?我想我用很少的(
g
awk
解决了这个问题。它甚至可以处理
*
字符串中的换行符。但也许我弄错了什么。在这种情况下,我想从中吸取教训。:)它不会从问题中的示例输入中产生预期的输出。对我来说是这样的。您是否使用GNU
awk
gawk
?IIRC,POSIX
awk
不支持正则表达式(RE)记录分隔符(RS)。如果您使用
gawk
对其进行测试,您得到的输出是什么?您使用的是哪个版本?是的,我使用的是gawk 4.1.1。输出的最后一行是没有终止换行符的
gibson gibson
,而不是带有终止换行符的
gibson gibsongibson
。终止换行符丢失,因为在最后一条记录中,
ORS
为空。因此,赋值计算为false,不会触发打印。我通过将作业包装成一个无条件的动作并使用
1
习惯用法添加一个无条件的
print
来解决这个问题。但是,
$$DATABASE\u GIBSON$$$$DATABASE\u GIBSON$$
部分按我的预期转换为
gibsongibson
。你能仔细检查一下我的最新版本是否适合你吗?我在
gawk4.0.2
上,所以从那以后可能有些变化。今天晚些时候我将尝试一个最近的
gawk
。Thx。这并不是按照OP的要求用
*
替换
$$DATABASE\u*$$
。它也会将所有输入转换为小写。这确实是一个不错的解决方案。但是请注意,如果
*
包含换行符,则它将不起作用。
awk '{gsub(/\$\$DATABASE_GIBSON\$\$/,"gibson")}1' file
gibson

test me gibson test me

gibson test gibson test

gibson gibsongibson
sed 's/$\$/\n/g;s/\nDATABASE_\([^\n]*\)\n/\L\1/g;s/\n/$$/g' file
awk '{gsub(/\$\$DATABASE_GIBSON\$\$/,"gibson")}1' file
gibson

test me gibson test me

gibson test gibson test

gibson gibsongibson