【问题标题】:Find lines with common words in two files [closed]在两个文件中查找常用单词的行[关闭]
【发布时间】:2016-10-12 08:16:21
【问题描述】:

文件 1

  • apache2-devel-2.0.59-5.2
  • apache2-doc-2.0.59-5.2
  • apache2-example-pages-2.0.59-5.2
  • apache2-mod_perl-2.0.3.99-1.1
  • utempter-32bit-9-200407011229
  • apache2-worker-2.0.59-5.2
  • apache2-prefork-2.0.59-5.2

文件 2

  • apache2-devel-2.2.12-1.38.2
  • apache2-doc-2.2.12-1.40.1
  • apache2-example-pages-2.2.12-1.40.1
  • apache2-mod_perl-2.0.4-40.19
  • utempter-32bit-0.5.5-106.20.1
  • apache2-worker-2.0.59-5.2
  • apache2-prefork-2.0.59-5.2

我想要不同版本的软件列表。例如,除了最后两个之外,所有版本都有不同的版本。顺序可能不同。

我不知道如何继续。

【问题讨论】:

  • 文件是否总是在同一行有相同的软件,只是版本不同?
  • 不,它们可以按任何顺序排列,软件列表也可以不同。我的意思是文件 1 中的内容可能在文件 2 中,也可能不在文件 2 中。但我只需要两个文件中不同版本的内容。
  • 目标是您添加一些自己的代码,以至少展示您为解决这个问题所做的研究工作。
  • 您帖子中的连字符- 是文件的一部分,还是您的意思是创建一个无序列表?请显示文件的真实内容
  • @Borodin 我认为这只是rpm -qa 的输出。有关示例,请参见 here

标签: perl unix sed


【解决方案1】:

我认为 Borodin 对您的 Q 的评论让我朝着正确的方向前进:您显示的文件似乎是 rpm -qa 的输出。

示例:

$ rpm -qa | head -5
aaa_skel-2006.5.19-0.3
glibc-i18ndata-2.4-31.30
release-notes-sles-10-43.51
sles-stor_evms_en-10.1-0.10
yast2-schema-2.13.5-0.13

rpm 的一些开关会影响该输出,即--queryformat 选项。有关详细信息,请参阅此处http://www.rpm.org/max-rpm/s1-rpm-query-parts.html(向下滚动到“标签”部分)。

使用该开关,可以使用自定义字符串定义输出格式和单独的包名称、版本和版本号:

$ rpm -qa --queryformat '%{NAME}\t%{VERSION}\t%{RELEASE}\n' | head -5
aaa_skel    2006.5.19   0.3
glibc-i18ndata  2.4 31.30
release-notes-sles  10  43.51
sles-stor_evms_en   10.1    0.10
yast2-schema    2.13.5  0.13

我在这里使用了标签\t,但任何其他唯一的字符/字符串也可以。

如果您可以以这种方式重写文件列表,那么区分名称和数字会简单,因为简单的split(/\t/) 就可以了。

我知道这不是您问题的完整答案,但如果您能判断rpm -qa --queryformat 是否适合您,我会修改它。 如果我完全错了,请告诉我,我会删除我的帖子。

【讨论】:

  • @quorious:请注意这篇文章。您的问题已经没有任何证据表明您自己解决了这个问题。如果您的数据确实来自rpm,那么您也隐瞒了可以帮助我们解决您的问题的有用信息。人们免费提供时间来帮助像您这样的人,您应该尽力帮助我们为您找到解决方案似乎是公平的
【解决方案2】:

这是我快速整理的内容,至少可以为您提供一个起点。我欺骗了 utempter-32bit-9-200407011229 这样难以解析的条目。

请注意,如果您有非常大的文件,则可能需要采用不同的方法,因为第一个文件会被完整地存储到内存中。

use warnings;
use strict;

open my $fh1, '<', 'f1.txt' or die $!;
open my $fh2, '<', 'f2.txt' or die $!;

my %f1; 

while (<$fh1>){
    chomp;
    next if ! check($_); 
    my ($app, $ver) = separate($_);
    next if ! $app;
    $f1{$app} = $ver;
}

while (<$fh2>){
    chomp;
    next if ! check($_); 
    my ($app, $ver) = separate($_);
    next if ! $app;

    if (exists $f1{$app}){
        if ($ver ne $f1{$app}){
            print "$app version differs\n";
        }
    } 
} 

