考虑两个文件
文件1:
name|gender|phone|email|city|country
abc|F|11111|ldiskmsjdh|pune|india
文件2:
sno|name|email|country
1|abc|zzzzzzzz|USA
我需要使用unix所需的以下输出:
name|gender|phone|email|city|country
abc|F|11111|zzzzzzzz|pune|USA
注意:根据主键(即“名称”)进行匹配
我能够更换整条生产线。但我想在匹配时仅替换行的列,并仅更新第二个文件中存在的那些列。原始格式不应改变。
我正在寻找适用于 N 列的通用代码。关键位置保持不变。
类似这样的事情:
节目:
merge.awk
BEGIN {
FS = "|"
OFS = "|"
FILE1_KEY_COL = 1
FILE1_COL_NAME = 1
FILE1_COL_GENDER = 2
FILE1_COL_PHONE = 3
FILE1_COL_CITY = 5
FILE2_KEY_COL = 2
FILE2_COL_EMAIL = 3
FILE2_COL_COUNTRY = 4
}
NR == FNR && FNR == 1 {
# Ignore first line
next
}
NR == FNR {
file2_h_email[$FILE2_KEY_COL] = $FILE2_COL_EMAIL
file2_h_country[$FILE2_KEY_COL] = $FILE2_COL_COUNTRY
}
NR != FNR && FNR == 1 {
print $0
next
}
NR != FNR {
print $FILE1_COL_NAME,$FILE1_COL_GENDER,$FILE1_COL_PHONE,file2_h_email[$FILE1_KEY_COL],$FILE1_COL_CITY,file2_h_country[$FILE1_KEY_COL]
}
这样执行:
awk -f merge.awk file2 file1
警告:
file2
(具有新值的引用)必须位于 file1
之前
这是一个尴尬但通用的解决方案。
# patch_csv.awk
BEGIN {
FS="|"
}
FNR==NR {
if (FNR == 1) {
nf1 = NF
print
for (i = 1; i <= NF; i++) {
header1[i] = $i
if ($i == "name") {
key = i
}
}
} else {
data[$key] = $0
}
}
FNR!=NR {
if (FNR == 1) {
for (j = 1; j <= NF; j++) {
header2[j] = $j
if ($j == "name") {
key = j
}
for (i = 1; i <= nf1; i++) {
if (header1[i] == header2[j]) {
colmap[i] = j
}
}
}
} else if (data[$key]) {
split(data[$key], record, FS)
for (i = 1; i <= nf1; i++) {
printf "%s%s", (colmap[i] ? $colmap[i] : record[i]), (i == nf1 ? "\n" : FS)
}
}
}
像这样运行:
awk -f patch_csv.awk file1 file2
输出:
name|gender|phone|email|city|country
abc|F|11111|zzzzzzzz|pune|USA
请注意,这是对 CSV 格式的非常原始的处理,因为它不支持引用或多行值。
当然,如果使用 Python 或 Ruby,这会简单得多。更不用说他们还正确处理完整的 CSV 规范。
但是如果您愿意对列号进行硬编码,那么这可能会更简单:
head -1 file1.csv && \
join -a 1 -t '|' -1 1 -2 2 \
<(tail -n +2 file1.csv | sort -t '|' -k 1,1) \
<(tail -n +2 file2.csv | sort -t '|' -k 2,2) | \
awk -v FS='|' \
'{ print($1 FS $2 FS $3 FS ($8 ? $8 : $4) FS $5 FS ($9 ? $9 : $6)) }'
使用
head -1
打印标题。然后使用 join
显示第一个文件 (-a 1
) 外连接到第二个文件的所有行,使用第一个文件的第一列 (-1 1
) 和第二个文件的第二列 (-2 2
)
)。对于join
,文件必须排序;因此,首先撕下标题 (tail -n +2
),然后对相关列上的每个文件进行排序 (sort -k
)。 join
和 sort
都应使用 |
作为分隔符 (-t '|'
)。 join
输出包含两个文件中的列,因此我们需要自己合并它们,并且我们可以使用 awk
来执行此操作,例如如果第 8 个字段为空,则打印第 4 个字段。请注意,file1.csv
顺序不会保留。
编辑:如果你有 300 个字段......只需使用适当的编程语言哈哈