比较两个文件并根据条件输出结果
Compare two files and output results according to conditionals
我需要比较两个输入文件(file1.csv 和 file2.csv)并存储结果和第三个文件(file3.csv)。
条件如下:
- 如果条目与文件 1 和文件 2 匹配,则将其存储在文件 3 中
- 如果在文件 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 类似的想法但是:
- 使用提供的文件名
- hard-code 脚本中的文件名而不是参数,因为它们不可互换
- 按要求的顺序产生结果
您可以将脚本保存为 task
、chmod 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
到 运行 基于第一列的左连接
我需要比较两个输入文件(file1.csv 和 file2.csv)并存储结果和第三个文件(file3.csv)。
条件如下:
- 如果条目与文件 1 和文件 2 匹配,则将其存储在文件 3 中
- 如果在文件 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 类似的想法但是:
- 使用提供的文件名
- hard-code 脚本中的文件名而不是参数,因为它们不可互换
- 按要求的顺序产生结果
您可以将脚本保存为 task
、chmod 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
到 运行 基于第一列的左连接