根据第一列组合几个制表符分隔文件的某些列
Posted
技术标签:
【中文标题】根据第一列组合几个制表符分隔文件的某些列【英文标题】:Combining certain columns of several tab-delimited files based on first column 【发布时间】:2014-09-22 16:01:30 【问题描述】:inFile 中的第一列包含一个不一定存在于所有 inFiles 中的字符串
每个 inFile 中的第 2 和第 7 列包含 Title# 字符串
使用 AWK,我无法正确地将其拼凑在一起。我对描述性变量的使用有望帮助澄清我正在尝试做的事情。这些是我认为我需要的组件:
-
制表符分隔的输入文件:
-F'\t'
增加第一列中的字符串,但每个“名称”只添加一次到“1stColumnNames”:!1stColumnNames[$1]++ name[++i] = $1
为每个 .tsv 文件创建一个新索引来存储每个文件的值以避免覆盖每列的值:!r[FILENAME]++ ++argind
在每个文件的第 2 列和第 7 列中存储对应的列值: 2ndColumnVals[$1, argind] = $2 7thColumnVals[$1, argind] = $7
打印所有 1stColumnNames 以及相关的 2ndColumnVals 和 7thColumnVals,包括它们的标题 'Title1' 'Title2' 'Title3' 等:?????
对于特定 2ndColumnVals 或 7thColumnVals 为空的索引值,打印为 Mtee:?????
对当前工作目录中的所有 .tsv 文件执行此操作并输出一个新的 tsv 文件:*.tsv > outFile.tsv
示例文件
inFile1.tsv
Names Title1 Title2
AAAA 1111 123456
BBBBB 1111 123456
CCC 1111 123456
inFile2.tsv
Names Title3 Title4
BBBBB 2222 789456
DDDDD 2222 789456
EEEE 2222 789456
inFile3.tsv
Names Title5 Title6
AAAA 3333 987654
CCC 3333 987654
EEEE 3333 987654
outFile123.tsv
Names Title1 Title2 Title3 Title4 Title5 Title6
AAAA 1111 123456 Mtee Mtee 3333 987654
BBBBB 1111 123456 2222 789456 Mtee Mtee
CCC 1111 123456 Mtee Mtee 3333 987654
DDDDD Mtee Mtee 2222 789456 Mtee Mtee
EEEE Mtee Mtee 2222 789456 3333 987654
测试脚本
GNU Awk 4.0.1 位于 /usr/bin/awk ,所以我制作了这个文件并在 3 个输入文件所在的同一工作目录中执行它:
命名为 script1.sh#### Example Usage: script1.sh inFile1.tsv inFile2.tsv inFile3.tsv > outFile123.tsv
awk -F'\t' '
FNR==1 ++numFiles
!seen[$1]++ keys[++numKeys] = $1
a[$1,numFiles] = $2 FS $3
END
for (keyNr=1; keyNr<=numKeys; keyNr++)
key = keys[keyNr]
printf "%s", key
for (fileNr=1;fileNr<=numFiles;fileNr++)
printf "\t%s", ((key,fileNr) in a ? a[key,fileNr] : "Mtee\tMtee")
print ""
' "$@"
运行 awk -F script1.awk inFile1.tsv inFile2.tsv inFile3.tsv > outFile123.tsv
会打印以下错误消息:
awk: cmd. line:1: inFile1.tsv
awk: cmd. line:1: ^ syntax error
来自 konsolebox 的测试脚本 2
完美运行,但我试图通过评论来理解每一行:
#!/usr/bin/awk -f
#### named as script2.awk
#### Example Usage: awk -f script2.awk inFile1.tsv inFile2.tsv inFile3.tsv > outFile123.tsv
BEGIN FS = "\t" #input File Style is tab-delimited
sub(/\r/, "") #remove all carriage return characters
!f[FILENAME]++ ++indx #for all files inputted make a single index called indx
!a[$1]++ keys[i++] = $1 #the new indx comprises only unique strings in column 1
b[$1, indx] = $2 FS $3 #the 2nd and 3rd column are tab delimited and each pair that corresponds to a string saved in keys gets stored after the 1st column string in matrix b
END
for (i = 0; i in keys; ++i) #????
key = keys[i] #????
printf "%s", keys #prints out all strings in the index column 1 stored as keys
for (j = 1; j <= indx; ++j) #????
v = b[key, j] #????
printf "\t%s", length(v) ? v : "Mtee" FS "Mtee" #print out strings as tab delimited and replace any lengths of 1 char to two Mtee separated by a tab
print "" #????
【问题讨论】:
+1 以获得清晰的描述、可测试的输入/输出,并努力自己解决!script1.awk
不是 awk 脚本,它是调用 awk 的 shell 脚本。为清楚起见,将其重命名为script1.sh
,使其像任何其他shell 脚本一样可执行,将"$@"
添加到awk 脚本的末尾,以便shell 将awk 传递给它的参数列表,然后以script1.sh inFile1.tsv inFile2.tsv inFile3.tsv > outFile123.tsv
执行它。我编辑了你的问题以表明这一点。
【参考方案1】:
你需要这样的东西:
Gawk 版本(用于 Gawk 4.0+ 中的 ARGIND 加上真正的 2D 数组):
$ gawk -F'\t' '
!seen[$1]++ keys[++numKeys] = $1
a[$1][ARGIND] = $2 FS $3
END
for (keyNr = 1; keyNr <= numKeys; keyNr++)
key = keys[keyNr]
printf "%s", key
for (fileNr = 1; fileNr <= ARGIND; fileNr++)
printf "\t%s", (fileNr in a[key] ? a[key][fileNr] : "Mtee\tMtee")
print ""
' file1 file2 file3
非 gawk 版本:
awk -F'\t' '
FNR==1 ++numFiles
!seen[$1]++ keys[++numKeys] = $1
a[$1,numFiles] = $2 FS $3
END
for (keyNr=1; keyNr<=numKeys; keyNr++)
key = keys[keyNr]
printf "%s", key
for (fileNr=1;fileNr<=numFiles;fileNr++)
printf "\t%s", ((key,fileNr) in a ? a[key,fileNr] : "Mtee\tMtee")
print ""
' file1 file2 file3
Names Title1 Title2 Title3 Title4 Title5 Title6
AAAA 1111 123456 Mtee Mtee 3333 987654
BBBBB 1111 123456 2222 789456 Mtee Mtee
CCC 1111 123456 Mtee Mtee 3333 987654
DDDDD Mtee Mtee 2222 789456 Mtee Mtee
EEEE Mtee Mtee 2222 789456 3333 987654
【讨论】:
感谢 Ed 的快速评论!我无法让它正常运行,这是我保存的文件:#!/usr/bin/gawk #####save 并运行为 ./test.sh gawk -F'\t' ' !看到[$1]++ 键[++numKeys] = $1 a[$1][ARGIND] = $2 FS $3 END for (keyNr = 1; keyNr 在什么情况下“运行不正常”?错误的输出,没有输出,核心转储,错误信息 - 什么?此外,您不能在 cmets 中包含格式,因此如果您有代码或其他格式的文本要向我们展示,请更新您的问题。最后 - 运行gawk --version
并发布结果,您需要 gawk 4.0 或更高版本才能获得真正的 2D 数组和许多其他非常有用的功能。
我的 test.sh 的第 1 行错误(gawk 所在的位置)似乎是一个问题。 $ which gawk 确实返回位置 /usr/bin/gawk。该死,我刚刚编辑/更新,然后才看到您将其修改为 awk,我会再试一次。
我看到你发布了。只需删除第一行 #!/usr/bin/gawk
,因为它告诉 shell 在脚本上运行 gawk,但第一行 (gawk -F...
) 是 shell 命令,而不是 awk 命令。您要么需要一个 shebang (#!..
),后跟一个 awk 脚本的主体,要么没有 shebang,然后是一个调用 awk 但你不能同时拥有的 shell 脚本。我个人通常发现 no shebang 方法更容易使用。【参考方案2】:
这是另一个awk
:
#!/usr/bin/awk -f
# Set field separator to tab (\t)
BEGIN FS = "\t"
# Remove carriage return characters if file is in DOS format (CRLF)
sub(/\r/, "")
# Increment indx by 1 (starts at 0) everytime a new file is processed
!f[FILENAME]++ ++indx
# Add a key ($1) to keys array every time it is first encountered
!a[$1]++ keys[i++] = $1
# Store the 2nd and 3rd field to b matrix
b[$1, indx] = $2 FS $3
# This block runs after all files are processed
END
# Traverse the keys in order
for (i = 0; i in keys; ++i)
key = keys[i]
# Print key
printf "%s", key
# Print columns from every file in order
for (j = 1; j <= indx; ++j)
v = b[key, j]
printf "\t%s", length(v) ? v : "Mtee" FS "Mtee"
# End the line with a newline
print ""
用法:
awk -f script.awk file1 file2 file3
输出:
Names Title1 Title2 Title3 Title4 Title5 Title6
AAAA 1111 123456 Mtee Mtee 3333 987654
BBBBB 1111 123456 2222 789456 Mtee Mtee
CCC 1111 123456 Mtee Mtee 3333 987654
DDDDD Mtee Mtee 2222 789456 Mtee Mtee
EEEE Mtee Mtee 2222 789456 3333 987654
【讨论】:
哇,谢谢!用法对于解决我在运行脚本时的错误特别有帮助。 @BlacquenedRed 这是一个不同的答案。 这很好用,但我正在努力理解它。 'b' 是您制作的矩阵,里面的索引是制表符分隔的 col 2 和 col 3? indx 是一个计数器,它在每个文件上递增,因此它从文件中的 1 开始,然后在文件 2 上变为 2,依此类推.. @BlacquenedRed 这是我最好的参考。我还没有为awk
尝试过任何其他材料。快速参考也是man gawk
。我一开始就做/something
,以便快速搜索。以上是关于根据第一列组合几个制表符分隔文件的某些列的主要内容,如果未能解决你的问题,请参考以下文章