Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I am trying to perform a mathematical operation with sed, but it continues to treat my variables as strings. The input is of this kind:

$ echo 12 | sed 's/[0-9]*/&+3/'
$ 12+3

I'd like to have 15 as output. I need to do the operation and replace its mathematical result in only one passage, because I am running the program as a Python daemon, and I want to avoid passages like redirecting stdout on files, open those files, perform operations, extract the result, do the replacement. To me, sed seems the best to perform all in one line.

I've tried to cast both input and output in various ways like

$ echo 12 | sed 's/[0-9]*/int(&+3)/'
$ echo 12 | sed 's/[0-9]*/\int(&+3)/'
$ echo 12 | sed 's/[0-9]*/\int(&+3)/'

but the result was always a printing of the second field.

share|improve this question
8  
It's treating your "variables" as strings because that's all sed does - string manipulation. It has no concept of "integer." –  Kevin Apr 20 '12 at 16:10
    
I'm very curious why you want to use sed to do math –  David Oneill Apr 21 '12 at 13:43
    
I just thought it could easily cast variables, didn't realize was so complex! –  Luigi Tiburzi Apr 22 '12 at 16:56

9 Answers 9

up vote 45 down vote accepted

If you honestly want to use sed, then this is the way to go:

s/[0-9]/<&/g
s/0//g; s/1/|/g; s/2/||/g; s/3/|||/g; s/4/||||/g; s/5/|||||/g; s/6/||||||/g
s/7/|||||||/g; s/8/||||||||/g; s/9/|||||||||/g
: tens
s/|</<||||||||||/g
t tens
s/<//g
s/+//g
: minus
s/|-|/-/g
t minus
s/-$//
: back
s/||||||||||/</g
s/<\([0-9]*\)$/<0\1/
s/|||||||||/9/; s/||||||||/8/; s/|||||||/7/; s/||||||/6/; s/|||||/5/; s/||||/4/
s/|||/3/; s/||/2/; s/|/1/
s/</|/g
t back

Input:

1+2
100+250
100-250

Output:

3
350
-150

Your mission, should you choose to accept it, is to implement multiplication.

share|improve this answer
2  
+1 for the challenge, love it! Maybe that'd be something for Code Golf ;-p –  Tatjana Heuser Apr 20 '12 at 14:15
2  
And some people say that programming is not mathematics. This little gem refutes them all. Best use of Base 1 ever. –  Bruce Ediger Apr 20 '12 at 15:42
1  
Nice one! - @Simon: I challenge you to implement tetration :P –  A T Apr 20 '12 at 17:45
6  
+1 This is a beautiful example of what a misconception paired with creativity can breed. –  rozcietrzewiacz Apr 20 '12 at 19:28
1  
Great! I don't understand the syntax very well but only looking at it is awesome! I'm convinced sed is not the way to go :-), thank you!!! –  Luigi Tiburzi Apr 22 '12 at 16:53

sed isn't the best option here, it doesn't do arithmetics natively (see Increment a number for how you could possibly do it though). You could do that with awk:

$ echo 12 | awk '{print $0+3}'
15

The best piece of code to use will depend on the exact format of your input and what you want/need to do if it is not numeric, or contains more than one number, etc.

You could also do this only with bash:

$ echo $(( $(echo 12) + 3 ))

or using expr in a similar fashion.

share|improve this answer

perl allows for a very similar construct to sed's ... one difference is that perl can do more complex things... sed is very good for simple text substitions

 echo 'a12' | perl -pe 's/([0-9]+)/($1+3)/e'  # the trailing /e means evaluate

output

a15
share|improve this answer
2  
can also do this without the capturing parentheses: perl -pe 's/[0-9]+/$&+3/e' –  glenn jackman Apr 20 '12 at 13:12

I tried to accept your challenge @Richter, this is what I did using part of your code:

sed 's/[0-9]/<&/g
s/0//g; s/1/|/g; s/2/||/g; s/3/|||/g; s/4/||||/g; s/5/|||||/g; s/6/||||||/g
s/7/|||||||/g; s/8/||||||||/g; s/9/|||||||||/g
: tens
s/|</<||||||||||/g
t tens
s/<//g
s/.*\*$/0/
s/^\*.*/0/
s/*|/*/
: mult
s/\(|*\)\*|/\1<\1*/ 
t mult
s/*//g
s/<//g
: back
s/||||||||||/</g
s/<\([0-9]*\)$/<0\1/
s/|||||||||/9/; s/||||||||/8/; s/|||||||/7/; s/||||||/6/; s/|||||/5/; s/||||/4/
s/|||/3/; s/||/2/; s/|/1/
s/</|/g
t back'

Input:

04*3
4*3
40*3
42*32
150*20
1*3
3*1
0*3
3*0

Output: all the correct results

share|improve this answer
    
@SimonRichter hope you'll enjoy!! –  Luigi Tiburzi Apr 24 '12 at 14:56

just feed the string into a calculator

 echo 12 | sed 's/[0-9]*/&+3/' | bc
share|improve this answer
    
this won't work if there's text amongst the numbers. –  glenn jackman Apr 21 '12 at 12:25

If you definitely have to combine regular expressions and arithmetic operations, choose a language where the regular expression's replacement parameter can be a callback function.

Perl, Ruby, JavaScript and Python are such languages:

bash-4.2$ echo 12 | perl -pe 's/\d+/$&+3/e'
15

bash-4.2$ echo 12 | ruby -pe '$_.sub!(/\d+/){|s|s.to_i+3}'
15

bash-4.2$ echo 12 | js -e 'print(readline().replace(/\d+/,function(s){return parseInt(s)+3}))'
15

bash-4.2$ echo 12 | python -c 'import re;print re.sub("\d+",lambda s:str(int(s.group(0))+3),raw_input())'
15
share|improve this answer

I really don't get why the extreme complexity of the accepted answer, either of the below do what you want:

echo 12 | sed 's/[0-9]*/echo \$(( & + 3 ))/e'

or

echo 12 | sed 's/[0-9]*/expr & + 3/e'

I think it might require GNU sed, but I am not sure.

share|improve this answer
    
It is a gnu extension. –  Kevin Mar 30 '13 at 17:02
    
Ok you are right but the answer goes beyond, it implements the general addition not a particular one, you can feed any two numbers and you will get the result –  Luigi Tiburzi Apr 1 '13 at 18:56
    
@LuigiTiburzi Its fairly straightforward to generalize this to "x+y" style input: echo 12+3 | sed -r 's/([0-9]*) *\+ *([0-9]*)/expr \1 + \2/e' –  DigitalTrauma Oct 15 at 18:28

Another simple bash solution, that actually works in a pipe:

 echo 12 | { read num; echo $(( num + 3)); }
share|improve this answer

If you mix in some bashism:

echo $(($(echo 12 | sed 's/[0-9]*/&+3/')))

To extract the number from a text:

echo $(($(echo "foo12bar" | sed -r 's/[^0-9]*([0-9]*).*/\1+3/')))

Without sed, just bash:

var="foo12bar"
echo $((${var//[^0-9]/}+3))

replaces every non-digit ${var//[^0-9]/} and does arithmetic in double round parens: $((x+3))

share|improve this answer
1  
There's no bashism in there. $((...)) was introduce by POSIX (the bashism is $[...]). ${var//xxx/x} is a kshism also copied by zsh and bash. sed -r is a GNUism –  Stéphane Chazelas Sep 18 '12 at 22:06

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.