sub separate { 
    my $line = shift; 
    if (my ($app, $ver) = $line =~ /(.*?)-(\d+\..*)/){
        return ($app, $ver);
    }
}
sub check {
    my $line = shift;
    if ($line !~ /\./){
        print "* can't parse $line, check manually\n";
        return 0;
    }
    return 1;
}

输出:

* can't parse utempter-32bit-9-200407011229, check manually
apache2-devel version differs
apache2-doc version differs
apache2-example-pages version differs
apache2-mod_perl version differs

【讨论】:

  • 我很确定还会有其他极端情况,但示例输入非常简短。
  • 感谢它的大量帮助。我会尝试以我的方式进一步调整逻辑。
  • @stevieb:请不要征求赞成或接受。最好发表评论并附上What should I do when someone answers my question? 的链接,但这应该针对问题,而不是针对任何具体答案。即便如此,我认为最好给 OP 几天时间——尤其是在周末——以防他们更喜欢另一个答案。许多人不愿意删除以前接受的答案,这是可以理解的,所以最好给他们时间来确保他们一开始就选择了正确的答案。
【解决方案3】:

这个程序似乎可以满足您的需要

将软件名称与其版本号分开是任意的,所以最好的猜测是,我使用所有仅包含十进制数字和点 . 的字段作为版本号,并将其前面的所有内容作为名称

use strict;
use warnings 'all';

use Sort::Naturally 'ncmp';

my @files = qw/ file1.txt file2.txt /;

my @info = map { read_file_info($_) } @files;

my @software = do {
    my %sw;
    ++$sw{$_} for map { keys %$_ } @info;
    sort keys %sw;
};

for my $sw ( @software ) {

    print "$sw:\n";

    my @versions = map { $_->{$sw} // '' } @info;

    if ( $versions[0] eq $versions[1] ) {
        printf "    Version %s in both files\n", $versions[0];
    }
    else {
        for my $i ( sort { ncmp($versions[$a], $versions[$b]) } 0 .. $#files ) {
            printf "    %s in %s\n",
                $versions[$i] ? "Version $versions[$i]" : "Doesn't appear",
                $files[$i];
        }
    }

    print "\n";
}


sub read_file_info {
    my ($file) = @_;

    use autodie;

    open my $fh, '<', $file;

    my %info;

    while ( my $line = <$fh> ) {
        next unless /\S/;

        my ( $software, $version ) = split_name_vn($line);
        $info{$software} = $version;
    }

    \%info;
}


sub split_name_vn {
    my ($s) = @_;
    return ( $1, $2 ) if $s =~ /^(.+?)-([\d.-]+)$/;
    return;
}

输出

apache2-devel:
    Version 2.0.59-5.2 in file1.txt
    Version 2.2.12-1.38.2 in file2.txt

apache2-doc:
    Version 2.0.59-5.2 in file1.txt
    Version 2.2.12-1.40.1 in file2.txt

apache2-example-pages:
    Version 2.0.59-5.2 in file1.txt
    Version 2.2.12-1.40.1 in file2.txt

apache2-mod_perl:
    Version 2.0.3.99-1.1 in file1.txt
    Version 2.0.4-40.19 in file2.txt

apache2-prefork:
    Version 2.0.59-5.2 in both files

apache2-worker:
    Version 2.0.59-5.2 in both files

utempter-32bit:
    Version 0.5.5-106.20.1 in file2.txt
    Version 9-200407011229 in file1.txt

【讨论】:

  • 太棒了。这里真正的问题是如何将名称与版本号分开?。您在 split_name_vn 函数中找到了一种非常合理的方法。
  • @PerlDog:确实。但是,正如您在自己的回答中所描述的那样,“正确”的方法是获取数据源以将它们分开。也许我今晚感觉特别糟糕!
【解决方案4】:
$ cat tst.awk
match($0,/[-0-9.]+$/) {
    pkg = substr($0,1,RSTART-1)
    ver = substr($0,RSTART+1)
}
NR==FNR { p2v[pkg]=ver; next }
ver != p2v[pkg]

$ awk -f tst.awk file1 file2
apache2-devel-2.2.12-1.38.2
apache2-doc-2.2.12-1.40.1
apache2-example-pages-2.2.12-1.40.1
apache2-mod_perl-2.0.4-40.19
utempter-32bit-0.5.5-106.20.1

【讨论】:

    猜你喜欢
    • 2016-04-07
    • 1970-01-01
    • 2017-04-18
    • 2017-11-27
    • 2021-10-09
    • 1970-01-01
    • 2020-06-25
    • 1970-01-01
    • 2013-04-29
    相关资源
    最近更新 更多