ls *.mp3|while read i;do ffmpeg -i "${i}" "${i}.wav" 2&gt;&amp;1;done

You don't say how this fails, but there are several reasons not to do things this way:

1. It's unwise to feed a 'while' loop thorough a pipe because the 'while' runs
in a separate shell which can lead to problems

2. Never use 'ls' to get a list of files for consumption in a script. Unless
you can be completely sure that the 'ls' you are using isn't adding suffixes
like '@' for links, and '/' for directories and isn't adding colour codes to
the names, don't do it. Much better to use 'find'.

My test with this pipeline returned colour codes which 'ffmpeg' didn't like,
and it failed that way.

for i in *.mp3;do ffmpeg -i "${i}" "${i}.wav" 2&gt;&amp;1;done

This doesn't use 'ls' it simply uses file expansion therefore no additional
filename garbage!

Did I mention: don't use 'ls' as a way of feeding filenames to a loop or
whatever?

The first example would have worked if you'd written:

while read i;do ffmpeg -i "${i}" "${i}.wav" 2&gt;&amp;1;done &lt; &lt;(find . -maxdepth 1 -name "*.mp3")

The '-maxdepth 1' option prevents 'find' from going into sub-directories. The
'find' is inside a process substitution which is redirected to the 'while' so
the 'read' inside it can obtain what is produced on its STDIN channel.

Also, if it had been me I'd have written:

ffmpeg -i "${i}" "${i}.wav"

as:

ffmpeg -i "${i}" "${i%mp3}wav"

to avoid the output files being called 'xxx.mp3.wav'. I spoke about this in my
"Bash Tips" show https://hackerpublicradio.org/eps.php?id=1648

Dave