多个文件基于 file1 键合并



我试图实现多个文件与主文件键的合并。 我的主文件是这样的

猫文件.txt

哪个有钥匙,想比较一下....

1
2
3
4
5
6
7
8
9
10
11

其他输入文件,如下所示

猫F1.txt

1 : 20
3 : 40
5 : 40
7 : 203

猫 F2.txt

3 : 45
4 : 56
9 : 23

想要这样的输出..

f1 f2 ....
1  20 NA
2  NA NA
3  40 45
4  56 NA
5  40 NA
6  NA NA
7  203 NA
8  NA NA
9  23 NA
10 NA NA
11 NA NA

尝试过此操作,但无法打印不匹配的键

awk -F':' 'NF>1{a[$1] = a[$1]$2}END{for(i in a){print i""a[i]}}' files.txt *.txt
1  20
3  40 45
4  56
5  40
7  203
9  23

请有人指导我这里缺少什么吗?

复杂的 GNUawk解决方案(将涵盖任意数量的文件,考虑到系统资源):

awk 'BEGIN{ 
PROCINFO["sorted_in"]="@ind_num_asc"; h="  "; 
for(i=2;i<=ARGC;i++) h=(i==2)? h ARGV[i]: h OFS ARGV[i]; print h 
}
NR==FNR{ a[$1]; next }{ b[ARGIND][$1]=$3 }
END{ 
for(i in a) { 
printf("%d",i); 
for(j in b) printf("%s%s",OFS,(i in b[j])? b[j][i] : "NA"); print "" 
} 
}' files.txt *.txt

示例输出:

f1 f2 
1 20 NA
2 NA NA
3 40 45
4 NA 56
5 40 NA
6 NA NA
7 203 NA
8 NA NA
9 NA 23
10 NA NA
11 NA NA

  • PROCINFO["sorted_in"]="@ind_num_asc"- 排序模式(按数字升序排列)

  • for(i=2;i<=ARGC;i++) h=(i==1)? h ARGV[i]: h OFS ARGV[i]- 循环访问脚本参数,收集文件名。ARGCARGV使命令行参数可用于程序

$ cat awk-file
NR==FNR{
l=NR
next
}
NR==FNR+l{
split(FILENAME,f1,".")
a[$1]=$3 
next
}
NR==FNR+l+length(a){
split(FILENAME,f2,".")
bwk -v OFS='t' -f awk-file files.txt f1.txt f2.txt[$1]=$3                                                                                                                                                 
next
}
END{
print "",f1[1],f2[1]
for(i=1;i<=l;i++){
print i,(a[i]!="")?a[i]:"NR",(b[i]!="")?b[i]:"NR"
}
}
$ awk -v OFS='t' -f awk-file files.txt f1.txt f2.txt 
f1      f2
1       20      NR
2       NR      NR
3       40      45
4       NR      56
5       40      NR
6       NR      NR
7       203     NR
8       NR      NR
9       NR      23
10      NR      NR
11      NR      NR

我修改了你进一步问题的答案。 如果您有第 3 个、第 4 个文件(假设是第 n 个文件),请按如下方式添加 n 个新块,

NR==FNR+l+length(a)+...+length(n){
split(FILENAME,fn,".")
n[$1]=$3
}

在你的End块里,

END{
print "",f1[1],f2[1],...,fn[1]
for(i=1;i<=l;i++){
print i,(a[i]!="")?a[i]:"NR",(b[i]!="")?b[i]:"NR",...,(n[i]!="")?n[i]:"NR"
}
}
$ cat tst.awk
ARGIND < (ARGC-1) { map[ARGIND,$1] = $NF; next }
FNR==1 {
printf "%-2s", ""
for (fileNr=1; fileNr<ARGIND; fileNr++) {
fileName = ARGV[fileNr]
sub(/.txt$/,"",fileName)
printf "%s%s", OFS, fileName
}
print ""
}
{
printf "%-2s", $1
for (fileNr=1; fileNr<ARGIND; fileNr++) {
printf "%s%s", OFS, ((fileNr,$1) in map ? map[fileNr,$1] : "NA")
}
print ""
}
$ awk -f tst.awk f1.txt f2.txt files.txt
f1 f2
1  20 NA
2  NA NA
3  40 45
4  NA 56
5  40 NA
6  NA NA
7  203 NA
8  NA NA
9  NA 23
10 NA NA
11 NA NA

上面使用 GNU awk 表示 ARGIND,其他 awks 只是在脚本的开头添加一行FNR==1{ARGIND++}

使用 awk 和sort -n对输出进行排序:

$ awk -F" *: *" '
NR==FNR {
a[$1]; next }
FNR==1 {
for(i in a)
a[i]=a[i] " NA"
h=h OFS FILENAME
}
{
match(a[$1]," NA")
a[$1]=substr(a[$1],1,RSTART-1) OFS $2 substr(a[$1],RSTART+RLENGTH)
} 
END { 
print h
for(i in a) 
print i a[i]
}' files f1 f2 |sort -n
f1 f2
1 20 NA
2 NA NA
3 40 45
4 56 NA
5 40 NA
6 NA NA
7 203 NA
8 NA NA
9 23 NA
10 NA NA
11 NA NA

陷阱: 1.在某些情况下,sort标头会失败。2.由于NA被替换为值$2,您的数据不能有NA起始字符串。这可以通过替换/ NA( |$)/来规避,但可能会导致代码中的更多检查,因此请仔细选择您的NA。:D

编辑

例如,为四个文件运行它:

$ awk '...' files f1 f2 f1 f2 | sort -n
1 20 20 NA NA
2 NA NA NA NA
3 40 45 40 45
4 56 56 NA NA
5 40 40 NA NA
6 NA NA NA NA
7 203 203 NA NA
8 NA NA NA NA
9 23 23 NA NA
10 NA NA NA NA
11 NA NA NA NA

请使用以下脚本进行处理。 FILESPATH 包含输入文件的列表 (f1.txt, f2.txt...)。 输入具有输入文件(文件.txt)。

script.sh

FILESPATH=/home/ubuntu/work/test/
INPUT=/home/ubuntu/work/files.txt
i=0
while read line
do
FILES[ $i ]="$line"
(( i++ ))
done < <(ls $FILESPATH/*.txt)
for file in  "${FILES[@]}"
do
echo -n "  ${file##*/}"
done
echo ""
while IFS= read -r var
do
echo -n "$var " 
for file in "${FILES[@]}"
do
VALUE=`grep "$var " $file | cut -d ' ' -f3`
if [ ! -z $VALUE ]; then
echo -n "$VALUE    "
else
echo -n "NA    "
fi
done
echo ""
done < "$INPUT"

======== 您可以使用 printf 而不是 echo 来获得更好的输出格式。

这可以通过简单的循环和回显语句来完成。

#!/bin/bash
NA=" NA"
i=0
#print header module start
header[i]=" "
for file in `ls f[0-9].txt`;
do
first_part=`echo $file|cut -d. -f1`
i=$i+1
header[i]=$first_part
done
echo ${header[@]}
#print header module end

#print elements start
for element in `cat files.txt`;
do
var=$element
for file in `ls f[0-9].txt`;
do
var1=`grep -w ${element} $file`
if [[ ! -z $var1 ]] ; then
field2=`echo $var1|cut -d":" -f2`
var="$var$field2"
else
var="$var$NA"
fi
done
echo $var
done
#print elements end