我发布这个问题是因为它是比现有的连接多个文件的用例更通用的用例,并且我在为其编写解决方案时偶然发现了一个小问题。
这是我正在尝试执行的示例(输入文件的数量可能会有所不同):
$ cat f1.tsv
F11 F12 F13
111 A 113
211 B 213
$ cat f2.tsv
F21 F22
121 D
$ cat f3.tsv
F31 F32 F33 F24
131 132 133 C
231 232 233 A
331 332 333 D
$ cat fn.tsv
Fn1 Fn2 Fn3
B 102 103
F 202 203
D 302 303
C 402 403
要加入上述文件,用户将提供 f1.tsv f2.tsv f3.tsv fn.tsv
(文件)作为参数和 2 2 4 1
(要连接的字段)作为参数;预期输出为:
F11 F13 F21 F31 F32 F33 Fn2 Fn3 KEY
111 113 0 231 232 233 0 0 A
211 213 0 0 0 0 102 103 B
0 0 0 131 132 133 402 403 C
0 0 121 331 332 333 302 303 D
0 0 0 0 0 0 202 203 F
注意:输入和输出中的字段用单个制表符分隔
这是我到目前为止所写的内容:
awk -v join='2 2 4 1' -v empty='0' '
BEGIN {
FS = OFS = "\t"
if ( err = (ARGC <= 2 || split(join,joinArr,"[[:space:],]") != ARGC-1) )
exit
}
FNR == 1 {
++fileNumber
j = joinArr[fileNumber]+0
if ( err = (j < 1 || NF < j) )
exit
rec = sep = ""
for (i = 1; i <= NF; i++)
if (i != j) {
rec = rec sep $i
sep = OFS
}
header = (fileNumber == 1 ? rec : header OFS rec)
next
}
{
rec = sep = ""
for (i = 1; i <= NF; i++)
if (i != j) {
rec = rec sep $i
sep = OFS
}
keys[$j]
records[fileNumber,$j] = rec
}
END {
if (err)
exit err
print header, "KEY"
for (k in keys) {
rec = sep = ""
for (i = 1; i <= fileNumber; i++) {
rec = rec sep ((i,k) in records ? records[i,k] : empty)
sep = OFS
}
rec = rec sep k
print rec
}
}
' f1.tsv f2.tsv f3.tsv fn.tsv
但我明白了:
F11 F13 F21 F31 F32 F33 Fn2 Fn3 KEY
111 113 0 231 232 233 0 A
211 213 0 0 102 103 B
0 0 131 132 133 402 403 C
0 121 331 332 333 302 303 D
0 0 0 202 203 F
备注:我需要它在 Linux 和 macOS 上工作,因此我使用 POSIX awk
,但 perl
以及可能的 python
(与 2.7 和 3.x 兼容)也可以是一个选项。
最佳答案
一般问题是 empty
被定义为单个字段;应根据每个文件中的字段数量将其定义为可变数量的字段。
调整当前代码以生成一个空[fileNumber]
数组:
awk -v join='2 2 4 1' -v empty_str='0' ' # modified
BEGIN {
FS = OFS = "\t"
if ( err = (ARGC <= 2 || split(join,joinArr,"[[:space:],]") != ARGC-1) )
exit
}
FNR == 1 {
++fileNumber
j = joinArr[fileNumber]+0
if ( err = (j < 1 || NF < j) )
exit
rec = sep = empty_rec = "" # modified
for (i = 1; i <= NF; i++)
if (i != j) {
rec = rec sep $i
empty_rec = empty_rec sep empty_str # new
sep = OFS
}
header = (fileNumber == 1 ? rec : header OFS rec)
empty[fileNumber] = empty_rec # new
next
}
{
rec = sep = ""
for (i = 1; i <= NF; i++)
if (i != j) {
rec = rec sep $i
sep = OFS
}
keys[$j]
records[fileNumber,$j] = rec
}
END {
if (err)
exit err
print header, "KEY"
for (k in keys) {
rec = sep = ""
for (i = 1; i <= fileNumber; i++) {
rec = rec sep ((i,k) in records ? records[i,k] : empty[i]) # modified
sep = OFS
}
rec = rec sep k
print rec
}
}
' f1.tsv f2.tsv f3.tsv fn.tsv
注释:
- 虽然描述中没有明确说明,但当前(和调整后的)代码假设我们只需处理缺失的行
- 换句话说:在给定文件中,所有行都将包含相同数量的列(即,我们不必担心某些行缺少一列或多列)
这会生成:
F11 F13 F21 F31 F32 F33 Fn2 Fn3 KEY
111 113 0 231 232 233 0 0 A
211 213 0 0 0 0 102 103 B
0 0 0 131 132 133 402 403 C
0 0 121 331 332 333 302 303 D
0 0 0 0 0 0 202 203 F
关于bash - 如何在不同字段上连接多个文件(任意数量),并使用默认值填充缺失字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76067774/