仅逐行比较特定列上的两个文件

仅逐行比较特定列上的两个文件

if i in [2,4,7]awk 中for 循环的语法是什么?还知道如何在下面的代码中实现这一点。谢谢

输入 :

$ cat f1
col1,col2,col3,col4,col5,col6,col7
col11,col12,col13,col14,col15,col16,col17
$ cat f2
col1,col2,col03,col4,col5,col06,col7
col11,col12,col13,col14,col015,col16,col17

$   awk -F,  '
   NR==FNR {
     a[FNR][0]=$0        #populate base file records
     for(i=1;i<=NF;i++)
       a[FNR][i]=$i      #populate base file fields
         next
   }
   {
     for(i=1;i<=NF;i++)
       {
       #if(i in [2,4,7])  <***How to chieve this column in list of values*** >
         #continue;
       if($i!=a[FNR][i])
         {
         printf "Line#%d, column:%d is different in two files.\n",FNR,i
         # ***<TODO print record from first file and second file after printing all mismatch columns>***
         }
       }
   }' f1 f2

预期输出:

Line#1, column:3 is different in two files.
Line#1, column:6 is different in two files.
col1,col2,col3,col4,col5,col6,col7
col1,col2,col03,col4,col5,col06,col7>
Line#2, column:5 is different in two files.
col11,col12,col13,col14,col15,col16,col17
col11,col12,col13,col14,col015,col16,col17>

答案1

基本上,您正在对两个文件和不包括某些列的特定列进行逐行比较;那么,您可以使用 GNU 来awk支持字边界\<& \>

awk -F, -v skip='2,4,7' 'BEGIN{ filetwo=ARGV[1]; ARGV[1]=""; };{
    getline lf2 <filetwo; split(lf2, arr, ",");
    for (i=1; i<=NF; i++) {
        if ( (skip !~ "\\<"i"\\>") && $i!=arr[i] ) {
            print "Line#"FNR, "Column#" i " is different in two files."; mismatch=1; };
    };
}; mismatch { print $0; print lf2; mismatch=0; };' file2 file1

或者任何awk版本:

awk -F, -v skip_cols='2,4,7' '
    BEGIN{ filetwo=ARGV[1]; ARGV[1]=""; split(skip_cols, skip, ","); };{
    getline lf2 <filetwo; split(lf2, arr, ",");
    for (i=1; i<=NF; i++) {
        if ( !(i in skip) && $i!=arr[i] ) {
            print "Line#"FNR, "Column#" i " is different in two files."; mismatch=1; };
    };
}; mismatch { print $0; print lf2; mismatch=0; };' file2 file1

解释一下代码:

  • BEGIN { ... }堵塞:这在想要读取任何输入
    之前首先执行一次。awk

    • 使用ARGV, filetwo=ARGV[1];:
      读取传递给命令的第二个参数(即file2)并将其保存到filetwo变量中;第一个参数ARGV[0]awk本身,第三个ARGV[2]file1
    • 在我们读取参数的值之后,ARGV[1]=""我们取消设置它的值,因此awk将找不到file2要读取的参数()。
    • skip="2 4 7";
      我们定义了一个多变的(看赋值表达式skip并设置我们想要忽略的列号。
  • getline命令- 看使用getline文件中的变量
    我们正在从 file2 中读取一行并将其分配给变量lf2(请注意上面的filetwo变量现在包含我们从中读取的第二个参数的名称ARGV[1]

  • split()功能
    我们分割读取的行文件2它是在lf2逗号字符上的变量中,并存储在名为的数组中;现在该行的每个字段都由(第一个字段)、(第二个字段)、(第三个)等arr.寻址。arr[1]arr[2]arr[3]

  • 之内for-loop陈述我们检查以下两件事:

    • 指示列号的变量值在变量值中看i不到(;并且是字边界锚点,GNU特定的,因此不会匹配);下一个! ~skipskip !~ "\\<"i"\\>"\<\>awki=222
    • 检查 file1 中的列值是否与具有相同索引的 file2 中的同一列不相等:$i!=arr[i];如果它们不相同,则打印不匹配的行号FNR和差异列索引i,并设置一个控制变量mismatch=1
  • mismatch { print ... }lf2:仅当检测到不匹配并且在语句mismatch中设置了变量时,才打印 file1 中的两行,然后打印 file2 中的行;并重置下一行的if变量。mismatch=0

答案2

如果我理解正确的话:

  • 您想对所有字段执行 for 循环: for(i=1;i<=NF;i++) { ... }
  • 和内部:当 i 是 4 个值之一时,您想要跳过(在 awk 中,“继续”将绕过当前 for 循环的其余部分并转到下一次迭代

一个简单的方法:如果您希望能够跳过字段,您可以使用以下技术来做到这一点

BEGIN { skip[2]++; skip[3]++; skip[22]++; skip[23]++ }

....
 for(i=1;i<=NF;i++) {
   if (i in skip) { continue ; rem="Will skip for values defined in skip array indexes" }
   ...

您还可以拥有一个包含要跳过的 4 个索引(每行 1 个)的文件,而不是从 BEGIN 部​​分定义“skip”,并使用 NR==FNR 条件读取该文件,并用此填充跳过数组,然后当 NR!=FNR 时(读取源文件时),您可以使用上述方法跳过这些字段。

答案3

$ cat tst.awk
BEGIN {
    FS=","
    split("2,4,7",tmp)
    for (i in tmp) {
        skipFldNrs[tmp[i]]
    }
}
NR==FNR {
    old[FNR] = $0
    next
}
FNR == 1 {
    for (fldNr=1; fldNr<=NF; fldNr++) {
        if ( !(fldNr in skipFldNrs) ) {
            chkFldNrs[++numToChk] = fldNr
        }
    }
}
old[FNR] != $0 {
    split(old[FNR],o)
    for (i=1; i<=numToChk; i++) {
        fldNr = chkFldNrs[i]
        if ( o[fldNr] != $fldNr ) {
            printf "Line#%d, column:%d is different in two files (\"%s\" vs \"%s\").\n", FNR, fldNr, o[fldNr], $fldNr
        }
    }
}

$ awk  -f tst.awk f1 f2
Line#1, column:3 is different in two files ("col3" vs "col03").
Line#1, column:6 is different in two files ("col6" vs "col06").
Line#2, column:5 is different in two files ("col15" vs "col015").

相关内容