Visit The New, Modern Unix Linux Community


Basic arithmetic operation with awk?


 
Thread Tools Search this Thread
Top Forums UNIX for Dummies Questions & Answers Basic arithmetic operation with awk?
# 1  
Basic arithmetic operation with awk?

input:
Code:
Name|Operation
rec_10|1+2+2-

Output:
Code:
rec_10|1

Basically I am trying to calculate the result of "the path" in $3 where the operators follow the number and not preceding them like we normally do:
rec_10: +1+2-2=1

But I realise (I am sure there is a good reason for that) that awk does handle basic arithmetic operations, like:
(+1) + (2) + (-2) = 1
Giving that to awk:
Code:
awk '{a==1; b==2; c==(-2); print a+b+c}'

makes it stuck in a loop.

Therefore, I think the approach of putting the numbers and operators in the right order (code below) would not work at the end anyway:
Code:
gawk '
BEGIN{FS=OFS="|"}

NR>1{
p=$2;

printf("%s|",$1);

a=split(p, b, "");

for(i=1;i<=a; i=i+2){
   printf("%s%s",substr($2, i+1, 1),substr($2,i,1))
   }

print substr(p,a)}'

output:
Code:
rec_10|+1+2-2-

Is there any awk trick I could use instead?
Maybe using an increment x++, but instead of the '+' sign writing some variables like for example:
Code:
gawk '
BEGIN{FS=OFS="|"}
{
   a=split($2,b,"");
   x==0;
   for(i=a; i>=1; i--){
      x(i)(i+1)
   }
}
END{print x}'

The code above is just an example and returns:
Code:
gawk: (FILENAME=- FNR=1) fatal: function `x' not defined


Last edited by beca123456; 12-18-2015 at 09:51 PM..
# 2  
Is this a homework assignment? (Homework and coursework questions can only be posted in the Homework & Coursework forum with a completely filled out template from the special homework rules described here.)

I have no idea what you are trying to do. Arithmetic operators (except unary minus) work on two operands. Therefore, a arithmetic formula has one more operand than there are operators. But, your example has 3 operands and 3 operators.

Are you saying that you want each number to have a trailing + or - sign to indicate whether the number is positive or negative and you then want to add up all of the signed numbers?

Are you only processing single-digit numbers?
# 3  
Thanks again for your help.
No, it has been a long time I left school (I should have probably taken more programming classes though).

Long story short. I have a string like this:
Code:
1+2+1-

Which actually defines 3 numbers (written in the usual format below):
Code:
(+1)
(+2)
(-1)

I am trying to get the result of the sum of these numbers:
(+1) + (+2) + (-1)
Which is equal to:
1+2-1=2

The + or - signs are actually not operators. They tell you if the number before the sign is positive or negative.

Another example:
Code:
10+12-8+4+

Should returns:
Code:
10

Because:
(+10) + (-12) + (+8) + (+4)
= 10 - 12 + 8 + 4
=10

I recognize this format is pretty unusual.

Last edited by beca123456; 12-19-2015 at 12:32 AM..
# 4  
Quote:
Originally Posted by beca123456
... ... ...
Code:
gawk '
BEGIN{FS=OFS="|"}
{
   a=split($2,b,"");
   x==0;
   for(i=a; i>=1; i--){
      x(i)(i+1)
   }
}
END{print x}'

The code above is just an example and returns:
Code:
gawk: (FILENAME=- FNR=1) fatal: function `x' not defined

I am having trouble following the logic of your script.

The diagnostic you are getting is because the sequence x(i) is a request to call the function named x with the parameter i, you haven't defined any functions, and the function x is not one that is defined by gawk. Furthermore, you can't have a variable and a function with the same name.

