bashshellbcherestring

Bash bc returns (standard_in) 1: parse error only when part of for loop


I'm iterating through an array of integers ${startTimes} (marker locations in an audio file, in samples) and using bc to convert those integers to milliseconds. I'm passing the results into a new array ${msValuesArray}. If I run each array element one at a time it works fine. If I run it in a for loop:

for i in $(seq 0 ${#startTimes[@]}); do 
    msValuesArray+=($(bc <<< ${startTimes[i]}/44.1))
done

The resulting ${msValuesArray} contains the expected results, but the terminal outputs (standard_in) 1: parse error.

While I intend to use this in a shell script, and after reading other questions here I learned that adding #!/bin/bash to the beginning of the command avoids the parse error, I still don't understand the following:

a) why does the manual passing of a ${startTimes} element into bc work without the parse error while the for loop also works, yet outputs the parse error (outside of the shell script)?

b) despite the parse error, I have the resulting array that I want. Should I ignore the error?

c) when adding #!/bin/bash to the beginning of the commands (still outside of the shell script, just in the command line) why are the results inaccessible? (Entering echo ${msValuesArray[@]} returns an empty array.)

d) While running inside the shell script, is the same error happening but just not printing to the terminal?

Any help is appreciated. Thanks.


Solution

  • You can iterate over the array directly instead of going via indices:

    for t in "${startTimes[@]}"; do
        msValuesArray+=($(bc <<< "$t / 44.1"))
    done
    

    This makes the loop easier to read.

    You get a parse error because you're trying to access a non-existing element (see John1024's answer), so bc sees just / 44.1. You shouldn't ignore the error.

    You should quote your here-string, even though in this very instance it doesn't seem to cause problems1.

    If you enter #!/bin/bash just on the command line, it has no effect at all, it's just considered a comment. It only does something as the first line of a script, namely indicate what interpreter should be used. If, as indicated by your comment, you enter the whole thing on a single line as

    #!/bin/bash; for ... (etc) ...
    

    nothing at all happens. It's just a comment.

    Lastly, you're truncating your results. If you want them more precise, you can set scale to a sensible value, as in

    bc <<< "scale = 3; $t / 44.1"
    

    1 Problems such as (unwanted) word splitting and globbing. This article is a good overview about the how and why of quoting.