Moj
Moj

Reputation: 23

How to add a constant number to all entries of a row in a text file in bash

I want to add or subtract a constant number form all entries of a row in a text file in Bash.

eg. my text file looks like:

21.018000 26.107000 51.489000 71.649000 123.523000 127.618000 132.642000 169.247000 173.276000 208.721000 260.032000 264.127000 320.610000 324.639000 339.709000 354.779000 385.084000

(it has only one row) and I want to subtract value 18 from all columns and save it in a new file. What is the easiest way to do this in bash?

Thanks a lot!

Upvotes: 2

Views: 421

Answers (4)

Jay jargot
Jay jargot

Reputation: 2868

Give a try to this compact and funny version:

$ printf "%s 18-n[ ]P" $(cat text.file) | dc

dc is a reverse-polish desk calculator (hehehe).

printf generates one string per number. The first string is 21.018000 18-n[ ]P. Other strings follow, one per number.

21.018000 18: the values separated with spaces are pushed to the dc stack.

- Pops two values off, subtracts the first one popped from the second one popped, and pushes the result.

n Prints the value on the top of the stack, popping it off, and does not print a newline after.

[ ] add string (space) on top of the stack.

P Pops off the value on top of the stack. If it it a string, it is simply printed without a trailing newline.

The test with an additional sed to replace the useless last (space) char with a new line:

$ printf "%s 18-n[ ]P" $(cat text.file) | dc | sed "s/ $/\n/" > new.file
$ cat new.file
3.018000 8.107000 33.489000 53.649000 105.523000 109.618000 114.642000 151.247000 155.276000 190.721000 242.032000 246.127000 302.610000 306.639000 321.709000 336.779000 367.084000

----

For history a version with sed:

$ sed "s/\([1-9][0-9]*[.][0-9][0-9]*\)\{1,\}/\1 18-n[ ]P/g" text.file | dc

Upvotes: 1

Andreas Louv
Andreas Louv

Reputation: 47117

With Perl which will work on multiply rows:

perl -i -nlae '@F = map {$_ - 18} @F; print "@F"' num_file
#     ^ ^^^^                               ^
#     | ||||                               Printing an array in quotes will join
#     | ||||                               with spaces
#     | |||Evaluate code instead of expecting filename.pl
#     | ||Split input on spaces and store in @F
#     | |Remove (chomp) newline and add newline after print
#     | Read each line of specified file (num_file)
#     Inplace edit, change original file, take backup with -i.bak

Upvotes: 0

Lars Fischer
Lars Fischer

Reputation: 10209

Taking advantage of awks RS and ORS variables we can do it like this:

awk  'BEGIN {ORS=RS=" "}{print $1 - 18 }' your_file > your_new_filename

It sets the record separator for input and output to space. This makes every field a record of its own and we have only to deal with $1.

Upvotes: 1

anubhava
anubhava

Reputation: 785761

Use simple awk like this:

awk '{for (i=1; i<=NF; i++) $i -= 18} 1' file >> $$.tmp && mv $$.tmp file    

cat file
3.018 8.107 33.489 53.649 105.523 109.618 114.642 151.247 155.276 190.721 242.032 246.127 302.61 306.639 321.709 336.779 367.084

Upvotes: 1

Related Questions