7

I'm quite new to bash scripting and usually avoid it all costs but I need to write a bash script to execute some simple things on a remote cluster. I'm having problems with a for loop that does the following:

for i in {1..20}
do
    for j in {1..20}
    do
        echo (i*i + j*j ) **.5  <--- Pseudo code!
    done
done

Can you help me with this simple math? I've thrown $'s everywhere and can't write it properly. If you could help me understand how variables are named/assigned in bash for loops and the limitations of bash math interpretation (how do you do the square root?) I'd be very grateful. Thanks!

11 Answers 11

12

Arithmetic expansion needs $((...)) notation, so something like:

echo $((i*i + j*j))

However, bash only uses integers so you may need to use an external tool such as dc.

E.g.

dc -e "18k $i $i * $j $j * + v p"
Sign up to request clarification or add additional context in comments.

Comments

8

Here's a decent solution:

for i in {1..20}
do
   for j in {1..20}
   do
       echo "scale = 3; sqrt($i*$i + $j*$j)" | bc
   done
done

Output will be:

1.414
2.236
3.162
2.236
[...etc...]

Comments

2

Use double paren to evaluate a variable.

variableA=$((variableB*variableC))

Only for ints though.

Comments

2

Shell math can be done in several ways.

echo $(( i*i + j*j ))
echo $[ i*i + j*j ]
expr "$i" '*' "$i" '+' "$j" '*' "$j"

However, this can only handle integer arithmetic. Instead, you can use bc:

echo "scale = 5; sqrt( $i*$i + $j*$j)" | bc

Change scale to the number of decimal places desired.

3 Comments

You're probably missing the .. between your 1 and 20 in your {1..20} range
Weird, it should work in any POSIX bc. What if you use dc? i.e. dc -e "5 k $i $i * $j $j * + v p"
Bah. I shorted the j loop so it wouldn't flood the screen and introduced a typo.
2
#!/bin/bash
for i in {1..20}; do
    for j in {1..20}; do
        echo 5k$i $i\* $j $j\*+vp | dc
    done
done

1 Comment

My variant: printf "%s %s 10kd*rd*+vp" "$i" "$j" | dc
1

The code

echo $[(($i * $i) + ($j * $j)) ** $X]

will work if $X is an integer. You're trying to take the square root, and I'm not sure if bash's built-in arithmetic will do that. You'll probably be better off using a more powerful calculator tool (like bc, et al.) for this.

Comments

1

does your remote cluster only have bash? if not, try and see if you have awk

awk 'BEGIN{
  for(i=1;i<=20;i++){
    for(j=1;j<=20;j++){
       print ( i*i + j*j ) ** 0.5
    }
  }
}'

Comments

0

Typically you would use $((1*3)), but your case won't work as bash doesn't support floating point numbers. You'll have to use an external tool like awk, bc or dc: http://mywiki.wooledge.org/BashFAQ/022

Comments

0

Bash doesn't offer mathematical functions. However, you almost certainly have the korn shell installed. This should work:

#!/bin/ksh

for i in {1..20}
do
    for j in {1..20}
    do
        x=$((sqrt(i*i + j*j)))
        echo "sqrt($i^2 + $j^2) = $x"
    done
done

The beginning of the output is

sqrt(1^2 + 1^2) = 1.41421356237309505
sqrt(1^2 + 2^2) = 2.2360679774997897
sqrt(1^2 + 3^2) = 3.16227766016837933
sqrt(1^2 + 4^2) = 4.12310562561766055
sqrt(1^2 + 5^2) = 5.09901951359278483
sqrt(1^2 + 6^2) = 6.08276253029821969
sqrt(1^2 + 7^2) = 7.07106781186547524
sqrt(1^2 + 8^2) = 8.06225774829854965
sqrt(1^2 + 9^2) = 9.05538513813741663
sqrt(1^2 + 10^2) = 10.0498756211208903

3 Comments

"almost certainly"? Certainly not standard on any Linux distribution I've seen. Perhaps you mean *BSD?
ksh is on Fedora and CentOS.
It's available as a package, but not part of the default install. Most Linux systems use Bash as the default shell; the BSDs tend to gravitate around various non-GNU shells.
0

with zsh, this will work

for i in {1..20};do
  for j in {1..20};do
     echo $((($i*$i + $j*$j)**.5))
  done
done

Comments

-1

Another form of integer math expressions in Bash puts the double parentheses on the outside of the entire expression for assignment operations:

(( var = i ** 2 ))
(( i++ ))
(( position += delta ))

As you can see, dollar signs are not needed here (nor inside $(())). Also, spaces are permitted around the equal sign.

Also, this form can be used in conditionals:

sixpacks=8             # note spaces not allowed here
(( beerprice = 8 ))    # but you can use spaces this way
budget=50

# you can do assignments inside a conditional just like C
until (( (( beertotal = sixpacks * beerprice )) <= budget ))
do
    (( sixpacks-- ))
done
echo "Buy ${sixpacks} six-packs at \$${beerprice} each for a total of \$${beertotal}."

Or you can replace all that with this, of course:

beerprice=8
budget=50

# integer division
(( sixpacks = budget / beerprice ))
(( beertotal = sixpacks * beerprice ))
echo "Buy ${sixpacks} six-packs at \$${beerprice} each for a total of \$${beertotal}."

Bash also has a let statement:

let a=2**16
let 'a>>=1'    # bitwise shift - some operations need to be quoted or escaped
(( a>>=1 ))    # but not inside (())

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.