鉴于以下文件:
文件1:
7997,1
7997,2
7997,3
5114,1
5114,2
文件2:
7997,52,
5114,12,
4221,52,
如何从第一个文件创建一个数组,其中第一列作为索引,第二列作为要与file2
awk 中的数据进行比较的值?
像这样的东西:
cat file1 file2 | awk -F, '{if(NF==2){arr[$1]=$2}else{if(arr[$1]){print arr[$1]","$0}}}'
期望的输出是:
1,2,3,7997,52
1,2,5114,12
答案1
这是一种方法:
$ awk -F, -vOFS=, 'NR==FNR{a[$1]=a[$1]","$2; next}
($1 in a){print a[$1],$0}' file1 file2 |
sed 's/^,\(.*\),$/\1/'
1,2,3,7997,52
1,2,5114,12
解释
-F, -vOFS=,
:这将输入字段分隔符 (-F
) 和输出字段分隔符(-vOFS
这是运行时在每个打印值之间插入的字符串print $1,$2
)设置为逗号。NR==FNR{a[$1]=a[$1]","$2; next}
:FNR
是行号当前的文件,NR
是输入的行号。当awk
给定两个文件来读取时,这些变量仅在读取第一个文件时才相等。因此,第一个块NR==FNR{}
只会在读取第一个文件时执行。此块中的代码将创建
a
以第一个字段作为索引的数组。每次执行该块时,它都会将一个逗号和第二个字段的值附加到存储在数组中索引处的任何内容$1
。跳转next
到下一个输入行而不继续执行脚本,这样第一个文件的第二个块将不会被执行。由于第一次运行时
a[$1]
将为空,这将在数组的开头添加一个额外的逗号。我们用sed
最后的删除它。($1 in a){print a[$1],$0}
:我们现在处于第二个文件中。如果该行的第一个字段是数组中的索引,则打印与当前行 ( )a
中的该索引关联的值。a
$0
sed 's/^,\(.*\),$/\1/'
:这匹配该行的第一个逗号 (^,
),然后使用括号捕获除最后一个逗号 (\(.*\),$
) 之外的所有内容。然后将整个内容替换为捕获的模式 (\1
)。结果是它只是删除每行的第一个和最后一个逗号。这是为了删除脚本在行首添加的额外逗号awk
以及file2
.我正在删除后者,因为您也没有在所需的输出中显示它。
答案2
您可以使用FNR
和NR
变量来实现此目的。
awk -F "," '{
if(FNR==NR){
if (a[$1] != ""){
a[$1]=a[$1]","$2
}
else{
a[$1]=$2
}
}
else{
if (a[$1]!= ""){
print a[$1]","$1","$2
}
}
}' file1 file2
答案3
从...开始Jijin P的完美答案并稍微加强一下逻辑。这本来是对他的答案的评论,然后它变得太长了(而且它是一个有效的答案本身)所以这里是:
awk 'BEGIN {
FS = ","
OFS = ","
}
FNR == NR {
if ($1 in a) {
a[$1] = a[$1] OFS $2
} else {
a[$1] = $2
}
next
}
$1 in a {
print a[$1], $1, $2
}' file1 file2
一般来说,最好使用if ($x in myarray)
代替if (myarray[$x] != "")
,除非您有特殊原因不这样做。如果您只想确保尚未创建数组的元素,请使用第一个版本。如果您知道有已创建并且想要确保它不是空白字符串,请使用第二个。第二个的技巧是,只需命名数组元素myarray[$x]
,即使在检查其值的上下文中,也会默默地创建该元素。在某些情况下,当您使用 打印数组时,这可能会让您陷入困境for (index in myarray)
。
并且,当使用 时print var1 "," var2 "," var3
,这正是OFS
(输出字段分隔符)存在的用例。在块中设置 OFSBEGIN
可以轻松快速地更改整个脚本的输出格式。
最后,当对第一个文件执行一个操作并对第二个/其他文件执行不同的操作时,我认为以语句FNR == NR
结尾的模式块比 if/else 块更干净。next