4

I want to compress my entire music collection (a copy of it, actually) using lame. So naturally, there are folders within folders and possible weird characters in file names. I used the graphical soundconverter as well, but didn't like it as it has predefined bit rates.

What I've tried so far:

$ find . -name "*.mp3" | lame -b 160

and

$  find . -name "*.mp3" > list 
$ cat list | lame -b 160

and

$ lame -b 160 < list

All of these give me usage error. What is the right way to do it? Also, if there's a way to overwrite the original file, I'll be too happy.

ankush981
  • 321
  • 1
  • 3
  • 15
  • `cat $file | $program` is equivalent to `$program <$file`, except for the unnecessary `cat` invocation (and the overhead associated with kicking off an extra process, etc). In general, you should avoid the first form. `find . | $program` is similarly preferable to `find . >$file; $program <$file;` in that it avoids creating an unnecessary file, _unless_ you'll be reusing the list, in which case it might be preferable to write it out to a file first. – Blacklight Shining Apr 19 '15 at 15:03

2 Answers2

7

lame cannot read in filenames from input. You will have to use find's -exec or xargs to run it over each file found:

find . -iname '*.mp3' -exec lame -b {} \;

If a second filename isn't specified, lame will attach another .mp3 to the given filename and write to that file. lame does not support writing to the same file. You'll have to convert to another file, then copy it over the original file:

find . -iname '*.mp3' -exec sh -c 'lame -b 160 "$0" "$0"-160 && mv "$0"-160 "$0"' {} \;
muru
  • 193,181
  • 53
  • 473
  • 722
2

Try a the following:

Start with removing whitespaces from the file names since these seems to cause trouble:

for f in $(find . -name "*.mp3"); do rename "s/\s+/_/g" *; done

Execute a loop and traverse through all files:

for f in $(find . -name "*.mp3"); do lame -b 160 "$f" tmp && mv tmp "$f"; done

Now you will overwrite the original files with those created by lame.

Niklas Lindskog
  • 179
  • 1
  • 11
  • Actually I wish to retain the directory structure as I organize my music by directories. – ankush981 Apr 19 '15 at 10:19
  • Okay, we'll just add the find statement in the second query. I'll change the code. – Niklas Lindskog Apr 19 '15 at 10:34
  • I tried that but I keep getting errors like `could not find "./01.", etc. I guess the spaces are causing a problem. How to overcome that? – ankush981 Apr 19 '15 at 11:17
  • Is removing/replacing the whitespaces an option or should they be preserved? – Niklas Lindskog Apr 19 '15 at 11:36
  • I think I'll be fine with underscores. But I'm hoping dots, etc., won't cause trouble. – ankush981 Apr 19 '15 at 12:01
  • @dotslash OK! Added a loop to replace whitespaces as well. Hope everything works according to plan now! – Niklas Lindskog Apr 19 '15 at 12:23
  • Nope, sorry. Now the `mv` command is breaking: mv: `cannot stat ‘./02.’: No such file or directory mv: cannot stat ‘Too’: No such file or directory mv: cannot stat ‘Much’: No such file or directory mv: cannot stat ‘To’: No such file or directory mv: cannot stat ‘Bear.mp3’: No such file or directory` – ankush981 Apr 19 '15 at 13:29
  • Should have tested it myself first, sorry! Now it is correct. – Niklas Lindskog Apr 19 '15 at 13:40
  • 1
    Hey, thanks a lot! Please don't mind but I accepted another answer. I just think it's more compact. :-) – ankush981 Apr 19 '15 at 14:06
  • 2
    If you need to loop over the output of `find`, a `for` loop over `$()` is the wrong way. http://mywiki.wooledge.org/DontReadLinesWithFor. It will break on filenames with spaces, for example. This is why `find -exec` exists, even though it's cumbersome to use when you have to do `-exec sh -c ...` – Peter Cordes Apr 19 '15 at 15:04
  • 1
    Instead of invoking `find` (`for file in $(find . -name "*.mp3")`), you could `shopt -s globstar` (for `bash`) and use `for file in **/*.mp3`. – Blacklight Shining Apr 19 '15 at 15:07