Trying to get my id3 tags cleaned up and am loving id3v2 on the command line -- but I've been using it only with a *.mp3 wildcard and want to explore if there's a way to use it recursively so I can batch all of my MP3s. There doesn't seem to be an option to use it recursively.

I'm pretty sure all you awesome command line folks know of a sweet way to do this, but I am still learning my way through it...

Here's the commands I have aliased to brute force it:

id3v2 --remove-frame "COMM" *.mp3
id3v2 --remove-frame "PRIV" *.mp3
id3v2 -s *.mp3

So -- is there a way to do this recursively, so I can run it a the root of my music folder? Gravy points for: including other audio file types and collapsing all three commands above into one uber command (I can do this with the ; between commands... right?)


You should be able to do this in a single line, with something like this:

find . -name '*.mp3' -execdir id3v2 --remove-frame "COMM" '{}' \; -execdir id3v2 --remove-frame "PRIV" '{}' \; -execdir id3v2 -s '{}' \;

The {} are substituted for the current filename match. Putting them in quotes ('') protects them from the shell. The -execdir runs until it hits a semi-colon, but the semi-colon (;) needs escaping from the shell, hence the use of the backslash (\). This is all described in the find manpage:

-exec command ;
      Execute  command;  true  if 0 status is returned.  All following
      arguments to find are taken to be arguments to the command until
      an  argument  consisting of `;' is encountered.  The string `{}'
      is replaced by the current file name being processed  everywhere
      it occurs in the arguments to the command, not just in arguments
      where it is alone, as in some versions of find. 
-execdir command {} +
      Like -exec, but the specified command is run from the  subdirec‐
      tory  containing  the  matched  file,  which is not normally the
      directory in which you started find.  This a  much  more  secure
      method  for invoking commands...

Since you sound like you're a little new to this, a caveat: As always with complex shell commands, run them cautiously, and try in a test directory first, to make sure you understand what is going to happen. With great power comes great responsibility!

  • Could I bother you to explain the {} and \ commands in the above? That looks awesome, will try that when this run finishes up... – chow Sep 12 '12 at 3:19
  • Finding, after multiple runs, that this works much better for me -- thanks ire_and_curses. – chow Sep 12 '12 at 4:39

A quick solution is loop through all sub folders, and process all files in it:

find . -type d | while IFS= read -r d; do
  cd "${d}"
  id3v2 --remove-frame "COMM" *.mp3
  id3v2 --remove-frame "PRIV" *.mp3
  id3v2 -s *.mp3
  cd -
  • That, my good friend, worked like a charm. – chow Sep 12 '12 at 3:15
  • Why use cd? Why not id3v2 … "$d/"*.mp3? – Gilles 'SO- stop being evil' Sep 13 '12 at 0:22

I don't use id3v2 so I can't be sure, but there's a good chance that you can combine all your commands into one:

id3v2 --remove-frame "COMM" --remove-frame "PRIV" -s *.mp3

To run this command in MP3 files in subdirectories as well, run

id3v2 --remove-frame "COMM" --remove-frame "PRIV" -s **/*.mp3

**/*.mp3 matches .mp3 files in the current directory and its subdirectories recursively. If your shell is zsh, **/ works out of the box. If your shell is bash ≥4, you need to run shopt -s globstar first (put this line in your ~/.bashrc). In ksh, you need to run set -o globstar (put it in ~/.kshrc). If you have another shell, or if this attempt fails with a message telling you that the command line is too big, you'll have to use the find method below (with variants such as those given in other answers).

A more complex, but more flexible and more portable way to act on files in a directory and its subdirectories recursively is the find command:

find . -type f -name '*.mp3' -exec id3v2 --remove-frame "COMM" --remove-frame "PRIV" -s {} +

The command after -exec is executed with the {} + bit at the end replaced by the paths to matching files. If you need to run multiple id3v2 commands, use multiple -exec directives:

find . -type f -name '*.mp3' -exec id3v2 --remove-frame "COMM" {} + -exec id3v2 --remove-frame "PRIV" {} + -exec id3v2 -s {} +

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.