3

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.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
Urphänomen
  • 81
  • 1
  • 8
  • What does the `starts` array look like? Does it have the same number of elements as `startTimes`? Is this your whole script? How do you run it? What do you mean by "adding `#!/bin/bash` in the command line"? – Benjamin W. Aug 28 '17 at 21:22
  • Sorry, bad copy/paste. I just fixed it. It's supposed to be `startTimes`. I don't have the whole script yet, just testing out commands one at a time in the command line to make sure my script will run (if there's a better method I'm all ears). For the shebang, if I enter all of this `#!/bin/bash; for...(etc)...; done` in the command line I get no parse error -- but also no accessible results. I'm assuming it's running in another shell that I can't access? Still learning, obviously. – Urphänomen Aug 28 '17 at 21:28
  • And if it helps, `startTimes` looks like this: `0 87053 91463 190062 194472 290520 294930 387582 391992`. Just a bunch of integers. – Urphänomen Aug 28 '17 at 21:30

2 Answers2

3

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.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
  • Thanks very much! Can you elaborate on the importance of quotes for the here-string? I'm still fuzzy on quote usage, in general. – Urphänomen Aug 28 '17 at 22:04
  • @Urphänomen I've added a link to a good article about quoting. The rule of thumb is really "if in doubt, quote". – Benjamin W. Aug 28 '17 at 22:11
  • 1
    Excellent, thank you @Benjamin W. An overview is just what I need, much appreciated. – Urphänomen Aug 28 '17 at 22:18
  • @Urphänomen Benjamin's solution of `for t in "${startTimes[@]}"` appears to me to be a good one. I recommend that you accept his answer. – John1024 Aug 29 '17 at 04:45
3

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
John1024
  • 109,961
  • 14
  • 137
  • 171