0

I am trying to write a parameter driven routine to extract parts of audio files using ffmpeg. Because the routine is parameter driven I end up with a number of options in variables (a technique I have used successfully before in simpler examples) and for some reason this time it isn't working. having stared at it and tried various experiments for hours I give up and hope the helpful experts can sort me out This is a simplified version with the variables set directly

...

#!/bin/bash

a="a b c.mp3"
b="out-$a"

trackstring="-metadata track=\"07/93\""
echo "trackstring=$trackstring"

titlestring="-metadata title=\"$a\""
echo "titlestring=$titlestring"

startpoint="-ss 0"
echo "startpoint=$startpoint"

endpoint="-to 300"
echo "endpoint=$endpoint"

coverstring="-c:v copy"
echo "coverstring=$coverstring"

audiostring="-c:a libmp3lame -ab 32k -ac 1"
echo "audiostring=$audiostring"

echo "ffmpeg $startpoint $endpoint -i \"$a\" -hide_banner -loglevel warning $coverstring $audiostring $titlestring $trackstring \"$b\""

ffmpeg $startpoint $endpoint -i "$a" -hide_banner -loglevel warning $coverstring $audiostring $titlestring $trackstring "$b"

...

The resulting output from my script looks like this:

trackstring=-metadata track="07/93"

titlestring=-metadata title="a b c.mp3"

startpoint=-ss 0

endpoint=-to 300

coverstring=-c:v copy

audiostring=-c:a libmp3lame -ab 32k -ac 1

ffmpeg -ss 0 -to 300 -i "a b c.mp3" -hide_banner -loglevel warning -c:v copy -c:a libmp3lame -ab 32k -ac 1 -metadata title="a b c.mp3" -metadata track="07/93" "out-a b c.mp3"

Which gives me exactly what I am expecting and I think all valid BUT.... Then ffmpeg gives me:

[mp3 @ 0x55ae679e4640] Estimating duration from bitrate, this may be inaccurate

[NULL @ 0x55ae679ea0c0] Unable to find a suitable output format for 'b'

b: Invalid argument

Peter McN
  • 31
  • 2
  • This might help: [How do I format my posts using Markdown or HTML?](https://stackoverflow.com/help/formatting). – Cyrus Mar 16 '22 at 16:29
  • 2
    Storing shell syntax (like quotes and escapes) in variables doesn't work (unless you use `eval`, but don't use `eval` -- it's a massive bug magnet). Either just don't store args first, or build the argument list in an array. See ["Why does shell ignore quoting characters in arguments passed to it through variables?"](https://stackoverflow.com/questions/12136948) and [BashFAQ #50: "I'm trying to put a command in a variable, but the complex cases always fail!"](http://mywiki.wooledge.org/BashFAQ/050) Note that you can append to an array with e.g. `args+=(-metadata track="07/93")`. – Gordon Davisson Mar 16 '22 at 17:06

1 Answers1

0

A bad one!

But interestingly putting the whole command into a string and the using an explicit sub-shell works exactly as expected: So starting from the last of the assignments in the original post

...
audiostring="-c:a libmp3lame -ab 32k -ac 1"
echo "audiostring=$audiostring"

cmd="ffmpeg $startpoint $endpoint -i \"$1\" -hide_banner -loglevel warning $coverstring $audiostring $titlestring $trackstring \"$2\""
echo "$cmd"
bash -c "$cmd"

Frankly, although I now have "working code" I think I am more confused than before.

The output is unchanged (except no error from ffmpeg) and the file(s) are generated exactly as expected

Peter McN
  • 31
  • 2
  • 1
    Don't follow this practice -- that way sits serious security bugs. Read BashFAQ #50, as was linked to you in a comment on the question. – Charles Duffy Mar 16 '22 at 20:36
  • 1
    Insofar as you're describing yourself as confused -- one important thing to understand is the difference between syntactic and literal quotes. Syntactic quotes are, well, syntax -- part of the code you write. Literal quotes are data. Literal quotes don't act like syntactic quotes -- if they did, it would be impossible to write code handling untrusted data in shell. Think of it like any other language -- in C or Java or Python, you don't expect quotes that are part of how you start and end a string, and quotes _inside_ that string, to be interchangeable with each other. – Charles Duffy Mar 16 '22 at 20:38
  • BTW, a lot of the near duplicates have answers that suggest `eval`. Those are just as bad as `bash -c` from a security perspective; see [BashFAQ #48](https://mywiki.wooledge.org/BashFAQ/048). – Charles Duffy Mar 16 '22 at 20:40
  • With respect to what I'm talking about wrt security -- think about what happens if you're trying to split a file created with `touch $'Why $(rm -rf ~)\'$(rm -rf ~)\' is dangerous.mp3'`. You don't want _any_ of those `$(rm -rf ~)`s treated as code, but with this answer they both will be. (The second one is present to demonstrate that the "easy answer" of switching `cmd="...\"$i\"..."` to `cmd="...'$i'..."` isn't good enough). – Charles Duffy Mar 16 '22 at 20:42
  • Thanks Charles, I came across the idea from the other link you included: "Why does shell ignore quoting characters in arguments passed to it through variables?". I don't like it I just was surprised that it worked when the other didn't. What I am still not managing to wrap my mind around is how the array solution you recommend actually works. – Peter McN Mar 16 '22 at 20:46
  • Reading the posts you suggest and others I got the impression that using eval might well turn out like trying to climb out of deep water into a quicksand – Peter McN Mar 16 '22 at 20:52
  • 1
    One tool that might be useful is `set -x`, to make the shell print each command it's about to run _quoted to evaluate to the actual argument list being used when evaluated by a shell_. `echo` doesn't do that (you can't tell the difference between `echo "two" "words"` and `echo "two words"`by comparing output); but with `set -x`, it's easier to understand _why_ a command isn't behaving the way you expect, or at least to understand the problem enough to make searching for answers about it easier. :) – Charles Duffy Mar 16 '22 at 21:12
  • That is a really useful tool -Thanks – Peter McN Mar 16 '22 at 22:08