比较两个文件并根据条件输出结果

Compare two files and output results according to conditionals

我需要比较两个输入文件(file1.csv 和 file2.csv)并存储结果和第三个文件(file3.csv)。

条件如下:

  1. 如果条目与文件 1 和文件 2 匹配,则将其存储在文件 3 中
  2. 如果在文件 1 上找到条目,则将其存储在文件 3 上

看起来像这样:

输入:file1.csv

"switch10"
"switch33"
"router51"
"switch6"
"router44"
"router12"
"switch2"

输入:file2.csv

"router51";"DatacenterA - cab1";"Prod - Tenant12"
"switch33";"DatacenterB - cab14";"Prod - Tenant4"
"switch2";"DatacenterA - cab3";"Dev - Tenant5"
"router44";"DatacenterC - cab2";"Test - Tenant2"

结果:file3.csv

"router12"
"router44";"DatacenterC - cab2";"Test - Tenant2"
"router51";"DatacenterA - cab1";"Prod - Tenant12"
"switch2";"DatacenterA - cab3";"Dev - Tenant5"
"switch6"
"switch10"
"switch33";"DatacenterB - cab14";"Prod - Tenant4"

我尝试了很多使用 'awk' 的组合;但是我无法构建条件。

我想知道是否有人可以帮助我构建条件来构建此结果 file3.csv。

非常感谢您的帮助。

我会按照以下方式使用 GNU AWK 完成此任务,令 file1.txt 内容为

"switch10"
"switch33"
"router51"
"switch6"
"router44"
"router12"
"switch2"

file2.txt内容为

"router51";"DatacenterA - cab1";"Prod - Tenant12"
"switch33";"DatacenterB - cab14";"Prod - Tenant4"
"switch2";"DatacenterA - cab3";"Dev - Tenant5"
"router44";"DatacenterC - cab2";"Test - Tenant2"

然后

awk 'BEGIN{FS=";"}(NR==FNR){arr[]=[=12=]}((NR!=FNR)&&( in arr)){arr[]=[=12=]}END{for(i in arr){print arr[i]}}' file1.txt file2.txt

产出

"switch6"
"router12"
"router44";"DatacenterC - cab2";"Test - Tenant2"
"router51";"DatacenterA - cab1";"Prod - Tenant12"
"switch33";"DatacenterB - cab14";"Prod - Tenant4"
"switch10"
"switch2";"DatacenterA - cab3";"Dev - Tenant5"

免责声明:我假设您能够接受输出文件中的任何行顺序。说明:我通知 GNU AWK ; 用作字段分隔符 (FS) 然后在处理第一个文件 (NR==FNR) 时我填充数组 arr将整个当前行 ([=22=]) 放在作为第一个字段 (</code>) 内容的键下。在处理下一个文件 (<code>NR!=FNR) 时,如果存在与数组 arr 中行的第一个字段对应的值,那么我将其值更新为整个当前行。处理完所有文件后,我使用 for.

print 数组 arr 中的所有值

(在 gawk 4.2.1 中测试)

与@Daweo 类似的想法但是:

  1. 使用提供的文件名
  2. hard-code 脚本中的文件名而不是参数,因为它们不可互换
  3. 按要求的顺序产生结果

您可以将脚本保存为 taskchmod 755 task,然后 运行 将其保存为 ./task > file3.csv:

#!/usr/bin/env -S awk -F; -f

BEGIN {
    while ( (getline <"file2.csv") > 0 ) {
        lookup[] = [=10=]
    }
    while ( ("sort -k1.1,1.7 -k1.8n file1.csv" | getline) > 0 ) {
        print ([=10=] in lookup) ? lookup[[=10=]] : [=10=]
    }
}

注意:这里使用的排序是脆弱的,因为它依赖于固定大小的前缀(路由器或交换机)来识别复合键的两个部分。

使用 gawk 的 PROCINFO["sorted_in"] 功能以指定顺序 return 键并使用自定义比较函数 str_num_cmp() 使其更健壮:

#!/usr/bin/env -S awk -F; -f

function str_num_to_a(i, a) {
    match(i, /"([^0-9]*)([0-9]+)"/, a)
}

# a1/a2 are specified as arguments to make them local variables
function str_num_cmp(i1, v1, i2, v2, a1, a2) {
    str_num_to_a(i1, a1)
    str_num_to_a(i2, a2)
    if(a1[1] < a2[1])
        return -1
    if(a1[1] == a2[1]) {
        if(a1[2] < a2[2])
            return -1
        if(a1[2] == a2[2])
            return 0
    }
    return 1
}

BEGIN {
    while ( (getline <"file1.csv") > 0 ) {
        keys[] = ""
    }
    while ( (getline <"file2.csv") > 0 ) {
        lookup[] = [=11=]
    }
    PROCINFO["sorted_in"] = "str_num_cmp"
    for (k in keys) {
        print (k in lookup) ? lookup[k] : k
    }
}

在每个 Unix 机器上的任何 shell 中使用任何 awk 并假设您引用的字段不包含 ;s 并且您不关心输出顺序:

$ cat tst.awk
BEGIN { FS=";" }
NR==FNR {
    first[]
    next
}
{
    print
    delete first[]
}
END {
    for ( i in first ) {
        print i
    }
}

$ awk -f tst.awk file1.csv file2.csv
"router51";"DatacenterA - cab1";"Prod - Tenant12"
"switch33";"DatacenterB - cab14";"Prod - Tenant4"
"switch2";"DatacenterA - cab3";"Dev - Tenant5"
"router44";"DatacenterC - cab2";"Test - Tenant2"
"switch6"
"router12"
"switch10"

如果您确实关心输出顺序,您可以将 decorate/sort/undecorate 习惯用法与任何 awk+sort+cut 结合使用:

$ cat tst.awk
BEGIN { FS=OFS=";" }
NR==FNR {
    first[]
    next
}
{
    prt([=12=])
    delete first[]
}
END {
    for ( i in first ) {
        prt(i)
    }
}

function prt(str,       arr, alpha, numeric) {
    split(str,arr)
    alpha = numeric = arr[1]
    sub(/[0-9].*/,"",alpha)
    gsub(/[^0-9]/,"",numeric)
    print alpha, numeric, str
}

$ awk -f tst.awk file1.csv file2.csv | sort -t';' -k1,1 -k2,2n | cut -d';' -f3-
"router12"
"router44";"DatacenterC - cab2";"Test - Tenant2"
"router51";"DatacenterA - cab1";"Prod - Tenant12"
"switch2";"DatacenterA - cab3";"Dev - Tenant5"
"switch6"
"switch10"
"switch33";"DatacenterB - cab14";"Prod - Tenant4"

如果awk不是必须的,你可以使用简单的Miller

运行

mlr --csv --fs ";" -N join --ul -j 1 -f input_01.csv then unsparsify then sort -f 1 input_02.csv

你有

router12;;
router44;DatacenterC - cab2;Test - Tenant2
router51;DatacenterA - cab1;Prod - Tenant12
switch10;;
switch2;DatacenterA - cab3;Dev - Tenant5
switch33;DatacenterB - cab14;Prod - Tenant4
switch6;;

一些注意事项:

  • --csv --fs ";" 设置输入输出为CSV,字段分隔符为;
  • -N设置输入输出不带表头行;
  • join --ul -j 1 到 运行 基于第一列的左连接