在文件C中,通过使用文件B的模式,查找并替换文件A的模式。

5

I have two files, fileA with a list of name :

AAAAA 
BBBBB
CCCCC
DDDDD

还有另一个名为文件B的文件,其中包含另一个列表:

111 
222
333
444

还有第三个文件C,其中包含一些文本:

Hello AAAAA toto BBBBB dear "AAAAA" trird BBBBBB tuizf AAAAA dfdsf CCCCC

我需要在文件C中找到并替换每个文件A模式为文件B模式。

这很有效!但是我发现文件C包含诸如“AAAAA”之类的单词,它们没有被替换为“111”。

我正在进行这项工作,但似乎不起作用。

#! /bin/bash
while IFS= read -r lineA && IFS= read -r lineB <&3; do
sed -i -e "s/$lineA/$lineB/g" fileC
done <fileA 3<fileB

你的 while 循环在我的机器上能运行。 - Hari Menon
1
我测试了你的解决方案,它对我有效:Hello 111 toto 222 dear 111 trird 222B tuizf 111 dfdsf 333 - svante
1
或许你只是没有在文件C中找到(-i)。 - svante
它有效!但我意识到文件C包含像“AAAAA”这样的单词,并且没有被替换为“111”。 - Peter Dev
1
@PeterDev 在文件C中的AAAAA没有被替换,因为fileA包含的是AAAAA 而不是AAAAA(请注意末尾的空格)。 - devnull
显示剩余3条评论
3个回答

3

这是一个适合使用 GNU awk 的好工具:

$ cat replace.awk 
FILENAME=="filea" {
    a[FNR]=$0
    next
}
FILENAME=="fileb" {
    b[a[FNR]]=$0
    next
}
{
    for (i=1;i<=NF;i++) {
        printf "%s%s",(b[$i]?b[$i]:$i),(i==NF?RS:FS)
    }
}

演示:
$ awk -f replace.awk filea fileb filec
Hello 111 toto 222 dear 111 trird BBBBBB tuizf 111 dfdsf 333

一个sehe的解决方案:

FILENAME==ARGV[1] {              # Read the first file passed in
    find[FNR]=$0                 # Create a hash of words to replace
    next                         # Get the next line in the current file
}
FILENAME==ARGV[2] {              # Read the second file passed in
    replace[find[FNR]]=$0        # Hash find words by the words to replace them 
    next                         # Get the next line in the current file
}
{                                # Read any other file passed in (i.e third)
    for (i=1;i<=NF;i++) {        # Loop over all field & do replacement if needed
        printf "%s%s",(replace[$i]?replace[$i]:$i),(i==NF?RS:FS)
    }
}

对于替换操作,忽略单词边界:

$ cat replace.awk 
FILENAME==ARGV[1] {
    find[FNR]=$0
    next
}
FILENAME==ARGV[2] {
    replace[find[FNR]]=$0
    next
}
{
    for (word in find)
        gsub(find[word],replace[find[word]])
    print
}

演示:

$ awk -f replace.awk filea fileb filec
Hello 111 toto 222 dear "111" trird 222B tuizf 111 dfdsf 333

1
我一直很惊讶的是,经过多年的接触之后,“awk”仍然无法在我的脑海中留下任何印象。我的意思是,它总是看起来像是完成工作的工具,但我真的不能理解它(FNR?NF,RS,FS?)此外,当脚本中已经将“filea”和“fileb”硬编码时,为什么它们仍然在命令行中呢?对我来说完全陌生。 - sehe
对我来说这很自然...你可能已经用记录和字段的术语描述数据,所以FS表示字段分隔符RS表示记录分隔符NF表示字段数量是相当合理的。你可以根据位置匹配文件并使用argv,但使用名称更易读在我看来,当然你仍然需要传递每个文件的句柄。 - Chris Seymour
我的脚本运行正常!但我发现文件C包含像“AAAAA”这样的单词,并且它没有被替换为“111”。有什么想法吗? - Peter Dev
@PeterDev 我添加了一个脚本,可以在不考虑单词边界的情况下进行替换。 - Chris Seymour
谢谢,干得好,但它似乎在“~/test# awk -f replace.awk fileA fileB fileC”上无法工作。 111 toto 222 亲爱的“AAAAA” trird 222B tuizf 111 dfdsf 333 - Peter Dev
@PeterDev请检查fileafileb是否不包含尾随空格。 - Chris Seymour

2
sed 's/.*/s/' fileA | paste -d/ - fileA fileB | sed 's/$/\//' | sed -f - fileC

正确且更快的版本应该是

paste -d/ fileA fileB | sed 's/^/s\//;s/$/\/g/' | sed -f - fileC

1
一枚两级火箭:

sed -e "$(paste file[AB] | sed 's/\(.*\)\t\(.*\)/s\/\1\/\2\/g;/')" fileC 

这个操作会使用 paste file[AB] | sed 's/\(.*\)\t\(.*\)/s\/\1\/\2\/g;/' 创建一个临时的sed脚本:
s/AAAAA/111/g;
s/BBBBB/222/g;
s/CCCCC/333/g;
s/DDDDD/444/g;

然后使用fileC作为输入运行它。

@hipe 我没有注意到。无论如何,我的版本也有限制(fileA/fileB 不能包含制表符)。 - sehe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接