【问题标题】:Merge fileA and fileB with difficult field separator using bash/awk使用 bash/awk 合并文件和文件具有困难的字段分隔符
【发布时间】:2012-11-02 18:31:55
【问题描述】:

类似于我的老问题: How do I merge FileA.txt and FileB.txt giving FileB.txt overwrite power using a bash script?

我想合并两个配置值文件,我还有 fileA 和 fileB。我想要fileA中fileB中的所有行,如果两个文件中出现相同的配置键,我希望fileB中的值覆盖fileA中的值。

每一行总是以'config'开头,然后是一个键,最后是一个值。困难的部分是该值可以是带引号的字符串,带有空格以分隔多个值(请参阅“网站”值)。

我有一些使用 awk 的经验和相当多的 bash 经验,但我一辈子都想不出办法来做到这一点。感谢所有帮助。谢谢

文件A:

config lanIP 10.1.1.1
config wanIP 1.1.1.1
config wanIPMask 255.255.255.255
config website "http://google.com http://yahoo.com"

文件B:

config lanIP 192.168.1.1
config wanIP 1.1.1.1
config website "http://google.com http://yahoo.com"
config moreWebsite "http://google.com http://msn.com"

预期输出:

config lanIP 192.168.1.1
config wanIP 1.1.1.1
config wanIPMask 255.255.255.255
config website "http://google.com http://yahoo.com"
config moreWebsite "http://google.com http://msn.com"

【问题讨论】:

    标签: linux bash configuration merge awk


    【解决方案1】:

    如果您不介意使用 Python,这里有一个小脚本可以做您想做的事情。把它翻译成 awk 应该很简单。总体思路是按顺序处理文件并填充字典,稍后处理的文件中的值会覆盖之前处理的文件中的值:

    import sys
    
    options = {}
    for fileName in sys.argv[1:]:
        with open(fileName) as f:
            for line in f:
                parts = line.strip().split(' ', 2)
                if len(parts) == 3:
                    options[parts[1]] = parts[2]
    
    for k in options:
        print 'config', k, options[k]
    

    您可以这样调用脚本:

    python merge.py fileA fileB
    

    【讨论】:

    • 也是一个很好的解决方案。我试图在这个项目中只使用 bash/awk,所以我不会使用它。但是,我目前正在学习 Python,所以很高兴看到 Python 中的示例。
    • 我很高兴它有用。作为 Python 中的一个练习,您可以对其进行修改,使其像 Kent 的 awk 脚本一样工作。顺便提一句。不要忘记关闭问题。
    【解决方案2】:

    Perl 解决方案:

    #!/usr/bin/perl
    use warnings;
    use strict;
    
    sub get_key_value {
        my $line = shift;
        die "Invalid line $line" unless $line =~ /^config /;
        chomp $line;
        return (split / /, $line, 3)[1, 2];
    }
    
    my %result;
    
    open my $MINOR, '<', 'fileA' or die "Cannot open fileA: $!";
    while (<$MINOR>) {
        my ($key, $value) = get_key_value($_);
        $result{$key} = $value;
    }
    
    open my $MAJOR, '<', 'fileB' or die "Cannot open fileB: $!";
    while (<$MAJOR>) {
        my ($key, $value) = get_key_value($_);
        delete $result{$key};
        print "config $key $value\n";
    }
    
    for my $rest (keys %result) {
        print "config $rest $result{$rest}\n";
    }
    

    【讨论】:

      【解决方案3】:

      这条单线可能会有所帮助:

      awk '{a[$2]=$0}END{for(x in a)print a[x]}' fileA fileB
      

      注意: 上面的行很短,但不保持行的顺序。 (你没有提到排序标准)

      测试:

      kent$  head a b
      ==> a <==
      config lanIP 10.1.1.1
      config wanIP 1.1.1.1
      config wanIPMask 255.255.255.255
      config website "http://google.com http://yahoo.com"
      
      ==> b <==
      config lanIP 192.168.1.1
      config wanIP 1.1.1.1
      config website "http://google.com http://yahoo.com"
      config moreWebsite "http://google.com http://msn.com"
      
      kent$  awk '{a[$2]=$0}END{for(x in a)print a[x]}' a b                       
      config wanIP 1.1.1.1
      config lanIP 192.168.1.1
      config moreWebsite "http://google.com http://msn.com"
      config wanIPMask 255.255.255.255
      config website "http://google.com http://yahoo.com"
      

      如果您想在问题中使用相同的顺序,请尝试以下单行:

      awk '!($2 in a){i[NR]=$2}{a[$2]=$0}END{for(x=1;x<=NR;x++)if(x in i)print a[i[x]]}' a b
      

      测试

      kent$  awk '!($2 in a){i[NR]=$2}{a[$2]=$0}END{for(x=1;x<=NR;x++)if(x in i)print a[i[x]]}' a b
      config lanIP 192.168.1.1
      config wanIP 1.1.1.1
      config wanIPMask 255.255.255.255
      config website "http://google.com http://yahoo.com"
      config moreWebsite "http://google.com http://msn.com"
      

      【讨论】:

      • 使用整行作为值是天才的火花...喜欢它:) 我在 awk 中找不到 string.split Python 函数的 maxsplit 参数的等效项,正在考虑正则表达式,但实际上所有这些解析都不是必需的。太好了。
      • 很好的解决方案。正是我所追求的。
      猜你喜欢
      • 2014-08-22
      • 2019-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多