【问题标题】:Find content of one file from another file in UNIX在 UNIX 中从另一个文件中查找一个文件的内容
【发布时间】:2013-06-21 11:24:04
【问题描述】:

我有 2 个文件。第一个文件包含数据库中表的元组的行 ID 列表。 第二个文件在查询的“where”子句中包含带有这些行 ID 的 SQL 查询。

例如:

文件 1

1610657303
1610658464
1610659169
1610668135
1610668350
1610670407
1610671066

文件 2

update TABLE_X set ATTRIBUTE_A=87 where ri=1610668350;
update TABLE_X set ATTRIBUTE_A=87 where ri=1610672154;
update TABLE_X set ATTRIBUTE_A=87 where ri=1610668135;
update TABLE_X set ATTRIBUTE_A=87 where ri=1610672153;

我必须读取文件 1 并在文件 2 中搜索与文件 1 中的行 ID 匹配的所有 SQL 命令,并将这些 SQL 查询转储到第三个文件中。

文件 1 有 1,00,000 个条目,文件 2 包含文件 1 的 10 倍,即 1,00,0000。

我使用了grep -f File_1 File_2 > File_3。但这非常慢,而且速度是每小时 1000 个条目。

有没有更快的方法来做到这一点?

【问题讨论】:

  • 是否所有使用文件 1 中提到的行 ID 的查询最终都在同一个输出文件中,或者您希望文件 1 中的每个行 ID 都有一个单独的文件?
  • 是否允许对 ID 上的文件进行排序?还是必须保留订单?
  • 只有一个输出文件。每次文件 1 中的条目与文件 2 中的条目匹配时,文件 2 中的条目应转储到输出文件中。并且文件 1 和文件 2 中的条目是唯一的。任何文件中都没有重复的行。
  • @fizzer 订单无关紧要。我们可以对其进行排序。
  • 您的意思可能是 1,000,000 和 10,000,000 对吧? :-)

标签: file grep


【解决方案1】:

你不需要正则表达式,所以grep -F -f file1 file2

【讨论】:

  • OP说他已经试过这个了,但是太慢了。
  • 我有点感兴趣这如何与JS웃's awk solution 相提并论,但我懒得复制 OP 的条件。
  • 在我的机器上:grep => 38.4s real; awk => 53.8s 实数; file1 19^6 行,file2 10^7 行。
  • 我在我的机器上进行了相同的测试,结果很好用 grep -F,重要的是要注意 -F(captial) 将模式解释为固定字符串列表(而不是常规表达式),由换行符分隔,其中任何一个都将被匹配,这就是为什么与仅使用 -f 的 awk 或常规 grep 相比非常快的原因。希望这会有所帮助!
  • 我为测试计时(time grep -F -f file1 file2)并添加 -F 将我有限的测试集的时间缩短了 10 倍。
【解决方案2】:

awk 的一种方式:

awk -v FS="[ =]" 'NR==FNR{rows[$1]++;next}(substr($NF,1,length($NF)-1) in rows)' File1 File2

这应该很快。在我的机器上,创建 100 万个条目的查找并将其与 300 万行进行比较只需不到 2 秒的时间。

机器规格:

Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz (8 cores)
98 GB RAM

【讨论】:

  • 这个单行字很棒:)
  • 如果您要指定运行命令所需的时间,可能与包含机器规格有关。
  • 你的机器是个野兽。顺便说一句,如果您将; 添加到 FS,即FS="[ =;]",您可以简单地使用$(NF-1) 来获得不带substr 和co 的数字。但不确定它会如何影响性能。
  • @doubleDown 这不是我的个人机器 :) 这是我在工作场所的测试箱。没错,我们也可以这样做......只是不想塞满太多的字段分隔符。
  • @JS웃 这个太棒了。我的机器上消耗的总时间是 46 分钟。机器规格:SunOS 5.10 Generic_127111-03 sun4v sparc SUNW,SPARC-Enterprise-T5120 非常感谢 :)
【解决方案3】:

我建议使用 Perl、Ruby 或 Python 等编程语言。

在 Ruby 中,读取两个文件(f1f2)一次的解决方案可能是:

idxes = File.readlines('f1').map(&:chomp)

File.foreach('f2') do | line |
  next unless line =~ /where ri=(\d+);$/
  puts line if idxes.include? $1
end

或使用 Perl

open $file, '<', 'f1';
while (<$file>) { chomp; $idxs{$_} = 1; }
close($file);

open $file, '<', 'f2';
while (<$file>) {
    next unless $_ =~ /where ri=(\d+);$/;
    print $_ if $idxs{$1};
}
close $file;

