[英]awk + bash: combining arbitrary number of files
我有一個腳本,該腳本采用多個布局相同但數據不同的數據文件,並將指定的數據列組合到一個新文件中,如下所示:
gawk '{
names[$1]= 1;
data[$1,ARGIND]= $2
} END {
for (i in names) print i"\t"data[i,1]"\t"data[i,2]"\t"data[i,3]
}' $1 $2 $3 > combined_data.txt
...在第一列中找到行ID,在第二列中找到有趣的數據。
這很好,但不適用於任意數量的文件。 雖然我可以簡單地增加$4 $5 ... $n
在最后排隊到任何我想我需要,以及添加相同文件的最大數量n
量"\\t"data[i,4]"\\t"data[i,5] ... "\\t"data[i,n]
上面那一行中的"\\t"data[i,4]"\\t"data[i,5] ... "\\t"data[i,n]
(即使對於小於n
文件,它似乎也可以工作; awk似乎忽略了n
大於n
的數量)輸入文件),這似乎是一個“丑陋”的解決方案。 有沒有辦法使此腳本(或提供相同結果的東西)采用任意數量的輸入文件?
或者,甚至更好的是,您是否可以以某種方式在其中合並find
,通過子文件夾搜索並查找與某些條件匹配的文件?
以下是一些示例數據:
文件1
A 554
B 13
C 634
D 84
E 9
file.2:
C TRUE
E TRUE
F FALSE
預期輸出:
A 554
B 13
C 634 TRUE
D 84
E 9 TRUE
F FALSE
這可能就是您要尋找的內容(就像您的原始腳本一樣,將GNU awk用於ARGIND):
$ cat tst.awk
BEGIN { OFS="\t" }
!seen[$1]++ { keys[++numKeys]=$1 }
{ vals[$1,ARGIND]=$2 }
END {
for (rowNr=1; rowNr<=numKeys; rowNr++) {
key = keys[rowNr]
printf "%s%s", key, OFS
for (colNr=1; colNr<=ARGIND; colNr++) {
printf "%s%s", vals[key,colNr], (colNr<ARGIND?OFS:ORS)
}
}
}
$ awk -f tst.awk file1 file2
A 554
B 13
C 634 TRUE
D 84
E 9 TRUE
F FALSE
如果您不關心行的輸出順序,那么您需要做的是:
BEGIN { OFS="\t" }
{ vals[$1,ARGIND]=$2; keys[$1] }
END {
for (key in keys) {
printf "%s%s", key, OFS
for (colNr=1; colNr<=ARGIND; colNr++) {
printf "%s%s", vals[key,colNr], (colNr<ARGIND?OFS:ORS)
}
}
}
您可以通過ARGV列表上的重定向getline訪問任意數量的文件(繞過awk的默認文件處理(通過BEGIN和exit)):
awk 'BEGIN {
for(i=1;i<=ARGC;++i){
while (getline < ARGV[i]) {
...
}
}
<END-type code>
exit}' $(find -type f ...)
假設輸入文件的命名模式為: 1
2
...。
gawk '{
names[$1]=$1
data[$1,ARGIND]=$2
}
END {
for (i in names) {
printf("%s\t",i)
for (x=1;x<=ARGIND;x++) {
printf("%s\t", data[i,x])
}
print ""
}
}' [0-9]* > combined_data.txt
結果:
A 554
B 13
C 634 TRUE
D 84
E 9 TRUE
F FALSE
如果對file1
, file2
, file3
等進行了排序,則使用join
, bash
, awk
和tr
另一種解決方案
多連接
#!/bin/bash
function __t {
join -a1 -a2 -o '1.1 2.1 1.2 2.2' - "$1" |
awk -vFS='[ ]' '{print ($1!=""?$1:$2),$3"_"$4;}';
}
CMD="cat '$1'"
for i in `seq 2 $#`; do
CMD="$CMD | __t '${@:$i:1}'";
done
eval "$CMD | tr '_' '\t' | tr ' ' '\t'";
或遞歸版本
#!/bin/bash
function __t {
join -a1 -a2 -o '1.1 2.1 1.2 2.2' - "$1" |
awk -vFS='[ ]' '{print ($1!=""?$1:$2),$3"_"$4;}';
}
function __r {
if [[ "$#" -gt 1 ]]; then
__t "$1" | __r "${@:2}";
else
__t "$1";
fi
}
__r "${@:2}" < "$1" | tr '_' '\t' | tr ' ' '\t'
注意:數據不能包含字符_
,該字符用作通配符
你得到,
./multijoin file1 file2
A 554 B 13 C 634 TRUE D 84 E 9 TRUE F FALSE
例如,如果
file3
包含
A 111 D 222 E 333
./multijoin file1 file2 file3
你得到,
A 554 111 B 13 C 634 TRUE D 84 222 E 9 TRUE 333 F FALSE
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.