如何在unix中比较和替换不同行中的字符串

时间:2014-08-01 14:47:50

标签: regex unix awk sed

我想比较并替换unix中不同行中的字符串

例如,我有一个文件,每行有两个单词

<a> <b>
<d> <e>
<b> <c>
<c> <e>

如果任何一行的第二个字与任何其他行的第一个字匹配,那么该行的第二个字应该被匹配行的第二个字替换,并且它应该迭代直到该行的第二个字与第一个字之间不匹配另一行

我需要像

这样的结果
<a> <e>
<b> <e>
<c> <e>
<d> <e>

我是unix的新手,并不知道如何实现它。任何人都可以提出建议或解释我们如何做到这一点

3 个答案:

答案 0 :(得分:3)

这非常明显是递归下降解决方案的一个例子:

$ cat tst.awk
function descend(node) {return (map[node] in map ? descend(map[node]) : map[node])}
{ map[$1] = $2 }
END { for (key in map) print key, descend(key) }

$ awk -f tst.awk file
<a> <e>
<b> <e>
<c> <e>
<d> <e>

如果输入中的无限递归是可能的,这里是一种方法,它将在递归开始之前作为第二个字段打印出最后一个节点,并在它旁边放一个“*”,这样你就知道它正在发生:

$ cat tst.awk
function descend(node,  child, descendant) {
    stack[node]
    child = map[node]
    if (child in map) {
        if (child in stack) {
            descendant = node "*"
        }
        else {
            descendant = descend(child)
        }
    }
    else {
        descendant = child
    }
    delete stack[node]
    return descendant
}
{ map[$1] = $2 }
END { for (key in map) print key, descend(key) }

$ cat file
<w> <w>
<x> <y>
<y> <z>
<z> <x>
<a> <b>
<d> <e>
<b> <c>
<c> <e>

$ awk -f tst.awk file
<w> <w>*
<x> <z>*
<y> <x>*
<z> <y>*
<a> <e>
<b> <e>
<c> <e>
<d> <e>

如果您需要输出订单以匹配输入订单和/或打印重复行两次,请将脚本的底部2行更改为:

{ keys[++numKeys] = $1; map[$1] = $2 }
END {
    for (keyNr=1; keyNr<=numKeys; keyNr++) {
        key = keys[keyNr]
        print key, descend(key)
    }
}

答案 1 :(得分:2)

Perl救援:

#!/usr/bin/perl
use warnings;
use strict;

my (@buff);
sub output {
    my $last = pop @buff;
    print map "$_ $last\n", @buff;
    @buff = ();
}

while (<>) {
    my @F = split;
    output() if @buff and $F[0] ne $buff[-1]; # End of a group.
    push @buff, $F[0] unless @buff;           # Start a new group.
    push @buff, $F[1];
}

output();                                     # Don't forget to print the last buffer.

说明:逐行读取输入。使用相同的第二个单词保留要打印的单词列表。如果第一个字与前一行的第二个字不同,则打印缓冲输出。

答案 2 :(得分:0)

awk '{i++;a[i]=$1;b[i]=$2;next}
      END{
            for(i=1;i in a;i++)
            {
              f=1;
              while (f==1)
              {
                f=0;
                for(j=i+1;j in a;j++)
                {
                  if(b[i]==a[j])
                  {
                    b[i]=b[j];
                    f=1;
                  }
                }
              }
            }
            for(i=1;i in a;i++)
            {
              print a[i],b[i];
            }
          }' input.txt

输入:

<a> <b>
<d> <e>
<b> <c>
<c> <e>

输出:

<a> <e>
<d> <e>
<b> <e>
<c> <e>

输入:

<a> <b>
<e> <z>
<b> <e>

输出:

<a> <z>
<e> <z>
<b> <e>

<小时/> 的修改

如果你需要

<a> <z>
<e> <z>
<b> <z>

作为第二个输入的输出,您可以更改此行:

if(b[i]==a[j])

为:

if(j!=i&&b[i]==a[j])

和此:

for(j=i+1;j in a;j++)

为:

for(j=1;j in a;j++)

另请注意,此代码假定不存在行的第二个单词与行的第一个单词及其第二个单词相同的情况,即:

<a> <b>
<e> <z>
<b> <b>

在这种情况下,代码的执行永远不会结束。