![awk:提取与第一行中的子字符串匹配的制表符分隔列](https://linux22.com/image/154358/awk%EF%BC%9A%E6%8F%90%E5%8F%96%E4%B8%8E%E7%AC%AC%E4%B8%80%E8%A1%8C%E4%B8%AD%E7%9A%84%E5%AD%90%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8C%B9%E9%85%8D%E7%9A%84%E5%88%B6%E8%A1%A8%E7%AC%A6%E5%88%86%E9%9A%94%E5%88%97.png)
我想从文本文件(“columns.txt”)中提取制表符分隔的列,其中标题(第一行)与另一个文本文件(“strings.txt”)中列出的某些字符串匹配。
“columns.txt”看起来像这样:
rs2438689 rs54666437 rs9877702046 rs025436779...
0 0 0 1
1 1 2 2
0 1 2 0
... ... ... ...
“strings.txt”看起来像这样:
rs2438689
rs9877702046
...
输出文本文件“output.txt”应如下所示(制表符分隔):
rs2438689 rs9877702046...
0 0
1 2
0 2
... ...
关于如何使用 awk 执行此操作有什么建议吗?谢谢你!
答案1
代替 Awk,如何从 中创建一个以逗号分隔的列名列表,并将其用作sstrings.txt
的列表:namedcol
csvtool
$ csvtool -t TAB -u TAB namedcol "$(paste -sd, < strings.txt)" columns.txt
rs2438689 rs9877702046
0 0
1 2
0 2
... ...
或与csvcut/csvformat
基于 Python 的类似csvkit
:
$ csvcut -tc "$(paste -sd, < strings.txt)" columns.txt | csvformat -T
rs2438689 rs9877702046
0 0
1 2
0 2
... ...
答案2
和perl
$ perl -F'\t' -lane 'if(!$#ARGV){ $h{$_}=1 }
else{ @i = grep { $h{$F[$_]} == 1 } 0..$#F if !$c++;
print join "\t", @F[@i]}' strings.txt columns.txt
rs2438689 rs9877702046
0 0
1 2
0 2
if(!$#ARGV){ $h{$_}=1 }
对于第一个输入文件,创建一个以行内容作为键的哈希值@i = grep { $h{$F[$_]} == 1 } 0..$#F if !$c++
对于第二个文件的第一行,从哈希中创建所有匹配列名称的索引列表print join "\t", @F[@i]
打印匹配的列
答案3
修改我对你之前问题的解决方案:
awk -F '\t' -f script.awk strings.txt columns.txt
哪里script.awk
BEGIN { OFS = FS }
FNR == NR {
columns[$1] = 1
next
}
FNR == 1 {
for (i = 1; i <= NF; ++i)
if ($i in columns)
keep[i] = 1
}
{
nf = split($0, fields, FS)
$0 = ""
j = 0
for (i = 1; i <= nf; ++i)
if (i in keep)
$(++j) = fields[i]
print
}
在这里,该FNR == NR
块只会在读取命令行 ( strings.txt
) 上列出的第一个文件时执行。它将使用columns
作为列名称的键填充数组。其余的代码是或多或少与旧解决方案相同,除了我们检查当前列是否是我们想要保留的列(在FNR == 1
块中)之外。
解决评论中的问题:
要始终复制前六列并剪切 处的列标题_
,请更改
FNR == 1 {
for (i = 1; i <= NF; ++i)
if ($i in columns)
keep[i] = 1
}
进入
FNR == 1 {
for (i = 1; i <= NF; ++i) {
sub("_.*", "", $i)
if (i <= 6 || $i in columns)
keep[i] = 1
}
}
答案4
通过使用下面的脚本完成,可能很长时间都可以正常工作
k=wc -l file1| awk '{print $1}'
for ((i=1;i<=$k;i++)); do for j in `cat file2`; do awk -v i="$i" -v j="$j" '$i == j {x=NR+k}(NR<=x){print $i}' file1; done ; done>final.txt
z=`wc -l final.txt| awk '{print $1}'`
for ((i=1;i<=$z;i++)); do j=$(($i+3)); sed -n ''$i','$j'p' final.txt >file_starting_with_$i.txt; i=$j; done
paste file_starting_with*
输出
rs2438689 rs9877702046
0 0
1 2
0 2