I am trying to write a script to automatically add track and title info to an mp3 file's id3 tag.

At this point, I have sed grabbing the info from the filename and formatting it to fit the id3v2 command, but upon hitting a multi-word title, I get the error Couldn't stat file '<second word of title>'. If I just echo the id3v2 command that is being run, I see the command in this format:

id3v2 -t Title\ Of\ Track -T 1 1_-_Title_Of_Track.mp3

Copy/pasting that text into the terminal executes tagging properly, but of course lacks the intended automation of scripting. So here's what the script intending to automate the process looks like:


for track in *.mp3 ; do
    id3v2 $(echo ${track} | sed -e 's/^\([1-9][0-9]*\)_-_\(.*\).mp3/-t \2 -T \1/' -e 's/_/\\ /g') ${track}

Any insight as to why the title is treated as a single argument when manually typed but not when scripted?

  • 2
    Use quotes "$track" . Also what is the final goal you want to achieve? – Valentin Bajrami Aug 21 '18 at 14:27
  • The intent is to tag the mp3 files with the track number and title as parsed from the file name. The problem should not be in the file name. The file name itself has no spaces; it uses underscores instead. When I parse the title from the file name, I replace the underscores with \ so as to have proper words for the id3 title tag. – Christian Gibbons Aug 21 '18 at 14:59
  • A wild guess of what you want: for f in *.mp3; do id3v2 --TIT3 "${f%.*}" "$f"; done – Valentin Bajrami Aug 21 '18 at 15:24
  • 1
    Your problem stems from the order of evaluation of commands. $() is done first, then the result is split into words. Add set -x to the start and you will see output like: id3v2 -t 'Title\' 'Of\' Track -T 1 1_-_Title_Of_Track.mp3 where you can see the backslash serves no purpose. – meuh Aug 21 '18 at 16:20
  • 1
    @ValentinB I appreciate the introduction to bash's string manipulation. In the end, I wound up using that feature to parse out the track number and title into variables and pass those as arguments to the id3v2 command. – Christian Gibbons Aug 22 '18 at 3:24

Based on the comments from Valentin B and mueh, I ditched sed for the parsing in the script and instead used bash's string manipulation. While sed was good for parsing multiple data and ordering it in a single command, the command-expansion to use the result would split the output up into "words" that would be delimited by spaces even if the spaces were preceded by backslash or surrounded by quotes.

So instead of trying to construct all of the arguments to id3v2 in a single pass with sed, it was broken up into parsing out the track number and title separately using bash's string manipulation. The end result:

for track in *.mp3 ; do
    title=${track#*_} ; title=${title%.mp3} ; title=${title//_/ }
    id3v2 -t "${title}" -T ${track_num} ${track}

Thanks to mueh for explaining exactly why my approach in the question was not working, and Valentin B for introducing bash's string manipulation.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.