![比较 2 个 csv 文件的多个列,并将其中一个列替换为另一个文件中的列](https://linux22.com/image/213683/%E6%AF%94%E8%BE%83%202%20%E4%B8%AA%20csv%20%E6%96%87%E4%BB%B6%E7%9A%84%E5%A4%9A%E4%B8%AA%E5%88%97%EF%BC%8C%E5%B9%B6%E5%B0%86%E5%85%B6%E4%B8%AD%E4%B8%80%E4%B8%AA%E5%88%97%E6%9B%BF%E6%8D%A2%E4%B8%BA%E5%8F%A6%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%E4%B8%AD%E7%9A%84%E5%88%97.png)
我必须比较 2 个文件file a
并file b
使用相同的列,但随机放置在 2 个文件中,例如比较列值
account
、code
、date
、type
、pc
、vol
和bs
如果为任何行找到匹配项,则将第 1 列替换为 fromaccount
并file a
显示所有非匹配的记录来自.输出应如下所示account1
file b
file a
file c
我没有任何 Linux 背景,在浏览在线论坛后,我稍微知道这可以通过 AWK 完成,但我对 awk 不太熟悉。请帮忙。对比需要在linux环境下进行
通过随机放置的列,我的意思是在命令中我应该能够给出可用于将文件 a 与文件 b 匹配的列(两个文件中列的顺序不会相同)。它可以是 8 或 10 或 15 列以在两个文件中匹配。
与该帐户类似,account1 不得是第一列和最后一列,该命令还应该能够选择需要更新的列
重复处理 -->如果文件b对于文件a中的1记录有很多重复,那么它应该更新文件b第一个匹配的记录,以便它不能在匹配中再次使用-->如果文件a有很多重复而文件b只有1 个匹配记录,那么最终文件 c 应该只更新 1 个帐户值,而不是更新文件 a 的所有匹配值记录
file a
account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub
6576,WEQR,TYRE,BS,54122022,OBCD,K,BAT,5000,F,SCSC
1234,GFHD,ASDF,BS,21122022,STOP,C,CAT,1000,S,MATH
7654,GHAD,LOPI,CV,9089022,KGAD,G,BSEE,5908,J,IOYU
file b
account,code,date,type,inst,insttype,pc,str,vol,bs,name,xdate,account1
1234,ASDF,21122022,BS,GOLDY,RUB,C,123.1,1000,S,RON,90891234,CCCCC
2761,LOPCS,10122022,BSFD,SLV,STR,C,123.9,1001,B,RON,99999988,DDDDD
0980,RTDF,28822025,JUFG,BRNZ,HIY,C,123.8,2000,S,RON,88881234,EEEEE
file c
account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub
6576,WEQR,TYRE,BS,54122022,OBCD,K,BAT,5000,F,SCSC
CCCCC,GFHD,ASDF,BS,21122022,STOP,C,CAT,1000,S,MATH
7654,GHAD,LOPI,CV,9089022,KGAD,G,BSEE,5908,J,IOYU
答案1
这个答案从使用的解决方案开始磨坊主csvsql
,继续使用 Miller 与from结合使用解决方案csvkit,然后以仅使用 的解决方案结束csvsql
。
使用磨坊主( mlr
) 首先(左)连接来自以下命名字段的fileA
数据:fileB
account,code,date,type,pc,vol,bs
...然后重命名该account1
字段account
(对于具有字段的记录account1
,该字段仅是已加入的记录)。
然后,我们对字段重新排序并删除输出中不需要的字段。
mlr --csv \
join -f fileA -j account,code,date,type,pc,vol,bs --ul then \
rename account1,account then \
cut -o -f account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub fileB
给出问题中的数据的输出:
account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub
CCCCC,GFHD,ASDF,BS,21122022,STOP,C,CAT,1000,S,MATH
6576,WEQR,TYRE,BS,54122022,OBCD,K,BAT,5000,F,SCSC
7654,GHAD,LOPI,CV,9089022,KGAD,G,BSEE,5908,J,IOYU
请注意,两个输入文件中的字段顺序无关。
如果您不知道可以使用哪些字段进行连接,则可以单独计算公共字段名称(不幸的是,Miller 无法执行“自然连接”操作,但必须给出要连接的显式字段名称列表):
mlr --csv put -q '
if (NR == 1) {
for (k in $*) { @f[k] = 1 }
} else {
for (k in @f) {
is_null($[k]) { unset @f[k] }
}
}
end {
common_fieldnames = joink(@f,",");
emit common_fieldnames
}' fileA fileB
对于给定的数据,输出以下 CSV 数据集
common_fieldnames
"account,code,type,date,pc,vol,bs"
到仅有的获取逗号分隔列表,使用可生成无标头不带引号的 CSV 输出的选项,例如与和--csv
结合使用。--headerless-csv-output
--quote-none
一种完全不同的方法是使用csvsql
fromcsvkit执行自然左连接,然后用于mlr
后处理输出:
csvsql --query 'SELECT * FROM "fileA" NATURAL LEFT JOIN "fileB"' fileA fileB |
mlr --csv \
put 'is_not_null($account1) { $account = $account1 }' then \
cut -o -f account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub
这样,您就不必关心两个文件之间哪些字段是通用的。
如果你愿意的话,你甚至可以用 SQL 来完成这一切:
csvsql --query '
CREATE TEMPORARY TABLE tmp AS SELECT * FROM "fileA" NATURAL LEFT JOIN "fileB";
UPDATE tmp SET account = account1 WHERE account1 IS NOT NULL;
SELECT account,temp1,code,type,date,subtask,pc,toy,vol,bs,sub FROM tmp;' fileA fileB
答案2
#!/bin/bash
IFS=, hdrs_to_check="$*"
col_to_change="account"
col_with_value="account1"
awk -F, -v hdrs_to_check="$hdrs_to_check" \
-v col_to_change="$col_to_change" \
-v col_with_value="$col_with_value" '
function make_compound_key(hdrs_2_idx_map) {
key = ""
for (i = 1; i <= hdrs_to_check_arr_size; i++) {
hdr_name = hdrs_to_check_arr[i]
hdr_idx = hdrs_2_idx_map[hdr_name]
key = key$hdr_idx","
}
return key
}
BEGIN {
hdrs_to_check_arr_size = split(hdrs_to_check, hdrs_to_check_arr, ",")
}
#Both file headers processing
FNR == 1 {
#Map file_a and file_b headers to their indexes.
for (i = 1; i <= NF; i++) {
if (NR == 1) {
f_b_hdr_2_idx_map[$i] = i
} else {
f_a_hdr_2_idx_map[$i] = i
}
}
next
}
#file_b processing
FNR == NR {
key = make_compound_key(f_b_hdr_2_idx_map)
# Duplicate keys are not changing the "account1" value, it is fixed
# from the first key occurence.
if (!(key in file_b_dct)) {
file_b_dct[key] = $f_b_hdr_2_idx_map[col_with_value]
}
next
}
#file_a processing
FNR != NR {
key = make_compound_key(f_a_hdr_2_idx_map)
# "file_a_updated_dct" is needed to prevent following lines
# with same "key" be changed. These lines are left unchanged.
if ((key in file_b_dct) && !(key in file_a_updated_dct)) {
$f_a_hdr_2_idx_map[col_to_change] = file_b_dct[key]
file_a_updated_dct[key]
}
print
}
' file_b.txt OFS=, file_a.txt
#' file_b.txt OFS=, file_a.txt | column -s, -t
用法:
$ ./my_script.sh account code date type pc vol bs