Note that the statement: x==0; is a logical expression (not an assignment statement) returning the value 1 if the variable x is zero or an empty string; otherwise it returns 0. (Note that the value of that logical expression doesn't affect the behavior of this script in any way; it does not assign a value to x.)

Although the gawk statement:
Code:
   a=split($2,b,"");

splits the 2nd input field into the array b[] with each element of b[] being set to one character from that field and sets a to the number of characters found; using an empty string as an ERE specifying the field delimiter is not available in all versions of awk (including the one I am using on OS X).

You might want to try something more like:
Code:
awk '
BEGIN {	FS = OFS = "|"	# Set input and output field separators.
}
{	# Initialize the accumulated sum and the current input number to zero.
	sum = number = 0
	print "Processing input: " $2
	end = length($2)
	# Process each character in the 2nd field from left to right...
	for(i = 1; i <= end; i++) {
		if((c = substr($2, i, 1)) ~ /[0-9]/)
			# We found a digit, add it to the current number.
			number = number * 10 + c
		else {	# If it is not a digit it must be a + or -...
			if(c == "+")
				# We found a +, add number to sum.
				sum += number
			else
				# Assume we found a minus sign; subtract number
				# from sum.
				sum -= number
			# We are now ready to look for the next number.
			number = 0
		}
	}
	# Print the results...
	print $1, sum
}' file

which, if file contains:
Code:
rec_10|1+2+2-
rec_20|10+12-8+4+
rec_30|1+10+100-1000+
rec_40|1-10-100+1000-

produces the output:
Code:
Processing input: 1+2+2-
rec_10|1
Processing input: 10+12-8+4+
rec_20|10
Processing input: 1+10+100-1000+
rec_30|911
Processing input: 1-10-100+1000-
rec_40|-911

This was tested using awk on OS X, but should also work with gawk on any Linux system.

If someone wants to try this script on a Solaris/SunOS system, change awk to /usr/xpg4/bin/awk or nawk.
This User Gave Thanks to Don Cragun For This Post:
# 5  
Different approach, not sure if better/faster:
Code:
awk '
        {n = split ($2, T, "[+-]")
         gsub (/[0-9]*/, _, $2)
         split ($2, S, "")
         for (t in T) $2 = $2 + sprintf ("%f", S[t]"1" * T[t])
        }
1
' FS="|" OFS="|" file
rec_10|1
rec_20|10
rec_30|911
rec_40|-911

These 2 Users Gave Thanks to RudiC For This Post:
# 6  
Quote:
Originally Posted by RudiC
Different approach, not sure if better/faster:
Code:
awk '
        {n = split ($2, T, "[+-]")
         gsub (/[0-9]*/, _, $2)
         split ($2, S, "")
         for (t in T) $2 = $2 + sprintf ("%f", S[t]"1" * T[t])
        }
1
' FS="|" OFS="|" file
rec_10|1
rec_20|10
rec_30|911
rec_40|-911

This is a nice approach, and works fine with gawk and other awk implementations that use FS="" to split each input character into a field. A similar approach that doesn't depend on this behavior (which the standards say produces unspecified results) would be:
Code:
awk '
        {n = split($2, T, "[+-]")
         split($2, S, "[0-9.]+")
	 $2 = 0
         for(i = 1; i < n; i++) $2 = $2 + sprintf ("%f", S[i+1]"1" * T[i])
        }
1
' FS="|" OFS="|" file

which also has a little feature creep allowing a decimal point to be included in the input numbers (sparked by RudiC's code using %f instead of %d in the sprintf() format string).

If you change:
Code:
         gsub (/[0-9]*/, _, $2)

in RudiC's script to:
Code:
         gsub (/[0-9.]*/, _, $2)

his script will also handle floating point values in awk versions where:
Code:
         split ($2, S, "")

doesn't produce a syntax error.
This User Gave Thanks to Don Cragun For This Post:
# 7  
The problem reminds me of a rudimentary RPN calculator that I recently did. As it expects operands and operators in separate fields, I added a gsub with spaces at the begin of each step:
Code:
awk '
function CALC(X, Y)     {if (OP == 1)   return X + Y
                         if (OP == 2)   return X - Y
                         if (OP == 3)   return X * Y
                         if (OP == 4)   return X / Y
                        }

        {gsub (/[|+-]/, " & ")
         for (i=2; i<=NF; i++)  {OP = index ("+-*/", $i)
                                 if (OP) STK[--PNT] = CALC(STK[PNT], STK[PNT+1])
                                 else    STK[++PNT] = $i
                                }
         if (PNT == 1) print $1 "|" STK[PNT]
         else print "invalid"
         PNT = 0
        }
' file
rec_10|1
rec_20|10
rec_30|911
rec_40|-911

( and, in principle, it would need a 0 operand in the beginning. It uses the "|" now resulting in a zero value to start with)
 

Previous Thread | Next Thread
Thread Tools Search this Thread
Search this Thread:
Advanced Search

Test Your Knowledge in Computers #251
Difficulty: Easy
MILNET was designed for highly classified military communications.
True or False?

10 More Discussions You Might Find Interesting

1. How to Post in the The UNIX and Linux Forums

How to get defined precision after arithmetic operation using syncsort?

I have to do some arithmetic operation on Field 8 which is calculated by Field 9/Field 7 Suppose i have data like : 0800123456|JAN|2017|JAN|2018|0800123456|0|0.0000|0.00| 0800234567|JAN|2017|JAN|2018|0800234567|4|2.5812|10.32| 0800666666|JAN|2017|JAN|2018|0800666666|2|1.7255|3.45|... (0 Replies)
Discussion started by: pumrao
0 Replies

2. Shell Programming and Scripting

Using awk to do arithmetic operation

Hi, I've this following text file FileVersion = 1.03 Filetype = meteo_on_curvilinear_grid TIME = 0 hours since 2016-10-03 12:00:00 +00:00 -6.855 -6.828 -6.801 -6.774 -6.747 -6.719 -6.691 -6.663 -6.634 -6.606 -6.577 -6.548 -6.519 -6.489 TIME = 0 hours since... (2 Replies)
Discussion started by: xisan
2 Replies

3. Shell Programming and Scripting

Arithmetic operation in variable

Hi, Here is the script i try to perform arithmetic operation in two variables . git branch -r | while read brname ; do REV_COMMITS=`git rev-list --count $brname` echo "$brname has $REV_COMMITS" (( TOTAL = TOTAL + REV_COMMITS )) echo "in loop" $TOTAL done echo "total is " $TOTAL ... (3 Replies)
Discussion started by: greet_sed
3 Replies

4. Shell Programming and Scripting

floating point arithmetic operation error

I am writing a script in zsh shell, it fetchs a number from a file using the awk command, store it as a variable, which in my case is a small number 0.62000. I want to change this number by multiplying it by 1000 to become 620.0 using the command in the script var2=$((var1*1000)) trouble is... (2 Replies)
Discussion started by: piynik
2 Replies

5. Shell Programming and Scripting

Arithmetic operation between columns using loop variable

Hi I have a file with 3 columns. say, infile: 1 50 68 34 3 23 23 4 56 ------- ------- I want to generate n files from this file using a loop so that 1st column in output file is (column1 of infile/(2*n+2.561)) I am doing like this: for ((i=1; i<=3; i++)) do a=`echo... (3 Replies)
Discussion started by: Surabhi_so_mh
3 Replies

6. Shell Programming and Scripting

Arithmetic operation with awk

I have output like following in a file usmtnz-dinfsi19 72 71 38 1199 1199 0.8 19:23:58 usmtnz-dinfsi19 72 71 38 1199 1199 0.8 19:24:04 (9 Replies)
Discussion started by: fugitive
9 Replies

7. Shell Programming and Scripting

How to perform arithmetic operation on date

Hi all, I would appreciate if anyone knows how to perform adding to date. As for normal date, i can easily plus with any number. But when it comes to month end say for example 28 Jun, i need to perform a plus with number 3, it will not return 1 Jul. Thanks in advance for your help. (4 Replies)
Discussion started by: agathaeleanor
4 Replies

8. UNIX for Dummies Questions & Answers

arithmetic operation on two columns

Hi, All, I have a file, its content is as follows: 100 150 120 135 140 170 I want to insert a column, its content is determined by the difference between the two values in the same line, if the difference is less than 20, the new value is 1, otherwise is 0. after the operation, the... (1 Reply)
Discussion started by: Jenny.palmy
1 Replies

9. Shell Programming and Scripting

Help with arithmetic operation

I am using egrep to extract numbers from a file and storing them as variables in a script. But I am not able to do any arithmetic operations on the variables using "expr" because it stores them as char and not integers. Here is my code and the error I get. Any help will be appreciated. #!/bin/sh... (3 Replies)
Discussion started by: emjayshaikh
3 Replies

10. Shell Programming and Scripting

Simple arithmetic operation

I have no idea why I can't get this to work, if anybody can help i would appreciate it. #!/bin/bash x=`cat counter.txt | wc -l` y= '$x / 7' printf "%d People have visited this page" $y :confused: (2 Replies)
Discussion started by: paladyn_2002
2 Replies

Featured Tech Videos