35

I want to take the absolute of a number by the following code in bash:

#!/bin/bash echo "Enter the first file name: " read first echo "Enter the second file name: " read second s1=$(stat --format=%s "$first") s2=$(stat -c '%s' "$second") res= expr $s2 - $s1 if [ "$res" -lt 0 ] then res=$res \* -1 fi echo $res 

Now the problem I am facing is in the if statement, no matter what I changes it always goes in the if, I tried to put [[ ]] around the statement but nothing.

Here is the error:

./p6.sh: line 13: [: : integer expression expected 
2
  • 1
    one of the easier ways could be adding extra line after calculating the $res res=echo $res | tr -d - Commented Sep 25, 2017 at 11:25
  • 1
    why not res=$(s2 - s1); res=${res#-}? Commented Dec 7, 2021 at 17:20

9 Answers 9

84

You might just take ${var#-}.

${var#Pattern} Remove from $var the shortest part of $Pattern that matches the front end of $var. tdlp


Example:

s2=5; s1=4 s3=$((s1-s2)) echo $s3 -1 echo ${s3#-} 1 
Sign up to request clarification or add additional context in comments.

2 Comments

This is great for getting a positive number out of $(( 2**63 )).
I think this point by @ltrainpr is that bash overflows as a signed int at this number: care should be taken whether or not the absolute magnitude is computed. I mean: if you've already overflowed, you've already got a problem.
8
$ s2=5 s1=4 $ echo $s2 $s1 5 4 $ res= expr $s2 - $s1 1 $ echo $res 

What's actually happening on the fourth line is that res is being set to nothing and exported for the expr command. Thus, when you run [ "$res" -lt 0 ] res is expanding to nothing and you see the error.

You could just use an arithmetic expression:

$ (( res=s2-s1 )) $ echo $res 1 

Arithmetic context guarantees the result will be an integer, so even if all your terms are undefined to begin with, you will get an integer result (namely zero).

$ (( res = whoknows - whocares )); echo $res 0 

Alternatively, you can tell the shell that res is an integer by declaring it as such:

$ declare -i res $ res=s2-s1 

The interesting thing here is that the right hand side of an assignment is treated in arithmetic context, so you don't need the $ for the expansions.

6 Comments

@AliSajid no, the error is at if. The problem is before if, when you never actually assign a value to res.
abs $res => ${res/#-/} :)
@rici heh. I thought of that, but ruled it out because OP said by the following code. But yeah…
@kojiro $ s2=5 s1=4 $ echo $s2 $s1 5 4 $ res= expr $s1 - $s2 -1 $ echo $res {empty line}
Sorry dude) Looks like you answer is outdated. Anyway rici answer helped me: ${VAR1#-} works fine
|
6

I know this thread is WAY old at this point, but I wanted to share a function I wrote that could help with this:

abs() { [[ $[ $@ ] -lt 0 ]] && echo "$[ ($@) * -1 ]" || echo "$[ $@ ]" } 

This will take any mathematical/numeric expression as an argument and return the absolute value. For instance: abs -4 => 4 or abs 5-8 => 3

1 Comment

$(( ... )) is the preferred syntax for arithmetic contexts rather than $[ ... ]. And this can be significantly simplified into a single arithmetic context: abs() { echo $(( $1 > 0 ? $1 : -$1 )); }
4

I translated this solution to bash. I like it more than the accepted string manipulation method or other conditionals because it keeps the abs() process inside the mathematical section

abs_x=$(( x * ((x>0) - (x<0)) )) x=-3 abs_x= -3 * (0-1) = 3 x=4 abs_x= 4 * (1-0) = 4 

Comments

2

A workaround: try to eliminate the minus sign.

  1. with sed
x=-12 x=$( sed "s/-//" <<< $x ) echo $x 12 
  1. Checking the first character with parameter expansion
x=-12 [[ ${x:0:1} = '-' ]] && x=${x:1} || : echo $x 12 

This syntax is a ternary opeartor. The colon ':' is the do-nothing instruction.

  1. or substitute the '-' sign with nothing (again parameter expansion)
x=-12 echo ${x/-/} 12 

Personally, scripting bash appears easier to me when I think string-first.

Comments

1

The simplest solution:

res="${res/#-}" 

Deletes only one / occurrence if - is at the first # character.

2 Comments

Sure its the simplest solution which is the solution oi the highest rated answer submitted 4+ years before yours ¯_(ツ)_/¯
I hadn't noticed.
1

This simple one works for floating point numbers:

echo "sqrt($var^2)" | bc

2 Comments

Hey! The solution with variable substitution is the best but yours is nice to implement a abs() function in bc.
I wonder if it’s more costly than a "if below zero then multiply by minus one" ?
0

If you don't care about the math and only the result matters, you may use

echo $res | awk -F- '{print $NF}' 

Comments

-1

For the purist, assuming bash and a relatively recent one (I tested on 4.2 and 5.1):

abs() { declare -i _value _value=$1 (( _value < 0 )) && _value=$(( _value * -1 )) printf "%d\n" $_value } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.