The following works great on the command-line:

$ ffmpeg -i input.m4a -metadata 'title=Spaces and $pecial char'\''s' output.m4a

How do I parameterize this command and use it in a script/function? I would like to add multiple metadata tags like this:

$ set-tags.sh -metadata 'tag1=a b c' -metadata 'tag2=1 2 3'


I simplified my question a little too much. I actually want to call a script that calls a script with with the parameterized command in it.

This is my exact use case:

This function converts files to audio-book format (defined in .profile):

# snippet of .profile
convert_to_m4b () {
    BASENAME=${FILE%.*}; shift

    ffmpeg -i "$FILE" -vn -ac 1 -ar 22050 -b:a 32k "$@" tmp.m4a &&
    mv tmp.m4a "$BASENAME.m4b"
}; export -f convert_to_m4b

Function convert_to_m4b is called from download-and-convert.sh:

 MP3_URL=$1; shift
FILENAME=$1; shift

if [ ! -f "${FILENAME}.mp3" ]; then
    curl --location --output "${FILENAME}.mp3" "$MP3_URL"

convert_to_m4b "${FILENAME}.mp3" "$@"

Download-and-convert.sh is called from process-all.sh:

download-and-convert.sh http://1.mp3 'file 1' -metadata 'title=title 1' -metadata 'album=album 1'
download-and-convert.sh http://2.mp3 'file 2' -metadata 'title=title 2' -metadata 'album=album 2'
download-and-convert.sh http://3.mp3 'file N' -metadata 'title=title N' -metadata 'album=album N'

I get this error from ffmpeg:

[NULL @ 00000000028fafa0] Unable to find a suitable output format for ''@''
'@': Invalid argument

"$@" works if I inline convert_to_m4b in download-and-convert.sh instead of calling the function.

The following does not work because the quotes are lost, causing arguments with spaces to be incorrectly split up:

ffmpeg -i input.m4a $@ output.m4a

I have tried various methods of quoting the $@, but this ends up quoting '-metadata' as well, so the command line argument is not properly recognized.

I guess I would like to only surround each argument with quotes if that argument was quoted to begin with. This seems difficult to do because bash strips the quotes before being passing arguments to the script/function.

Or is there a better method of relaying the -metadata arguments? (like environment variables or files)

  • 2
    Have some faith man, ffmpeg -i input.m4a "$@" output.m4a does what you want. :) Also, -metadata "title=It's got spaces" is easier to write than -metadata 'title=It'\''s got spaces'. Sep 6, 2017 at 5:25
  • @Satō please turn that into an answer, it’s much better than the current answers :-/. Sep 6, 2017 at 5:37
  • @SatōKatsura: I actually have one additional level of indirection that prevents "$@" from working. Will update question...
    – Leftium
    Sep 6, 2017 at 5:39
  • The solution is still "$@". Change the upper layer scripts to take this into account, not the other way around. Unquoted $@ is almost never what you want (it certainly isn't here). Sep 6, 2017 at 5:44
  • @SatōKatsura: Can you explain how to change the upper layer scripts? I tried changing the upper layer scripts to "$@" as well, but I got an error where $@ did not seem to be expanded. BTW I am using single quotes so special charactes like '$' do not get expanded if they are part of the metadata.
    – Leftium
    Sep 6, 2017 at 5:51

1 Answer 1


"$@" does exactly what you want provided that you use it consistently. Here's a little experiment for you:

  • script1.sh:

    #! /bin/sh
    ./script2.sh "$@"
  • script2.sh:

    #! /bin/sh
    ./script3.sh "$@"
  • script3.sh:

    #! /bin/sh
    printf '|%s|\n' "$@"

With this the arguments stay unmolested all the way down:

$ ./script1.sh -i input.m4a -metadata "title=Spaces and \$pecial char's" output.m4a
|title=Spaces and $pecial char's|

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .