1

I have a tab delimited file, where I want to scan the header (first line) for columns that contain a key word. Then, I want to print only those columns from the rest of the file.

From this example file:

sample  AAA BBB sample_CCC  DDD EEE sample_FFF
000 aaa bbb ccc ddd eee fff

I am looking to get something like this:

sample  sample_CCC  sample_FFF
000 ccc fff

I have tried:

awk -F"\t" '{for(i=1;i<=NF;i++){if ($i ~ /sample/){print $i}}}' filename > outputfile

however, this only pulls out columns with that exact string (sample), not columns that contain it (sample_CCC, sample_FFF).

Is there a way to alter this code so that it will pull columns that do not match the string exactly, but contain the string?

3
  • So you want to scan the header (first line) for columns that contain a key word; you then want to print only those columns from the rest of the file?
    – Jeff Schaller
    Commented Jul 30, 2019 at 15:22
  • yes that's exactly what I'm trying to do
    – kate
    Commented Jul 30, 2019 at 15:30
  • Feel free to steal my words into the question, if you find them helpful!
    – Jeff Schaller
    Commented Jul 30, 2019 at 15:31

3 Answers 3

2

perl allows a nice concise solution:

perl -ale '
    @cols = grep {$F[$_] =~ /sample/} 0..$#F if $. == 1;
    print "@F[@cols]";
' file
1
  • 1
    Conciseness means brevity with clarity. YMMV with the clarity part of that :-)
    – Ed Morton
    Commented Jul 31, 2019 at 4:56
1

What about something like this?

NR==1   {
        for (i=1; i<=NF; i++) {
                if ($i ~ /sample/) {
                        head[i]= $i
                        printf "%s\t", $i
                }
        }
        printf "\n"
}

NR>1    {
        for (i=1; i<=NF; i++) {
                if (head[i]) {
                        printf "%s\t", $i
                }
        }
        printf "\n"
}

A somewhat naïve approach, but it does the job for small files.

13
  • 1
    Use head[i]=1 (less memory) instead. The match should be against an string (not a regex), use ($i ~ "sample").
    – user232326
    Commented Jul 30, 2019 at 15:47
  • you should be able to use for i in head printf "%s\t,$i instead of looping all columns.
    – pLumo
    Commented Jul 30, 2019 at 15:51
  • 2
    I tried the 'for i in head' but it put them in hash order instead of the order that the columns appear in the file - maybe that's awk-implementation-specific?
    – mmusante
    Commented Jul 30, 2019 at 15:51
  • 1
    @Isaac can you explain why "sample" is preferred over /sample/ in a ~ regex match? At least according to the GNU Awk user's guide Using Dynamic Regexps that doesn't seem to be the case. Commented Jul 30, 2019 at 16:06
  • 1
    No, -F "\t" is fine: awk understands that the 2 character string \t represents a tab. Commented Jul 30, 2019 at 18:34
1
$ awk '
    BEGIN { FS=OFS="\t" }
    NR==1 {
        for (inFldNr=1; inFldNr<=NF; inFldNr++) {
            if ($inFldNr ~ /sample/) {
                out2inFldNr[++numOutFlds] = inFldNr
            }
        }
    }
    {
        for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
            inFldNr = out2inFldNr[outFldNr]
            printf "%s%s", $inFldNr, (outFldNr<numOutFlds ? OFS : ORS)
        }
    }
' file
sample  sample_CCC      sample_FFF
000     ccc     fff

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .