【问题标题】:find lines existing in one file and not in another, based on a portion of the line根据行的一部分查找存在于一个文件中而不存在于另一个文件中的行
【发布时间】:2020-05-26 15:54:38
【问题描述】:

我有两个文件 A.datB.dat

A.dat

112381550RSAP002839002C00000000020200600000110102020-05-26
112539961RSAP002839002C00000000020200700000140102020-05-26
140823748RSAP002839002C00000000020210200000050102020-05-26
110604754RSAP002839002C00000000020200600000110102020-05-26

B.dat

112381550RSAP002839002C00000000020200600000000102020-05-26
112539961RSAP002839002C00000000020200700000000102020-05-26
119A06559RSAP002839002C00000000020210100000000102020-05-26
119231672RSAP002839002C00000000020200900000000102020-05-26
118372226RSAP002839002C00000000020200800000000102020-05-26

我想根据前 22 个字符(在 BOLD 中查找 A.dat 中不存在的记录) 输出应该低于

119A06559RSAP002839002C00000000020210100000000102020-05-26 119231672RSAP002839002C00000000020200900000000102020-05-26 118372226RSAP002839002C00000000020200800000000102020-05-26

尝试使用grep,如下所示

grep -Fvxf B.dat A.dat > c.dat 

但没有找到只比较那部分数据的方法。

【问题讨论】:

  • 欢迎来到 SO,在 SO 上,我们鼓励用户添加他们为解决自己的问题所做的努力,所以请在您的问题中添加相同的内容,然后让我们知道。
  • 我尝试将您的示例包装在代码标签中,但不确定它们看起来是否正常,因此最好将您的示例包装在 CODE TAGS 中以便更好地理解。感谢您在问题中加入您的努力。
  • 我会根据awk使用下面的方法:awk '{s=substr($0,1,22)}(FNR==NR){a[s];next}!(s in a)' A.dat B.dat

标签: linux shell unix awk


【解决方案1】:

请您尝试以下方法。

awk 'FNR==NR{array[substr($0,1,22)];next} !(substr($0,1,22) in array)'  A.dat B.dat

说明:为上面添加详细说明。

awk '                             ##Starting awk program from here.
FNR==NR{                          ##Checking condition if FNR==NR then do following.
  array[substr($0,1,22)]          ##Creating an array whose index is first 22 elements of current line.
  next                            ##next will skip all further statements from here.
}
!(substr($0,1,22) in array)       ##Checking condition if current line first 22 characters are NOT in array the print the current line.
'  A.dat B.dat                    ##Mentioning Input_file names here.

【讨论】:

    【解决方案2】:

    我会根据awk使用下面的方法:

    awk '{s=substr($0,1,22)}(FNR==NR){a[s];next}!(s in a)' A.dat B.dat
    

    这可确保您始终匹配前 22 个字符。

    它本质上做了以下事情:每次读取一行(忽略文件)它都会创建一个小字符串s,其中包含该行的前 22 个字符。如果我们处理第一个文件(FNR==NR) 将字符串存储在数组a 中,如果我们处理第二个文件,则检查该字符串是否是a 的成员,如果不是,则打印该行。

    您也可以尝试基于grep 的解决方案,但这可能会导致误报,具体取决于您喜欢输入的方式:

    cut -c1-22 A.dat | grep -vFf - B.dat
    

    但是,这可以匹配A.dat 行的前 22 个字符B.dat 行中的任意位置(不一定是前 22 个字符)

    【讨论】:

      【解决方案3】:

      如果输出的顺序不重要,这里有一个使用bashsortGNU uniqgrep-free 方法:

      sort {A,A,B}.dat | uniq -uw 22
      

      ...或在 POSIX 外壳中:

      sort A.dat A.dat B.dat | uniq -uw 22
      

      任一方法的输出:

      118372226RSAP002839002C00000000020200800000000102020-05-26
      119231672RSAP002839002C00000000020200900000000102020-05-26
      119A06559RSAP002839002C00000000020210100000000102020-05-26
      

      【讨论】:

      • 这很有创意!请注意,POSIX 不支持 -w 标志 uniq (pubs.opengroup.org/onlinepubs/9699919799/utilities/…)
      • 注意:如果 A.dat 非常大,使用 cat A.dat A.dat 连续读取两次可能会很昂贵,在这种情况下,使用单通喜欢:sed p A.dat | cat - B.dat | sort | uniq -uw 22.
      • 你也可以只做sort {A,A,B}.dat | uniq -uw 22 或者如果你不想读第二个文件两次:sed p A.dat | sort - B.dat | uniq -uw 22
      • @kvantour 谢谢。在发帖之前,我记得在没有cat 的情况下尝试过sort,但由于某种原因它没有用(我想我可能使用过sort -u),但经过审查,它显然可以。
      【解决方案4】:

      您可以使用 grep 和 colrm 来执行此操作,如下所示(文件名“-”被理解为标准输入,您可以将其与“-f”一起使用):

      colrm 23 < A.dat | grep -F -v -f - B.dat
      

      如果您不能 100% 确定那些 22 个字符的模式只会在行首匹配,则需要在 colrm 的每一行输出中添加一个“^”并省略“-F”标志来自 grep 的标志,如下所示:

      colrm 23 < A.dat | sed -e 's/^/\^/;' | grep -v -f - B.dat
      

      【讨论】:

      • 如果前 22 个字符中的任何一个包含特殊的正则表达式模式,例如 .*?[]&amp;,这可能会失败
      • 如果一行的前 22 个字符中有魔术字符,第二个命令可能会失败,是的。第一个仍然有效,但有值得注意的警告。我没有尝试保护魔法字符,因为它看起来不像示例数据的问题。不过,感谢您的注意。
      猜你喜欢
      • 1970-01-01
      • 2013-01-06
      • 1970-01-01
      • 2019-07-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-20
      • 2022-01-09
      • 2022-06-27
      相关资源
      最近更新 更多