Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 707
Urphänomen Avatar asked Oct 19 '25 03:10

Urphänomen


2 Answers

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.

like image 122
Benjamin W. Avatar answered Oct 21 '25 17:10

Benjamin W.


You have an off-by-1 problem. Observe that, with your sample startTimes, seq generates 10 numbers:

$ startTimes=(0 87053 91463 190062 194472 290520 294930 387582 391992)
$ seq 0 ${#startTimes[@]}
0
1
2
3
4
5
6
7
8
9

The problem is that startTimes has only 9 entries:

$ declare -p startTimes
declare -a startTimes=([0]="0" [1]="87053" [2]="91463" [3]="190062" [4]="194472" [5]="290520" [6]="294930" [7]="387582" [8]="391992")

When i=9, startTimes[9] evaluates to an empty string and that leads to the bc error that you see:

$ i=9; msValuesArray+=($(bc <<< ${startTimes[i]}/44.1))
(standard_in) 1: syntax error

Or, more directly:

$ bc <<<"/44.1"
(standard_in) 1: syntax error
like image 20
John1024 Avatar answered Oct 21 '25 16:10

John1024



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!