在多个文件中替换多个字符串

时间:2018-07-31 07:54:36

标签: bash awk

我有一个文件,其中包含以下格式的正则表达式和替换文字字符串的列表:

OLD_REGEXP_1 NEW_STRING_1
OLD_REGEXP_2 NEW_STRING_2
...

我想用多个文件OLD_REGEXP_X中的NEW_STRING_X替换所有与*.txt匹配的字符串。

我相信这是一个常见的问题,有人以前应该已经做过类似的事情,但我只是找不到使用bash编写的现有解决方案。

例如:

Tom Thompson
Billy Bill&Ted
goog1e\.com google.com
https?://www\.google\.com https://google.com

输入:

Tom and Billy are visiting http://www.goog1e.com

预期输出:

Thompson and Bill&Ted are visiting https://google.com

主要挑战是:

  • 要替换的字符串由POSIX扩展正则表达式而不是文字描述,并且必须是非POSIX ERE元字符的任何字符,包括/,某些工具通常将其用作正则表达式定界符视为文字。
  • 替换字符串是文字的,并且可以包含任何文字字符,包括&\1之类的字符,这些字符通常用作替换字符串中的反向引用元字符,但在这种情况下必须为文字。
  • 替换必须按照它们在映射文件中出现的顺序发生,因此,如果我们在映射文件中具有该顺序的A-> B和B-> C,并且A出现在要更改的文本文件中,则输出将包含“ C”代替“ A”,而不是“ B”。

2 个答案:

答案 0 :(得分:1)

您可以将替换列表文件转换为sed脚本文件,然后让sed为您完成这项工作。

尝试使用gnu sed:

sed -i -f <(sed -r 's/^(\S*) (.*)/s@\1@\2@/g' listfile) *.txt

答案 1 :(得分:1)

考虑到您到目前为止告诉我们的内容,并考虑了评论中所说的所有内容以及问题中涉及的内容以及我可能想到的所有可能的字符串,这些字符串目前并未包含在您的示例中,但是可能会出现(字符串除外)包含空格-您必须告诉我们如何在mapfile中识别旧的还是新的),听起来这就是您所需要的:

$ cat mapfile
Tom Thompson
Billy Bill&Ted
goog1e\.com google.com
https?://www\.google\.com https://google.com

$ cat textfile
Tom and Billy are visiting http://www.goog1e.com

awk '
NR==FNR {
    old[NR] = $1
    gsub(/&/,RS,$2)
    new[NR] = $2
    next
}
{
    for (i=1; i in old; i++) {
        gsub(old[i],new[i])
    }
    gsub(RS,"\\&")
    print
}
' mapfile textfile
Thompson and Bill&Ted are visiting https://google.com

以上内容将“旧字符串”视为正则表达式,将“新字符串”视为不带反向引用的文字字符串,并严格按照输入文件中定义的顺序应用替换。

第一个gsub()将替换字符串中的每个&转换为一个记录分隔符(由于我们在一条记录内进行操作,因此无法显示),因此第二个gsub()将不处理{{1 }}中的新字符串(例如反向引用),然后第三个gsub()会将RS放回&

以上内容可在任何UNIX系统上的任何Shell中使用任何awk进行工作。