【问题标题】:how to subtract the two files in linux如何在linux中减去两个文件
【发布时间】:2013-08-15 20:36:24
【问题描述】:

我有两个如下文件:

文件1

"Connect"    CONNECT_ID="12"
"Connect"    CONNECT_ID="11"
"Connect"    CONNECT_ID="122"
"Connect"    CONNECT_ID="109"

文件2

"Quit"    CONNECT_ID="12"
"Quit"    CONNECT_ID="11"

文件内容与上述不完全相同,但记录数最少为100,000。

现在我想将如下所示的结果放入 file1 中(意味着最终结果应该在 file1 中)

"Connect"    CONNECT_ID="122"
"Connect"    CONNECT_ID="109"

我使用了类似下面的 while 循环:

awk {'print $2'} file2 | sed "s/CONNECTION_ID=//g" > sample.txt

while read actual; do

    grep -w -v $actual file1 > file1_tmp
    mv -f file1_tmp file1

done < sample.txt

这里我已经根据示例调整了我的代码。所以它可能有效,也可能无效。

我的问题是循环重复超过 1 小时才能完成该过程。

那么任何人都可以建议我如何通过使用diffcommsedawk 或任何其他运行速度更快的任何其他Linux 命令来实现相同的效果吗?

这里主要是想消除这个典型的while大循环。

【问题讨论】:

  • connect_id 是唯一的数字还是重复的?
  • 我认为“缺乏”并不明显,"lakh" / "lac"(我认为您实际上是有意的)在东南亚以外也不明显。

标签: linux shell sed awk grep


【解决方案1】:

大多数 UNIX 工具都是基于行的,因为您没有整行匹配,这意味着 grepcommdiff 不在窗口中。提取你想要的基于字段的信息awk 是完美的:

$ awk 'NR==FNR{a[$2];next}!($2 in a)' file2 file1
"Connect"    CONNECT_ID="122"
"Connect"    CONNECT_ID="109"

要将结果存储回file1,您需要将输出重定向到一个临时文件,然后将文件移动到file1,如下所示:

$ awk 'NR==FNR{a[$2];next}!($2 in a)' file2 file1 > tmp && mv tmp file1

说明:

awk 变量 NR 每读取一条记录,即每个文件中的每一行都会递增。 FNR 变量会针对每条记录递增,但会针对每个文件重置。

NR==FNR    # This condition is only true when reading file1
a[$2]      # Add the second field in file1 into array as a lookup table
next       # Get the next line in file1 (skips any following blocks)
!($2 in a) # We are now looking at file2 if the second field not in the look up
           # array execute the default block i.e print the line 

要修改此命令,您只需更改匹配的字段。在您的实际情况中,如果您想将来自 file1 的字段 1 与来自 file2 的字段 4 匹配,那么您会这样做:

$ awk 'NR==FNR{a[$1];next}!($4 in a)' file2 file1

【讨论】:

    【解决方案2】:

    这可能对你有用(GNU sed):

    sed -r 's|\S+\s+(\S+)|/\1/d|' file2 | sed -f - -i file1
    

    【讨论】:

    • 请解释一下它是如何工作的,以便我可以根据我的要求进行更改。
    • @vidyadhar 此解决方案将 file2 更改为 sed 脚本,该脚本又针对 file1 运行。查看脚本从管道中删除。第一个 sed 命令删除第一个非空格字符串,即“退出”字符串,并使用剩余部分作为模式删除 file1 中的一行。
    • 很好....如果 file1 包含像 和 FILE2 包含像 那么如何使用sed
    • @vidyadhar 确定 file2 中的唯一键并将其用作匹配 file1 中的模式TIMESTAMPCONNECTION_ID...
    【解决方案3】:

    最适合这项工作的工具是join(1)。它根据每个文件的给定列中的值连接两个文件。通常它只输出两个文件中匹配的行,但它也有一种模式可以输出其中一个文件与另一个文件不匹配的行。

    join 要求在您加入的字段上对文件进行排序,因此要么对文件进行预排序,要么使用进程替换(bash 功能 - 如下例所示)来执行它在一个命令行上:

    $ join -j 2 -v 1 -o "1.1 1.2" <(sort -k2,2 file1) <(sort -k2,2 file2)
    "Connect" CONNECT_ID="122"
    "Connect" CONNECT_ID="109"
    

    -j 2 表示要在第二个字段中为两个文件加入文件。

    -v 1 表示仅输出文件 1 中与文件 2 中的任何内容都不匹配的字段

    -o "1.1 1.2" 表示使用文件 1 的第一个字段 (1.1) 对输出进行排序,然后是文件 1 的第二个字段 (1.2)。如果没有这个,join 将首先输出连接列,然后是其余列。

    【讨论】:

    • 一个很大的警告是join 需要排序输入。如果文件太大而无法放入内存,则无法避免,但如果您可以将所有内容都保留在核心中,则避免排序通常会更快。
    【解决方案4】:

    您可能需要首先分析 file2,并将所有已出现在缓存中的 ID 附加到缓存中(例如内存) 比逐行扫描file1来调整该ID是否在缓存中。

    python 代码如下:

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    import re
    
    p = re.compile(r'CONNECT_ID="(.*)"')
    
    quit_ids = set([])
    
    for line in open('file2'):
        m = p.search(line)
        if m:
            quit_ids.add(m.group(1))
    
    
    output = open('output_file', 'w')
    for line in open('file1'):
        m = p.search(line)
        if m and m.group(1) not in quit_ids:
            output.write(line)
    output.close()
    

    【讨论】:

    • 这里我使用的是Shell脚本......如果可能的话请帮助我如何在shell脚本中编写代码..
    【解决方案5】:

    真正的主要瓶颈不是while 循环,而是您重写输出文件数千次这一事实。

    在您的特定情况下,您也许可以摆脱这个:

    cut -f2 file2 | grep -Fwvf - file1 >tmp
    mv tmp file1
    

    (我不认为 grep-w 选项在这里有用,但由于您在示例中使用了它,所以我保留了它。)

    这假定file2 是制表符分隔的;如果没有,你的awk '{ print $2 }' file2 很好。

    【讨论】:

    • 这里将 file2 的内容与 file1 进行比较,并将 file1 中不匹配的记录正确打印...
    猜你喜欢
    • 1970-01-01
    • 2020-07-14
    • 2011-05-21
    • 1970-01-01
    • 2016-06-12
    • 2017-03-05
    • 1970-01-01
    • 2020-10-06
    • 2017-09-29
    相关资源
    最近更新 更多