【讨论】:

    【解决方案4】:

    上面提到的 awk/grep 解决方案在我的机器上运行缓慢或内存不足(file1 10^6 行,file2 10^7 行)。所以我想出了一个使用 sqlite3 的 SQL 解决方案。

    将file2转成CSV格式的文件,其中第一个字段是ri=之后的值

    cat file2.txt  | gawk -F= '{ print $3","$0 }' | sed 's/;,/,/' > file2_with_ids.txt
    

    创建两个表:

    sqlite> CREATE TABLE file1(rowId char(10));
    sqlite> CREATE TABLE file2(rowId char(10), statement varchar(200));
    

    从 file1 导入行 ID:

    sqlite> .import file1.txt file1
    

    使用“准备好的”版本从 file2 导入语句:

    sqlite> .separator ,
    sqlite> .import file2_with_ids.txt file2
    

    选择表file2中与表file1中匹配的rowId的所有语句:

    sqlite> SELECT statement FROM file2 WHERE file2.rowId IN (SELECT file1.rowId FROM file1);
    

    可以通过在发出 select 语句之前将输出重定向到文件来轻松创建文件 3:

    sqlite> .output file3.txt
    

    测试数据:

    sqlite> select count(*) from file1;
    1000000
    sqlite> select count(*) from file2;
    10000000
    sqlite> select * from file1 limit 4;
    1610666927
    1610661782
    1610659837
    1610664855
    sqlite> select * from file2 limit 4;
    1610665680|update TABLE_X set ATTRIBUTE_A=87 where ri=1610665680;
    1610661907|update TABLE_X set ATTRIBUTE_A=87 where ri=1610661907;
    1610659801|update TABLE_X set ATTRIBUTE_A=87 where ri=1610659801;
    1610670610|update TABLE_X set ATTRIBUTE_A=87 where ri=1610670610;
    

    在没有创建任何索引的情况下,选择语句在 AMD A8 1.8HGz 64 位 Ubuntu 12.04 机器上花费了大约 15 秒。

    【讨论】:

      【解决方案5】:

      以前的大多数答案都是正确的,但唯一对我有用的是这个命令

      grep -oi -f a.txt b.txt
      

      【讨论】:

        【解决方案6】:

        也许尝试 AWK 并使用文件 1 中的数字作为键,例如简单脚本

        第一个脚本将生成 awk 脚本:
        awk -f script1.awk

        { print "\$0 ~ ",$0,"{ print \$0 }" > script2.awk; }

        然后用文件调用 script2.awk

        【讨论】:

        • 但这也会导致文件 1 和文件 2 上的多个文件读取。你不认为这也会变慢吗。
        • 是的,但是如果您读取 file1 一次并创建一个大的正则表达式,然后再次检查 file2 中的所有行,它只会读取每个文件一次。
        【解决方案7】:

        我可能遗漏了一些东西,但是仅仅迭代 file1 中的 ID 并为每个 ID grep file2 并将匹配项存储在第三个文件中还不够吗?即

         for ID in `cat file1`; do grep $ID file2; done > file3
        

        这不是非常有效(因为 file2 将被一遍又一遍地读取),但它可能对你来说已经足够了。如果您想要更快的速度,我建议您使用更强大的脚本语言,它可以让您将file2 读入地图,从而快速识别给定 ID 的行。

        这是这个想法的 Python 版本:

        queryByID = {}
        
        for line in file('file2'):
          lastEquals = line.rfind('=')
          semicolon = line.find(';', lastEquals)
          id = line[lastEquals + 1:semicolon]
          queryByID[id] = line.rstrip()
        
        for line in file('file1'):
          id = line.rstrip()
          if id in queryByID:
            print queryByID[id]
        

        【讨论】:

        • 我也用过 perl,但没有帮助:我的 $input_ri_file="input_RI.csv";我的 $input_sql_file="SQL_file.sql";我的 $output_sql_file = "Out_SQL_file.sql";打开(SQLFILE,“){ push @ lin,$_ unless ($_ eq "\n"); } 关闭 $SQLFILE;打开(RILIST,“){ push @ri_list,$_ 除非 ($_ eq "\n"); } 关闭 $RILIST;打开 (FINALSQLFILE, ">$output_sql_file"); foreach $ri (@ri_list){ chomp $ri; for (@lin) { if ($_ =~ /$ri/ ) { 打印 FINALSQLFILE $_; } } } 关闭 FINALSQLFILE;
        • @Tirthankar:我刚刚添加了一个 Python 版本,有帮助吗?该脚本需要两个文件file1file2。它将匹配项打印到标准输出,因此您可以将其重定向到文件。
        • @Tirthankar 您可能想使用 pastebin 而不是将代码转储到评论中... :-)
        【解决方案8】:

        ## 报告 中包含的所有行在 中丢失

        IFS=$(echo -en "\n\b") && for a in $(cat < file 1>); 
        do ((\!$(grep -F -c -- "$a" < file 2>))) && echo $a; 
        done && unset IFS
        

        或者做提问者想要的,去掉否定并重定向

        (IFS=$(echo -en "\n\b") && for a in $(cat < file 1>); 
        do (($(grep -F -c -- "$a" < file 2>))) && echo $a; 
        done && unset IFS) >> < file 3> 
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-02-10
        • 1970-01-01
        • 1970-01-01
        • 2013-01-12
        • 2013-02-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多