繁体   English   中英

如何使用awk按列合并两个文件?

[英]How can I merge two files by column with awk?

我有以下两个文本文件:

文件 1

-7.7
-7.4
-7.3
-7.3
-7.3

文件 2

4.823
5.472
5.856
4.770
4.425

我想将它们并排合并,用逗号分隔:

文件 3

-7.7,4.823
-7.4,5.472
-7.3,5.856
-7.3,4.770
-7.3,4.425

我知道这可以通过paste -d ',' file1 file2 > file3轻松完成,但我想要一个允许我控制每次迭代的解决方案,因为我的数据集很大,我还需要向输出添加其他列文件。 例如:

A,-7.7,4.823,3
A,-7.4,5.472,2
B,-7.3,5.856,3
A,-7.3,4.770,1
B,-7.3,4.425,1

这是我到目前为止所得到的:

awk 'NR==FNR {a[$count]=$1; count+=1; next} {print a[$count] "," $1; count+=1;}' file1 file2 > file3

输出:

-7.3,4.823
-7.3,5.472
-7.3,5.856
-7.3,4.770
-7.3,4.425

我是 bash 和 awk 的新手,因此将不胜感激:)

编辑:
假设我有一个包含成对文件的目录,以两个扩展名结尾:.ext1 和 .ext2。 这些文件的名称中包含参数,例如 file_0_par1_par2.ext1 有其对,file_0_par1_par2.ext2。 每个文件包含 5 个值。 我有一个函数可以从它的名字中提取它的序列号和它的参数。 我的目标是在单个 csv 文件 (file_out.csv) 上写入文件中存在的值以及从其名称中提取的参数。
代码:

for file1 in *.ext1 ; do
    for file2 in *.ext2 ; do
        # for each file ending with .ext2, verify if it is file1's corresponding pair
        # I know this is extremely time inefficient, since it's a O(n^2) operation, but I couldn't find another alternative
        if [[ "${file1%.*}" == "${file2%.*}" ]] ; then
            # extract file_number, and par1, par2 based on some conditions, then append to the csv file
            paste -d ',' "$file1" "$file2" | while IFS="," read -r var1 var2;
            do
                echo "$par1,$par2,$var1,$var2,$file_number" >> "file_out.csv" 
            done
        fi
    done
done

您可以将您的解决方案与“粘贴”一起使用。 例如,只需添加 while 循环即可控制每次迭代。

paste -d ',' file1 file2 | while IFS="," read -r lineA lineB;
do
    # you can build new file here like you need
    echo "$lineA,$lineB"
done

您的命令失败:

awk 'NR==FNR {a[$count]=$1; count+=1; next} {print a[$count] "," $1; count+=1;}' file1 file2 > file3

不要使用$count而是count ,从 count 1 开始,在 file2 中开始时将 count 重置为 1。 最后两个条件可以通过FNR==1 {count=1}{count=FNR}
count始终与FNR ,为什么要使用count

awk 'NR==FNR {a[FNR]=$1; next} {print a[FNR] "," $1; }' file1 file2

有效地执行更新的问题所描述的方法:

假设我有一个包含成对文件的目录,以两个扩展名结尾:.ext1 和 .ext2。 这些文件的名称中包含参数,例如 file_0_par1_par2.ext1 有其对,file_0_par1_par2.ext2。 每个文件包含 5 个值。 我有一个函数可以从它的名字中提取它的序列号和它的参数。 我的目标是在单个 csv 文件 (file_out.csv) 上写入文件中存在的值以及从其名称中提取的参数。

for file1 in *.ext1 ; do
    for file2 in *.ext2 ; do
        # for each file ending with .ext2, verify if it is file1's corresponding pair
        # I know this is extremely time inefficient, since it's a O(n^2) operation, but I couldn't find another alternative
        if [[ "${file1%.*}" == "${file2%.*}" ]] ; then
            # extract file_number, and par1, par2 based on some conditions, then append to the csv file
            paste -d ',' "$file1" "$file2" | while IFS="," read -r var1 var2;
            do
                echo "$par1,$par2,$var1,$var2,$file_number" >> "file_out.csv" 
            done
        fi
    done
done

将是(未经测试):

for file1 in *.ext1; do
    base="${file1%.*}"
    file2="${base}.ext2"
    paste -d ',' "$file1" "$file2" |
    awk -v base="$base" '
        BEGIN { split(base,b,/_/); FS=OFS="," }
        { print b[3], b[4], $1, $2, b[2] }
    '
done > 'file_out.csv'

base="${file1%.*}"; file2="${base}.ext2" base="${file1%.*}"; file2="${base}.ext2"本身比for file2 in *.ext2 ; do if [[ "${file1%.*}" == "${file2%.*}" ]] ; then效率高 N^2 倍(给定 N 对文件) for file2 in *.ext2 ; do if [[ "${file1%.*}" == "${file2%.*}" ]] ; then for file2 in *.ext2 ; do if [[ "${file1%.*}" == "${file2%.*}" ]] ; then for file2 in *.ext2 ; do if [[ "${file1%.*}" == "${file2%.*}" ]] ; then和做| awk '...' | awk '...'本身比| while IFS="," read -r var1 var2; do echo ...; done效率高一个数量级| while IFS="," read -r var1 var2; do echo ...; done | while IFS="," read -r var1 var2; do echo ...; done | while IFS="," read -r var1 var2; do echo ...; done (请参阅为什么使用a-shell-loop-to-process-text-thinked-bad-practice )因此您可以期望看到性能比现有脚本有巨大的改进。

两条可能有帮助的建议:

首先,我怀疑一个执行您想要的操作的 awk 脚本对于单行程序来说太长了。 我会编写一个多行脚本,将file1file2作为参数,并将其存储在名为mymerge.awk或其他文件中。 这是一个骨架:

#!/usr/bin/awk -f

BEGIN {
    file1=ARGV[1]; file2=ARGV[2]
}

# The guts of your script go here.

然后您可以简单地使您的脚本可执行( chmod +x mymerge.awk )并从 shell 调用它: mymerge.awk file1 file2 这种方法的优点是使您的脚本易于阅读、重用和维护。

第二条建议:使用 Awk 的getline < file1file1而不是stdin读取数据。 同样对于file2 要将刚刚读取的行存储在变量中,您可以说

getline var1 < file1; getline var2 < file2

Gnu Awk 用户指南getline及其使用方法进行了全面且易读的描述

今晚我无法为您编写和测试工作脚本,但我希望这能帮助您取得一些进展。

awk 'BEGIN {FS=","} {getline file2_line < "file2.txt"; print $1","file2_line }' file1.txt

begin 块将字段分隔符设置为逗号,但这仅适用于 file1.txt 中的数据

脚本主体中的第一条语句将 file2.txt 中这一行的值存储到名为 file2_line 的变量中。 此变量包含 file2.txt 中的行,并且该行中的数据不会以通常的方式拆分为字段。 这意味着如果 file2.txt 也是逗号分隔的,那么您可能希望使用 awk 的 split 函数将字符串拆分为一个数组,以便处理各个字段。

在 awk 中,通过简单地一个接一个地写入字符串值来连接,所以print $1","file2_line写入第一个文件中的第一个字段,一个文字逗号,以及我们之前存储的 file2.txt 这一行的字符串值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM