2

I have a shell script to run several times a .c program ('switch') which admit 3 input paramenters. I want to run the program 4 times passing values {4,16,32,64}, and for parameters {0.1,0.2,.3,.4,.5,.55,.575,.6,.625,.65,.7,.75,.8,.9}, building a table of average and confidence interval. The script is the following


#!/bin/bash  

if [ -e salida.txt ]; then  
  # echo "File exists"  
  rm salida.txt  
fi  
touch salida.txt  

touch sal1.txt  
touch sal2.txt  
touch sal3.txt  
touch sal4.txt  
touch salida.txt  

num=1000000  
stud=3.182  
    for j in {0.1,0.2,.3,.4,.5,.55,.575,.6,.625,.65,.7,.75,.8,.9}  
    do  
    rm sal1.txt sal2.txt sal3.txt sal4.txt  
    for i in {4,16,32,64}  
    do  
        margen=0  
        sum=0  
        avg=0  
        for k in {1..4}  
        do  
            a$k=$((`./switch -N$i -r$j -n$num`))  
            sum=$((sum + a$k))  
        done  
        avg=$((sum/4))  
        dvt=0  
        for k in {1..4}  
        do  
            dvt=$((dvt + (a$k - $avg)*(a$k - $avg)))  
        done  
        dvt=$((dvt /3))   
        dvt=$((echo `sqrt($dvt) | bc -l`))  
        margen=$((dvt*$stud/2))  
        echo $avg $margen >> sal$i.txt   
    done  
    join sal1.txt sal2.txt sal3.txt sal4.txt >> salida.txt  
done  

Nevertheless is giving me error in line 27

a$k=$((`./switch -N$i -r$j -n$num`))

and lines

dvt=$((echo `sqrt($dvt) | bc -l`))  
margen=$((dvt*$stud/2))  

What am I doing wrong?

jimmij
  • 46,064
  • 19
  • 123
  • 136
Pablo
  • 83
  • 1
  • 6
  • 1
    `a$k=$((`./switch -N$i -r$j -n$num`))` is not a good var name - the parser won't handle it correctly in that context without an `eval`. Better, though, would be to assign it like: `: $((a$k=$(./switch "-N$i" "-r$j" "-n$num")))`. – mikeserv Oct 10 '14 at 17:24
  • your scope for `a$k` only lasts as long as you're in the for loop where you're setting them. You need to set them outside the for loop prior if you want to use them later on. – slm Oct 10 '14 at 17:24
  • Bash also provides arrays for storing data: http://stackoverflow.com/questions/1494178/how-to-define-hash-tables-in-bash & here: http://wiki.bash-hackers.org/syntax/arrays#associative_bash_4 – slm Oct 10 '14 at 17:31
  • I'm fairly sure it's all to do with `a$k`. It looks like the errors you mention for the latter two lines will likely be division by zero errors. The shell doesn't expand variables in assignment contexts - left or right side of the equal sign they remain unexpanded. So `a$k=?` doesn't work, and your references to it in `$dvt` leave it with a zero-value, I bet. – mikeserv Oct 10 '14 at 17:46
  • @mikeserv - in my experimenting, I think you're right. That `a$k` doesn't work on assignent. If you put an `export` in front of it it does though. I'm assuming since it's now an argument to the `export` command the shell, Bash anyways, expands it first. – slm Oct 10 '14 at 17:56
  • @slm - exactly. But POSIX specifies that the shell's *last* priority in a math expansion *must* be the actual math - while all other expansions must occur first. So it's a cheap way to evaluate the name of a var before assigning it. POSIX *also* specifies that `$((x))` and `$(($x))` must evaluate identically *(so long as `$x` is actually defined)* so you can *always* refer to a name like `"$((var$var2))"` resulting in a single name composed of `$var{1,2}`'s values. Of course, that's no good if your var doesn't represent a number. – mikeserv Oct 10 '14 at 18:01
  • 1
    @slm - by the way, I have used it in the past as a *really* cheap way to verify a value as a proper shell name: `chknm() { return "$(($1$$=0))"; }; chknm "$var" && eval "_$var=\$val"`. – mikeserv Oct 10 '14 at 18:24

2 Answers2

1

I would restructure the setting of a$k to an associative array like this:

    declare -a a
    for k in {1..4}  
    do  
        a[$k]=$((`./switch -N$i -r$j -n$num`))  
        sum=$((sum + a[$k]))  
    done  

This would allow for the array, a to be accessible after it's populated with values from the above for loop to other loops further down in your code.

This line further down int the last for loop also needs to be adjusted like so:

        dvt=$((dvt + (a[$k] - $avg)*(a[$k] - $avg)))  

Floats with $((..))

I do not believe you can add floats using the $((...)) notation. At least when I tried it, it did not work:

$ echo $((0.10 + .20))
bash: 0.10 + .20: syntax error: invalid arithmetic operator (error token is ".10 + .20")

To perform those operations with floats as tokens you'll need to use bc or a calculator that can perform these.

$ echo "0.10 + .20" | bc
.30
slm
  • 363,520
  • 117
  • 767
  • 871
  • `sum=$((sum+$((a$k=$(./switch "-N$i" "-r$j" "-n$num")))))` would be portable - and likely faster if it really is `bash`. – mikeserv Oct 10 '14 at 17:51
  • @mikeserv - true! My approach only works if it's Bash v4 or higher. – slm Oct 10 '14 at 17:58
  • Thank you for the answers. I still have error in line a[$k]=$((`./switch -N$i -r$j -n$num`)) telling 0.100098: syntax error: invalid arithmetic operator (error token is ".100098") – Pablo Oct 10 '14 at 18:43
  • @Pablo - check that `$((..))` can do floats, my testing shows that it cannot. – slm Oct 10 '14 at 18:47
  • definitely no floats - which is likely to come up again later with the `bc` thing I think unless the output radix is addressed. Or maybe not... Can a `sqrt` be a float? hmmm... I guess I gotta get out the old text books or something... – mikeserv Oct 10 '14 at 18:51
  • @mikeserv - yeah I almost never use `$((..))` it's easier to just use `bc` or something else. – slm Oct 10 '14 at 18:53
  • Lots of calcuators to choose from here: [Command line expression solver?](http://unix.stackexchange.com/questions/105111/command-line-expression-solver/105116#105116) – slm Oct 10 '14 at 18:55
1

The first two lines you identified are using $((command)) to execute a command and capture its output.  That should be $(command).  Your uses of $((expression)) to evaluate a mathematical expression are correct.

Also, the square root line should be

dvt=$(echo "sqrt($dvt)" | bc -l)

rather than

dvt=$((echo `sqrt($dvt) | bc -l`))

or even

dvt=$(echo `sqrt($dvt) | bc -l`)

The problem with the margen= command may be that you’re passing it a float as one of the terms of the expression.