Reputation: 23
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
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
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
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
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