0

I have a file bla.tsv (FS=\t):

id hub fil plop drift
3 187 0.91 bis XX
2 245 0.93     XX
4 250 0.70 bis XX
1 245 0.95 bis XX

And I want to print in the column plop the folowing string :

  1. If hub<200 print intcv
  2. If hub>200 and fil<0.90 print int
  3. If hub>200 and fil>0.90 don't print

In my example it would be :

id hub fil plop drift
3 187 0.91 intcr XX
2 245 0.93     XX
4 250 0.70 int XX
1 245 0.95 bis XX

I've write this but did not have a good result :

awk -F"\t" -vOFS="\t" '
   NR==1{print $0} ; 
   (NR>1 && $2<200){$(NF-1)="intcv";  print $0,$(NF-1) }; 
   (NR>1 && $2>200 && $3<0.90) {$(NF-1)="int";  print $0,$(NF-1) }
' bla.tsv > result.tsv

Do you have an idea how to do that ? Thanks

3
  • 2
    Welcome to the site. Please ensure that you have no typos in your test (e.g. intcr vs. intcv). Also, please describe in what way your attempt did not yield a "good result". In addition, if you write "if (hub>200) ... don't print": Do you mean to change the 4th column to an empy field or to simply not change the existing value? And what happens if hub is exactly 200 / fil is exactly 0.90? – AdminBee Mar 8 at 15:07
  • What actions to take when hub == 200 and/or fil == 0.9 ? – guest_7 Mar 8 at 20:18
  • 1
    Note intcv in your code, but intcr in the expected output. – Kusalananda Mar 8 at 20:25
3
awk -F '\t' 'BEGIN { OFS = FS } NR != 1 { $4 = ($2 < 200 ? "intcv" : ($3 < 0.9 ? "int" : $4)) }; 1' file

This is using (condition ? value_if_true : value_if_false), which is also known as a "ternary operator". It uses this twice to arrive at the correct value for $4.

The trailing 1 at the end of the code could be replaced by { print }, i.e. an unconditional print statement that prints the possibly modified line. For each input line, the code goes through whatever logic that it needs to go through, and then, at the end, prints out the modified line.

The result of running this on your data:

id      hub     fil     plop    drift
3       187     0.91    intcv   XX
2       245     0.93            XX
4       250     0.70    int     XX
1       245     0.95    bis     XX

Expanded into an if-statement, the code

$4 = ($2 < 200 ? "intcv" : ($3 < 0.9 ? "int" : $4))

would look like

if ($2 < 200)
    $4 = "intcv"
else if ($3 < 0.9)
    $4 = "int"

or, on a single line,

if ($2 < 200) $4 = "intcv"; else if ($3 < 0.9) $4 = "int";

... and there would be else $4 = $4 at the end, but we can obviously skip that assignment.

0
1

As you want to print all lines, with changes in some, you can just do the changes and then append 1 to the very end, it means true and the default is executed (= print the line).

So for your script, it means: simply remove all print statements and add 1 and you should be done.

awk -F"\t" -vOFS="\t" ' 
   (NR>1 && $2<200){$(NF-1)="intcv"}; 
   (NR>1 && $2>200 && $3<0.90) {$(NF-1)="int"}
1' bla.tsv > result.tsv
1
$ cat tst.awk
BEGIN { FS=OFS="\t" }
FNR==1 {
    for (i=1; i<=NF; i++) {
        f[$i] = i
    }
    next
}
{
    if ( ($(f["hub"]) < 200) ) {
        $(f["plop"]) = "intcv"
    }
    else if ( ($(f["hub"]) > 200) && ($(f["fil"]) < 0.90) ) {
        $(f["plop"]) = "int"
    }
    print
}

$ awk -f tst.awk bla.tsv
3       187     0.91    intcv   XX
2       245     0.93            XX
4       250     0.70    int     XX
1       245     0.95    bis     XX
0

You can just assign to $4 to modify the field. It rebuilds the field separators, but with single tabs as separators, that doesn't break anything.

$ awk -F"\t" -vOFS="\t" '
   NF < 3 {print; next}   
   ($2 < 200) { $4 = "intcv" }; 
   ($2 > 200 && $3 < 0.90) {$4 = "int" };
   ($2 > 200 && $3 > 0.90) { /* nothing */ };
   {print}
' bla.tsv
id      hub     fil     plop    drift
3       187     0.91    intcv   XX
2       245     0.93            XX
4       250     0.70    int     XX
1       245     0.95    bis     XX

Note that the conditions here are checked sequentially, which matters if you change them so that more than one can match. I added the NF < 3 case to deal with empty lines. Also, you may want to check what you want to do when $2 == 200 or $3 == 0.90.

0
0
awk '{if($2 < 200){$4="intcv"}else{if(($2 >200) && ($3<0.90)){$4="int"}}}1' filename

Output:

id hub fil plop drift
3 187 0.91 intcv XX
2 245 0.93     XX
4 250 0.70 int XX
1 245 0.95 bis XX
1
  • 3
    If you assign intcr to $4, how come the output says intcv? – Kusalananda Mar 8 at 20:20

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.