0

I am running different program with different config. I tried to convert string (kmeans and bayes) in the inner loop to variables I defined at the beginning, so I can run the programs and capture the console output. kmeans_time and bayes_time are used to record execution time of each program.

#!/bin/bash
kmeans="./kmeans -m40 -n40 -t0.00001 -p 4 -i inputs/random-n1024-d128-c4.txt"
bayes="./bayes -t 4 -v32 -r1024 -n2 -p20 -s0 -i2 -e2"
kmeans_time=0
bayes_time=0

for n in {1..10}
do 
    for prog in kmeans bayes
    do
        
        output=($(${prog} | tail -1))
        ${$prog + "_time"}=$( echo $kmeans_time + ${output[1]} | bc)
        echo ${output[1]}
    done
done

However, I got the following errors. It seems that the prog is executed as a string instead of command I defined. Also, concatenation of the time variable filed. I've tried various ways. How is this accomplished in Bash?

./test.sh: line 11: kmeans: command not found
./test.sh: line 12: ${$app + "_time"}=$( echo $kmeans_time + ${output[1]} | bc): bad substitution

What I am trying to do is to execute as follow, which can work properly.

kmeans="./kmeans -m40 -n40 -t0.00001 -p 4 -i inputs/random-n1024-d128-c4.txt"
output=($($kmeans | tail -1))
# output[1] is the execution time
echo "${output[1]}"
kmeas_times=$kmeans_times+${output[1]}

I want to iterate over different programs and calculate each of their average execution time

Yuwei Chu
  • 1
  • 3
  • 1
    On line 11, you're trying to run `kmeans`, not the contents of the variable `$kmeans`. – choroba Jun 02 '21 at 09:09
  • @YuweiChu: What does it mean to _convert a string to a variable_? – user1934428 Jun 02 '21 at 09:21
  • @YuweiChu : I would expect that `${$prog + "_time"}=...` gives you a _bad substitution_ error; at least by bash does. Which version are you using? – user1934428 Jun 02 '21 at 09:24
  • @user1934428 I want to replace `kmeans` with "./kmeans -m40 -n40 -t0.00001 -p 4 -i inputs/random-n1024-d128-c4.txt" – Yuwei Chu Jun 02 '21 at 09:43
  • This would then be `output=$(${!prog} | tail ...)` in line 11, but it still does not make sense in line 12, because you are doing these things there on the left side of the equal sign. – user1934428 Jun 02 '21 at 09:49
  • If my answer doesn't already cover everything in your question, can you please [edit] to clarify where you are still stuck, and (especially!) straighten out the things which several commenters have been trying to ask you about? Code which doesn't do what you want is not a good way to explain what you want, so probably add an explanation for the less clear parts of the code. – tripleee Jun 02 '21 at 09:59
  • @user1934428 Thank you, my bash version is 5.0.17(1)-release, but `output=$(${!prog} | tail ...)` still doesn't work. – Yuwei Chu Jun 02 '21 at 10:16
  • You forgot the parentheses to make the result into an array. Your original code had this right, and I updated the code in the question correspondingly. (Maybe I shouldn't have.) – tripleee Jun 02 '21 at 10:24
  • You still have a syntax error in the `bayes` assignment, there is a stray single quote. But as pointed out elsewhere, saving commands in string variables is going to break as soon as you need quoting in the command. – tripleee Jun 02 '21 at 10:25
  • Sorry that's a typo, I 've corrected it. – Yuwei Chu Jun 02 '21 at 10:33
  • 1
    In absence of a hashbang line, I'd also throw in the question whether your using `bash` or `sh`, which makes a big difference in the fineprint. In any case, first thing to do in such cases is to create a [mcve]! – Ulrich Eckhardt Jun 02 '21 at 10:33

1 Answers1

0

I am vaguely guessing you are looking for printf -v.

The string in bayes is not a valid command, nor a valid sequence of arguments to another program, so I really can't guess what you are hoping for it to do.

Furthermore, output is not an array, so ${output[1]} is not well-defined. Are you trying to get the first token from the line? You seem to have misplaced the parentheses to make output into an array; but you can replace the tail call with a simple Awk script to just extract the token you want.

Your code would always add the value of kmeans_time to output; if you want to use the variable named by $prog you can use indirect expansion to specify the name of the variable, but you will need a temporary variable for that.

Mmmmaybe something like this? Hopefully this should at least show you what valid Bash syntax looks like.

kmeans_time=0
bayes_time=0

for n in {1..10}
do 
    for prog in kmeans bayes
    do
        case $prog in
           kmeans) cmd=(./kmeans -m40 -n40 -t0.00001 -p 4 -i inputs/random-n1024-d128-c4.txt);;
           bayes) cmd=(./bayes -t 4 -v32 -r1024 -n2 -p20 -s0 -i2 -e2);;
        esac
        output=$("${cmd[@]}" | awk 'END { print $2 }')
        var=${prog}_time
        printf -v "$var" %i $((!var + output))
        echo "$output"
    done
done

As an alternative to the indirect expansion, maybe use an associative array for the accumulated time. (Bash v5+ only, though.)

If running the two programs alternatingly is not important, your code can probably be simplified.

kmeans () {
    ./kmeans -m40 -n40 -t0.00001 -p 4 -i inputs/random-n1024-d128-c4.txt
}
bayes () {
    ./bayes -t 4 -v32 -r1024 -n2 -p20 -s0 -i2 -e2
}
get_output () {
    awk 'END { print $2 }'
}
loop () {
    time=0
    for n in {1..10}; do
    do
        output=("$@" | get_output)
        time=$((time+output))
        print "$output"
    done
    printf -v "${0}_time" %i "$time"
}

loop kmeans
loop bayes

Maybe see also http://mywiki.wooledge.org/BashFAQ/050 ("I'm trying to put a command in a variable, but the complex cases always fail").

tripleee
  • 175,061
  • 34
  • 275
  • 318