I have a file describing allele frequencies and I am trying to use awk to print lines if either one of the frequencies in the two last columns are equal or greater than 0.01. Below is the format of the frequency file: I am interested in the last two frequencies.

2L      10000133        A      125/125 115/131 0/125   16/131
2L      10000374        A      121/124 143/143 3/124   0/143

From my limited knowledge of awk, I wrote the following code.

cat myfile.txt |
awk ' BEGIN { {FS="/"}{OFS="\t"}
     if( ($8>0) && ($8/$9 >= "0.01") || ($10>0) && ($10/$11 >= "0.01"))
             { print $1,$2 }
            }' > myfilteredfile.txt

However, the output file myfilteredfile.txt is empty. I expected that the output file would have printed:

2L      10000133
2L      10000374

...Because both lines contain a frequency in the last two columns that is equal or greater than 0.01. Any advice?

$  awk '{ split($(NF-1),a,/\//) } { split($NF,b,/\//) } a[1]/a[2] > 0.01 || b[1]/b[2] > 0.01 { print $1, $2 }' file.in
2L 10000133
2L 10000374

This splits the last two fields individually on the / character and stores the parts in the two arrays a and b. If either division, when carried out, results in a number greater than 0.01, the two first fields are outputted.

$NF is the value of the last field (column) in the input record (line). and $(NF-1) is the second to last field's value.

The issue with your code is that you're doing everything in a BEGIN block. This block is mainly for initialization and will be executed before any data has actually been read. There is also an issue with the chosen value of FS that the other answers points out.

You also do not need to quote numbers when they are used as numbers.


There are two issues with your existing script.

The first is the separator: you want to split on runs of spaces, and slash; so you need to specify a regular expression which matches that, and use that as FS: FS="[ /]+".

The second is that all your code is in the BEGIN block. You need to initialise the separators in the BEGIN block (or elsewhere, e.g. using specific AWK parameters), then use a matcher block to process the lines.

This version works:

awk 'BEGIN { FS="[ /]+"; OFS="\t" }
     (($8>0) && ($8/$9 >= "0.01") || ($10>0) && ($10/$11 >= "0.01")) {
         print $1, $2
     }' myfile.txt

Strictly speaking it would be better to use 0.01 instead of "0.01" since you’re matching numeric values:

awk 'BEGIN { FS="[ /]+"; OFS="\t" }
     (($8>0) && ($8/$9 >= 0.01) || ($10>0) && ($10/$11 >= 0.01)) {
         print $1, $2
     }' myfile.txt

In awk's BEGIN{...} parts is running before reading input line from input, so in your code actually awk not reading any input, that's why it's returning nothing. if you want to feed it you need to use getline but it will read only one line, you can use for loop to read all.

another problem is when you use FS="/" it splitting the lines into the fields which delimited by /, but the problem is $8 doesn't exit since max fields are only 5 and those the filed is like this 125 115, 131 0, 125 16, etc.

You can use this code instead to achieve what you want. also note that awk can read from file directly not required using cat there

awk -F'[/ ]+' 'BEGIN{ OFS="\t"}
    {if( ($8>0) && ($8/$9 >= 0.01) || ($10>0) && ($10/$11 >= 0.01))
    { print $1,$2 }
}'  infile

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.