我有一个较旧的剧本,一直困扰我一段时间,其中有一个小错误,我还没有真正解决,但我认为这是关于时间的。该脚本基本上根据行的ID附加不同文件的列。例如......
test1.txt的:
a 3
b 2
的test2.txt:
a 5
b 9
......应该产生以下结果:
a 3 5
b 2 9
脚本本身如下:
#!/bin/bash
gawk '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)
}
} printf "\n"
}' $1 $2
...称为$ script.sh test1.txt test2.txt
。问题是我得到的结果不是完全我应该得到的结果:
a 3 5
b 2 9
NA NA NA
...即我在文件的最后加NA
行。到目前为止,我只是手动删除了这一行,但不必这样做很好。我不知道这个奇怪的功能来自哪里,但是......有人有任何想法吗?如果重要的话,我在OSX上使用GAWK。
这里有一些实际的输入(这是我试图让问题变得简单而重要的!= P)
target_id length eff_length est_counts tpm
ENST00000574176 596 282 6 0.825408
ENST00000575242 103 718 105 5.19804
ENST00000573052 291 291 21 2.61356
ENST00000312051 223 192 2559 46.8843
我对target_id
和tpm
列感兴趣,其他列不重要。我的完整剧本:
FILES=$(find . -name 'data.txt' | xargs)
# Get replicate names for column header
printf "%s" 'ENSTID'
for file in $FILES; do
file2="${file/.results\/data.txt/}"
file3="${file2/.\/*\//}"
printf "\t%s" $file3
done
printf "\n"
gawk 'BEGIN { OFS="\t" }
{ vals[$1,ARGIND]=$5; 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)
}
} printf "\n"
}' $FILES
(即所有文件都名为data.txt
,但位于不同名称的子文件夹中。)
答案 0 :(得分:3)
更简单的惯用方法是
$ cat test1.txt
a 3
b 2
$ cat test2.txt
a 5
b 9
$ awk -v OFS="\t" 'NR==FNR{rec[$1]=$0;next}$1 in rec{print rec[$1],$2}' test1.txt test2.txt
a 3 5
b 2 9
对于实际输入
$ cat test1.txt
target_id length eff_length est_counts tpm
ENST00000574176 596 282 6 0.825408
ENST00000575242 103 718 105 5.19804
ENST00000573052 291 291 21 2.61356
ENST00000312051 223 192 2559 46.8843
$ cat test2.txt
target_id length eff_length est_counts tpm
ENST00000574176 996 122 6 0.3634
ENST00000575242 213 618 105 7.277
ENST00000573052 329 291 89 2.0356
ENST00000312051 21 00 45 0.123
$ awk 'NR==FNR{rec1[$1]=$1;rec2[$1]=$5;next}$1 in rec1{printf "%-20s %-15s %-15s\n", rec1[$1],rec2[$1],$5}' test1.txt test2.txt
target_id tpm tpm
ENST00000574176 0.825408 0.3634
ENST00000575242 5.19804 7.277
ENST00000573052 2.61356 2.0356
ENST00000312051 46.8843 0.123
注意:
-v OFS="\t"
用于输出中的制表符分隔字段,传递文件的顺序很重要(对第一个解决方案很重要)。硬编码换行符,如
printf "%-20s %-15s %-15s\n", rec1[$1],rec2[$1],$5
不是一个好主意,因为它会使脚本不那么便携。您可以用
替换它printf "%-20s %-15s %-15s", rec1[$1],rec2[$1],$5;print # same effect
编辑:对于两个以上的文件
$ shopt -s globstar
$ awk 'NR==FNR{rec1[$1]=$1" "$5;next}{if($1 in rec1){rec1[$1]=rec1[$1]" "$5}else{rec1[$1]=$1" "$5}}END{for(i in rec1){if(i != "target_id"){print rec1[i];}}}' **/test*.txt
ENST00000312051 46.8843 46.8843 0.123 46.8843 0.123
ENST00000573052 2.61356 2.61356 2.0356 2.61356 2.0356
ENST00000575242 5.19804 5.19804 7.277 5.19804 7.277
ENST00000574176 0.825408 0.825408 0.3634 0.825408 0.3634
ENST77777777777 01245
ENST66666666666 7.277 7.277
$ shopt -u globstar
答案 1 :(得分:2)
据我所知,你在输出结尾处获得一个空行的唯一原因(这是我在OS X上使用gawk
得到的)是你有一个{{1}在脚本的末尾,即使您刚刚打印printf "\n"
,也会添加换行符。
由于您的ORS
脚本本质上是一个bash
脚本,因此我会从中制作一个正确的awk
脚本。这样可以额外避免在shell脚本中引用awk
和$1
的错误问题(会破坏外来文件名)。如果它理解Awk:
$2
使用更复杂的#!/usr/bin/gawk -f
BEGIN { OFS = "\t" }
{
vals[$1,ARGIND] = $2;
keys[$1] = 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));
}
}
}
编辑脚本也可以做到这一点。