Bash 如何合并两个文件并创建一个新文件,其中包含第一个文件的所有行和列,以及一个包含第二个文件的值的新列?
我有两个这样的文件:Bash 如何合并两个文件并创建一个新文件,其中包含第一个文件的所有行和列,以及一个包含第二个文件的值的新列?,bash,awk,Bash,Awk,我有两个这样的文件: 文件1 文件2: a 23 100 a 24 221 a 19 454 b 45 345 b 44 344 b 10 443 c 45 190 c 98 324 我想合并这些文件以获得一个输出,其中包含第一个文件中的所有行和列,以及一个新列,如果file1和file2的第一列和第二列相等,则该列的值为file2中第三列的值,否则该列应为零。这样的输出: a 23 100 a 24 221 a 34 0 b 45 345 b 34 0 b 44 344 b 1 0 c 45
文件1 文件2:
a 23 100
a 24 221
a 19 454
b 45 345
b 44 344
b 10 443
c 45 190
c 98 324
我想合并这些文件以获得一个输出,其中包含第一个文件中的所有行和列,以及一个新列,如果file1和file2的第一列和第二列相等,则该列的值为file2中第三列的值,否则该列应为零。这样的输出:
a 23 100
a 24 221
a 34 0
b 45 345
b 34 0
b 44 344
b 1 0
c 45 190
c 43 0
非常感谢。为什么不在上面循环一下呢
F1="/path/to/File1"
F2="/path/to/File2"
while read LINE; do
test -z "$LINE" && continue
FOUND=$(grep -e "^$LINE " "$F2")
test -z "$FOUND" && FOUND="$LINE 0"
echo "$FOUND"
done < "$F1"
当然,如果文件已经排序,您可以跳过排序。awk中的一个:
$ awk 'NR==FNR {
a[$1 FS $2]=$3
next
}
{
print $1,$2,((i=a[($1 FS $2)])?i:0)
}' file2 file1
输出:
a 23 100
a 24 221
a 34 0
b 45 345
b 34 0
b 44 344
b 1 0
c 45 190
c 43 0
纯方式:
declare-afnames=();declare-a订单=()
读取字段时;do fnames[$field]=0订单+=(“$field”);完成我的答案与@JamesBrown的答案非常相似,我不打算发布它,但是@F.Hauri声称他们的shell解决方案在针对发布的输入文件运行时比awk更快,所以下面是我的awk脚本和他们的shell脚本之间的第三次运行时比较:
$ time awk 'NR==FNR{a[$1,$2]=$3; next} {print $0,a[$1,$2]+0}' file2 file1 > ou.awk
real 0m0.046s
user 0m0.000s
sys 0m0.000s
然后,我们可以清楚地看到awk脚本在执行时间上比bash脚本有了巨大的改进。两个脚本产生相同的输出值
要理解为什么shell脚本比awk脚本慢得多,请参阅上讨论的性能部分,只需google“bash associative arrays slow”或类似内容
请注意,除了运行得更快之外,awk脚本还更简洁、更清晰、更可移植,因此仅使用shell内置代码编写脚本对该任务没有任何好处。发明了shell对工具的顺序调用和操纵文件/进程的人也发明了awk,让shell调用它来进行一般用途的文本操纵,所以只需遵循他们的意图即可获得最佳结果
哦,仅供参考@JamesBrown的脚本在两个原始文件中的运行速度与我的大致相同:
$ time awk 'NR==FNR { a[$1 FS $2]=$3; next } { print $1,$2,((i=a[($1 FS $2)])?i:0) }' file2 file1 > ou.awk
real 0m0.045s
user 0m0.000s
sys 0m0.030s
以及我生成的文件:
$ time awk 'NR==FNR { a[$1 FS $2]=$3; next } { print $1,$2,((i=a[($1 FS $2)])?i:0) }' file2 file1 > ou.awk
real 0m0.066s
user 0m0.046s
sys 0m0.015s
我的文件太大(超过7000万行),所以我想这需要很多时间。@Vpers:在你的问题中,7000万行的信息丢失了。如果您认为bash
对于7000万行来说太慢,我建议不要用bash
标记您的问题。请参阅@EdMorton查看(我的纯文本回答)[,没有叉子,比使用awk
!@F.Hauri的更快一些。我看了一下,你的脚本比中等大小的输入文件的awk脚本慢几个数量级。我在上发布了即使是很小的文件的计时。试试grep-F File1 File2
@F.Hauri,它会在b1
匹配的地方输出错误匹配例如,b 10
。对于发布的两个输入文件,回答这个SO问题的速度更快。这似乎不太可能,因为您在循环中调用read(速度较慢)并使用bash关联数组(速度也较慢)但是在任何情况下,即使这是真的-对于小文件来说是最快的速度是没有用的,因为任何解决方案都会在一眨眼的时间内运行在小文件上。性能只对非小的输入文件有影响,而对于这样的文件,您的解决方案的运行速度将比awk解决方案慢几个数量级。我只是同时运行了awk解决方案,甚至对于小文件ion速度更快。我会将结果发布在答案中。我在上发布了这些小文件的计时,并且,至少在我的系统上,您的shell脚本比我的awk脚本慢,即使是这些小文件。[神话破灭]
$ time awk 'NR==FNR{a[$1,$2]=$3; next} {print $0,a[$1,$2]+0}' file2 file1 > ou.awk
real 0m0.046s
user 0m0.000s
sys 0m0.000s
$ cat tst.sh
#!/bin/env bash
declare -A fnames=() ; declare -a order=()
while read field;do fnames[$field]=0 order+=("$field") ;done <file1
while read a b c;do fnames[$a $b]=$c ;done <file2
for fnam in "${order[@]}";do echo $fnam ${fnames[$fnam]} ;done
$ time ./tst.sh > ou.bash
real 0m0.062s
user 0m0.015s
sys 0m0.030s
$ awk 'BEGIN{for (i=97; i<=122; i++) for (j=1; j<=1000; j++) printf "%c %d 27\n", i, j}' > file2
$ awk 'NR%3{print $1, $2}' file2 > file1
$ wc -l file2 file1
26000 file2
17334 file1
43334 total
$ time awk 'NR==FNR{a[$1,$2]=$3; next} {print $0,a[$1,$2]+0}' file2 file1 > ou.awk
real 0m0.065s
user 0m0.031s
sys 0m0.030s
$ time ./tst.sh > ou.bash
real 0m1.674s
user 0m0.890s
sys 0m0.765s
$ time awk 'NR==FNR { a[$1 FS $2]=$3; next } { print $1,$2,((i=a[($1 FS $2)])?i:0) }' file2 file1 > ou.awk
real 0m0.045s
user 0m0.000s
sys 0m0.030s
$ time awk 'NR==FNR { a[$1 FS $2]=$3; next } { print $1,$2,((i=a[($1 FS $2)])?i:0) }' file2 file1 > ou.awk
real 0m0.066s
user 0m0.046s
sys 0m0.015s