从文件中挑出一组列

时间:2017-02-12 07:32:53

标签: bash awk

我有一个包含许多列的文件,我试图挑选出它们的一部分。该子集不是由连续范围定义的(例如,我不能迭代2到10)。

示例:

$ cat test
1 2 3 4 5 6 7 8 9 10 
2 4 6 8 10 12 14 16 18 20

$ cat index
3
5
9

此示例中的预期输出:

3 5 9
6 10 18

我想要第3,第5和第9列(比如说)。 我知道我可以awk '{print $3, $5, $9}' test在这里做{但是这在我的真实数据中是不可行的> 100列,我需要选择>其中50个。因此,请不要建议手动编写所有列号。

我目前的一个非常糟糕的解决方案如下:

while read column
do
    awk -v var=$column '{print $var}' test > "$column"
done < index

将与我想要的列(保存在名为index的文件中)对应的各列放入单个文件(根据索引命名),然后将它们粘贴在一起。

这感觉非常低效,我确信有更好的方法。 你能建议吗?

5 个答案:

答案 0 :(得分:2)

假设您的输入是空格分隔的,您可以使用cutpaste

$ cut -d ' ' -f $(paste -s -d ',' index) infile
3 5 9
6 10 18

cut-f选项中以逗号分隔的字段列表,paste -s -d ','index文件创建该列表。

您的输入是制表符分隔的,您可以从-d ' '命令中删除cut

答案 1 :(得分:2)

的简单构造函数

由于此问题已被标记为:[ ],因此我会使用两者来解决问题:

printf -v var "$%s, " $(<index)
awk "{print ${var%, }}" test

3 5 9
6 10 18

通过使用双引号("),我确保不会看到$var,只会看到它的内容。

答案 2 :(得分:1)

与其他好的答案相比,我似乎很冗长  但我可以把它留在这里作为一些复杂/复杂案例(分隔规则或字段过滤)的替代方案:

假设test文件包含:

1 2 3 4 5 6 7 8 9 10 15 25 30 35 45 75 80 90
2 4 6 8 10 12 14 16 18 20 40 50 60 70 85 100

我们需要提取文件index中指定位置的所有字段,内容为:

3
5
9
8
12
14

工作:

indices=$(<index)
echo $indices | awk -v f="$indices" 'BEGIN{split(f, a, "\n")}
     {f=""; for (i=1;i in a;i++) {printf "%s%s", f, $a[i]; f=OFS} print ""}' test

您可以在split(f, a, "\n")函数调用

中设置/调整自定义分隔符

输出:

3 5 9 8 25 35
6 10 18 16 50 70

答案 3 :(得分:0)

仅限Awk:

$ awk '
NR==FNR { a[$1]; next }                     # code for index file
{                                           # code for test file below this point
    for(i=1;i<=NF;i++)
        printf "%s%s", (i in a ? $i OFS : ""), (i==NF ? ORS : "")
}' index test
3 5 9 
6 10 18 

解决方案读入index文件,并将要打印的字段编号存储到a哈希。然后test的所有字段都会for进行迭代,而i中的a字段printf将以ORS结束{/ 1}}。

答案 4 :(得分:0)

由于我得到了很多答案,我认为我会对它们进行基准测试。 我生成了一个包含1000列和10,000行的文件。为了生成索引,我随机选择了一个包含100个数字的子集。

@Benjamin W -

time cut -d ' ' -f $(paste -s -d ',' index) test | wc -l
10000

real    0m0.844s
user    0m0.839s
sys     0m0.020s

@F。 Hauri

printf -v var "$%s, " $(<index) ; time awk "{print ${var%, }}" test
10000

real    0m0.242s
user    0m0.233s
sys     0m0.017s

@RomanPerekhrest

indices=$(<index)
time echo $indices | awk -v f="$indices" 'BEGIN{split(f, a, "\n")}
     {f=""; for (i=1;i in a;i++) {printf "%s%s", f, $a[i]; f=OFS} print ""}' test

10000

real    0m0.460s
user    0m0.456s
sys     0m0.016s

@James Brown

time awk '
NR==FNR { a[$1]; next }
{
    for(i=1;i<=NF;i++) 
        printf "%s%s", (i in a ? $i OFS : ""), (i==NF ? ORS : "")
}' index test | wc -l

10000

real    0m2.667s
user    0m2.658s
sys     0m0.018s