awk programming - Changing a field value conditionally

I'm a beginner in awk programming. This post is to clarify a doubt. I have file like this:

70 17 5 mb
71 18 6 ms
72 19 7 ml
73 20 8 mw

in which 4th column is a string. I want to make changes in the 3rd column with respect to 4th column. For example: to read the 4th column, if $4=ms then $3=$3+1.

Actually I don't know how to make the program read string ms.

Please help me regarding this!

4 Answers

To do that, you need to use conditional statement inside action block {}.

The syntax is if ( expr ) statement

So to match the condition where the 4th column is ms and then reassign the value for the 3rd column when the condition is matched:

if ($4 == "ms") $3=$3+1

and then print the whole line using print $0

So the full command looks like this:

awk '{ if ($4 == "ms") $3=$3+1; print $0 }'

1

If the condition is simple you can use pattern matching to modify lines.

Assuming test.txt contains the example data:

cat test.txt
70 17 5 mb
71 18 6 ms
72 19 7 ml
73 20 8 mw

Let's examine the following line and its output:

awk '/ms$/ {$3++} {print}' test.txt
70 17 5 mb
71 18 7 ms
72 19 7 ml
73 20 8 mw

The awk command reads the contents of test.txt and runs the awk script /ms$/ {$3++} {print} on each line. The script can be rewritten as:

/ms$/ {$3++} {print}
  • There are two actions in the script inside curly braces: {}.
    • The second is easier to explain: it just prints out the whole line.
    • The first one contains a pattern specification before the action. The action will only run on matching lines. The pattern is written inside slashes: //.
      • ms$ means each line having the string ms at the end of line $ (The dollar sign indicates that the string should be the last on the line).
      • The action $3++ increments the value of the third column by one.

Please note the {print} action will always run, because there is no pattern for this action, but the {$3++} pattern will only run if the string "ms" is found at the end of line. Also the incrementing happens before the print, so all necessary modifications will be performed on time.

2

Input file:

cat f.txt
70 17 5 mb
71 18 6 ms
72 19 7 ml
73 20 8 mw

Possible solutions:

1. awk '$4=="ms",$3=$3+1;{print}' f.txt
70 17 5 mb
71 18 7 ms
71 18 7 ms
72 19 7 ml
73 20 8 mw

(not sure why but ain't good - target line(s) duplicated)

2. awk '{if ($4=="ms"){$3=$3+1}; print}' f.txt
70 17 5 mb
71 18 7 ms
72 19 7 ml
73 20 8 mw

(worked as expected)

3. awk '{if ($4=="ms"){$3=$3+1} print}' f.txt
70 17 5 mb
71 18 7 ms
72 19 7 ml
73 20 8 mw

(semicolon ";" isn't necessary)

4. awk '{if ($4=="ms"){$3=$3+1} else {$3=$3/2}; print}' f.txt
70 17 2.5 mb
71 18 7 ms
72 19 3.5 ml
73 20 4 mw

(worked as expected and more...)

5. awk '{if ($4=="ms"){$3=$3+1} else {$3=$3/2} print}' f.txt
70 17 2.5 mb
71 18 7 ms
72 19 3.5 ml
73 20 4 mw

(semicolon ";" is dispensable)

2

OP answered their own question , when they described the conditions. It should have been translated as so:

$ awk '$4=="ms"{$3=$3+1};1' input.txt
70 17 5 mb
71 18 7 ms
72 19 7 ml
73 20 8 mw

Expression before {} in awk is treated as if statement, so it works just as OP asked: if $4 is "ms" , increment $3. The 1 after the {}; simply means print. Sequence is also important, because we check conditions first, and then print.

As alternative to awk approach , python can do so as well. The little script below can do the job. It can be turned into one-liner as well, but for readability, I'm providing only the script here

#!/usr/bin/env python
from __future__ import print_function
import sys
with open(sys.argv[1]) as f: for line in f: words = line.strip().split() if words[3] == "ms": words[2] = str(int(words[2]) + 1) print(" ".join(words))

And sample run:

$ ./increment_field.py input.txt
70 17 5 mb
71 18 7 ms
72 19 7 ml
73 20 8 mw

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

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

You Might Also Like