2

I created a shell script to convert all wave files to mp3 files. I use Ubuntu 18.04, FFmpeg was installed using apt, and I run my script in bash.

My script:

#!/bin/bash

ls *.wav | while read file
do
    echo $file
    ffmpeg -i "$file" -codec:a libmp3lame -b:a 192k "${file%.*}.mp3"
done

The target files are followings: (include space characters. The real filenames are longer, but I simplified the filenames. Also in this case, the problem occurs)

% ls *.wav
'01 A.wav'  '02 A.wav'  '03 A.wav'

The problem is that sometimes $file in the loop is blank or a part of filename strangely ('echo $file' shows that), and ffmpeg says '[broken filename]: No such file or directory'. I confirmed the following things.

  • When I comment out the ffmpeg line, 'echo' shows what I expected. ($file not broken)
  • When I replace ffmpeg to a similar command like 'lame', it works. ($file not broken)
  • When I replace 'ls *.wav | while read file' to 'for file in *.wav', it works. ($file not broken)

So, only when I use combination of 'ls' and 'ffmpeg', $file is broken. What's going on? Or do I misunderstand something?

15
  • Now I was able to reproduce the problem. It did not work with empty files, but it worked with actual .wav files. For me read reads 2 A.wav instead of 02 A.wav resulting in the ffmpeg error 2 A.wav: No such file or directory. Commented Dec 13, 2018 at 9:14
  • Thank you for the try and additional information. It's really mysterious. Commented Dec 13, 2018 at 9:19
  • 1
    @oguzismail But maybe different locales and wav files. I can reproduce the problem with export LC_ALL=C and files generated by ffmpeg -f lavfi -i anullsrc -t 1 1.wav; echo {2..4}.wav | xargs -n 1 cp 1.wav. Commented Dec 13, 2018 at 9:59
  • 1
    @user3288408 See: stackoverflow.com/a/44249993/10248678 Commented Dec 13, 2018 at 10:07
  • 1
    Yes, think so too. But man, I was so close. ffmpeg was meddling with stdin, but I assumed it would add somthing and tried > /dev/null when it actually consumed something and I should have used < /dev/null. Commented Dec 13, 2018 at 10:13

1 Answer 1

2

You should not use ls to pipe a list of files, because this approach will not work well for corner cases like filenames with blanks.

The most readable way is to use globbing:

for file in *.wav
do
  echo "$file"
  ffmpeg -i "$file" -codec:a libmp3lame -b:a 192k "${file%.*}.mp3"
done

find also does a great job here, but you will end up with a little less maintainable code:

find -name *.wav -exec ffmpeg -i {} -codec:a libmp3lame -b:a 192k {}.mp3 \;

The find example will give you filenames ending in .wav.mp3 instead of .mp3, if you want to avoid that, you have to call a shell from find, allowing you to either use basename or do something like ${file.*} (you would have to assign find's {} to a variable like file in that shell first:

find -name "*.wav" -exec sh -c "file=\"{}\" ; ffmpeg -i \"\$file\" -codec:a libmp3lame -b:a 192k \${file%.*}.mp3" \;
Sign up to request clarification or add additional context in comments.

5 Comments

While this may solve the problem, it does not answer the question. OP already mentioned in the question that using for file in *.wav resolves the issues.
Thank you for the informative answer and the comment. Also I'm curious about what causes this mysterious behavior. I also welcome answers about this phenomenon.
@Socowi That is correct, unfortunately I missed that info while scanning the question earlier. I hope I'll get to looking into it again a little later.
@Michael Jaros I appreciate your answer. Sorry for the misleading question...
@user3288408 Ah, that's not your fault, I did not read properly :-)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.