我有两个巨大的逗号分隔文件。
第一个文件有2.8亿行,其后各列
first name, last name, city, state, ID, email*, phone
John,Smith,LA,CA,123123123123,johnsmith@yahoo.com,12312312
Bob,Marble,SF,CA,120947810924,,48595920
Tai,Nguyen,SD,CA,134124124124,tainguyen@gmail.com,12041284
第二个文件有4.2亿行和以下各列
first name, last name, city, state, email
John,Smith,LA,CA,johnsmith@hotmail.com
Bob,Marble,SF,CA,bobmarble@gmail.com
Tai,Nguyen,SD,CA,tainguyen@gmail.com
*其中许多字段为空
我要合并两个文件中前四列匹配的所有行。然后,如果第二个文件中的电子邮件不是空白,则用第二个文件中的电子邮件填充第一个文件中缺少的电子邮件,请不要更改它。该过程应不区分大小写。如果有很多实例具有相同的4个信息,那么只需忽略这些实例,然后仅对唯一实例进行工作即可。
结果应包含以下列,如下所示
first name, last name, city, state, ID, email, phone
John,Smith,LA,CA,123123123123,johnsmith@yahoo.com,12312312
Bob,Marble,SF,CA,120947810924,bobmarble@gmail.com,48595920
Tai,Nguyen,SD,CA,134124124124,tainguyen@gmail.com,12041284
他们应该只打印出4列而不是1或2或3匹配的内容。我的老板坚持为此使用Bash shell脚本,我是Bash的新手。我是新手,请给我一个清晰的解释。
我已阅读并理解awk需要将信息存储到cpu内存中。但是,在这种情况下,我可以将大文件拆分为小文件并使用awk。我在线复制了一些代码,并根据需要进行更改,但是每当它填充空白电子邮件时,它也会将行定界符从逗号重新格式化为空格。我想停止,但不知道如何。请帮我解决这个问题。所有建议和答案都受到高度赞赏。
awk -F "," 'NR==FNR{a[$1,$2,$3,$4]=$5;next}{if ($6 =="") $6=a[$1,$2,$3,$4];print}' file2.txt file1.txt > file3.txt
答案 0 :(得分:3)
您显示的awk
方法不适用于太大的文件。它将部分文件存储在内存中。使用相同的方法,您将需要存储...或...
first name, last name, city, state
→ID, phone
first name, last name, city, state
→email
假设我们采用第一个选项,并且每个条目仅占用50个字节的内存。要存储所有2.8亿个条目,我们需要280M·50B = 14'000 MB = 14 GB。这是运行awk
命令所需的绝对最小内存。实际上,由于关联数组的实现细节,该数量甚至更多。
使用经典方法解决问题:
sort
两个文件join
按文件的前四列* cut
来自合并结果的所需列** *需要进行一些预处理和后处理,因为join
只能连接一列。
**由于我们必须重新安排电子邮件列cut
is not sufficient。我们可以改用awk
。
#! /bin/bash
prefixWithKey() {
sed -E 's/([^,]*,){4}/\L&\E\t&/' "$1"
}
sortByKeyInPlace() {
sort -t $'\t' -k1,1 -o "$1" "$1"
}
joinByKey() {
join -t $'\t' "$1" "$2"
}
cutColumns() {
awk 'BEGIN{FS="\t|,\t*"; OFS=","} {print $5,$6,$7,$8,$9,$16,$11}'
}
file1="your 1st input file.csv"
file2="your 2nd input file.csv"
for i in "$file1" "$file2"; do
prefixWithKey "$i" > "$i.tmp"
sortByKeyInPlace "$i.tmp"
done
joinByKey "$file1.tmp" "$file2.tmp" | cutColumns > result.csv
rm "$file1.tmp" "$file2.tmp"
此脚本假定输入文件没有没有标题,并且包含没有选项卡。不管是否定义了第一个文件的电子邮件字段,我们总是从第二个文件中获取电子邮件字段。
我几乎没有测试过此脚本,因为您未提供任何示例输入。如果您遇到一些错误并分享了导致该错误的简短输入信息,我很乐意修复脚本(如果需要修复)。
理论上,脚本可以在没有临时文件的情况下编写。由于输入大小,我故意使用了临时文件。 Programs like sort
may run faster on files。
例如,可以通过以下方式加快该脚本的速度:
prefixWithKey
的两个调用。LC_ALL=C
之类的命令前添加sort
。sort
添加选项,例如-S 70%
。对于大文件,将它们存储到数据库中并在其中进行处理可能会更快。甚至还有一个q
工具可以在一个命令中进行这样的思考,但是从我的经验来看,它非常慢。