4

File contents:

RANDOM TEXT    num1=400    num2=15    RANDOM TEXT
RANDOM TEXT    num1=300    num2=10    RANDOM TEXT
RANDOM TEXT    num1=200    num2=5    RANDOM TEXT

I would like to subtract 5 for each num2 per line like so:

RANDOM TEXT    num1=400    num2=10    RANDOM TEXT
RANDOM TEXT    num1=300    num2=5    RANDOM TEXT
RANDOM TEXT    num1=200    num2=0    RANDOM TEXT

Pure bash is preferred, but no biggie if another GNU tool does it better.

  • 4
    Bash is not a good text editor; would you be satisfied with calling the proper tool from bash? – Jeff Schaller Oct 16 at 16:57
  • Are the separators between RANDOM and TEXT the same as those between the numerical columns, or do you have tab-separated data in which some columns contain spaces? – steeldriver Oct 16 at 17:42
  • In my scenario, I do believe RANDOM TEXT will be consistently 2 columns, but to play it safe, the consistent tell will be num2= – TuxForLife Oct 16 at 17:48
  • Is num2= column always goes as 4th column? – RomanPerekhrest Oct 16 at 18:11
6

Using awk:

awk '{ for (i=1;i<=NF;i++) { if ($i ~ /num2=/) {sub(/num2=/, "", $i); $i="num2="$i-5; print} } }' file

This will loop through each column of each line looking for the column that contains num2=. When it finds that column it will:

  1. Remove num2= - sub(/num2=/, "", $i)
  2. Redefine that column as num2={oldnum-5} - $i="num2="$i-5
  3. Print the line - print
  • In my scenario, I do believe RANDOM TEXT will be consistently 2 columns, but to play it safe, the consistent tell will be num2= – TuxForLife Oct 16 at 17:01
  • @TuxForLife: I've updated the answer. – Jesse_b Oct 16 at 17:05
  • 1
    Works like a charm, thanks Jesse – TuxForLife Oct 16 at 17:10
5

perl:

perl -pe 's/(?<=num2=)(\d+)/$1 - 5/e' file

To store the contents back into the file:

perl -i -pe ...
4

[Just because I'm trying to become more familiar with Miller - it requires some jumping through hoops to get the heterogeneous output]:

$ mlr --fs ' ' --repifs --ocsvlite --headerless-csv-output put '
    $num1 = "num1=".$num1; $num2 = "num2=".($num2-5)
' file
RANDOM TEXT num1=400 num2=10 RANDOM TEXT
RANDOM TEXT num1=300 num2=5 RANDOM TEXT
RANDOM TEXT num1=200 num2=0 RANDOM TEXT

If the input is actually TSV in which some columns may contain spaces, then

$ mlr --fs '\t' --ocsvlite --headerless-csv-output put '
    $num1 = "num1=".$num1; $num2 = "num2=".($num2-5)
' file
RANDOM TEXT num1=400    num2=10 RANDOM TEXT
RANDOM TEXT num1=300    num2=5  RANDOM TEXT
RANDOM TEXT num1=200    num2=0  RANDOM TEXT
0

Using Vim:

vim +'g/^/exe "norm! 3Wf=5\<C-A>"' +wq file

The first + argument runs a series of normal mode operations on each line to increment the number 5 times. It uses a :global command to run this on every line. Simply using :normal with a range %norm would be simpler if we weren't using CTRLA to increment the number, but in order to (easily) pass this control character into Vim we build the command as a string to run with :execute, and thus we need the :global.

The second + argument simply saves the file.

If the format of RANDOM TEXT is less regular (or if you just prefer regular expressions), you could instead use a :substitute command:

vim +'%s/num2=\zs\d\+/\=submatch(0) + 5' +wq file

This uses a :substitute command to replace the N part of num2=N with the existing value plus five.

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.