I have a situation where I have some input files like this:


And I want to run a command (in a Bash shell) on each similarly named file in the directory. I'd like the current file name to be given to this command as an input and a modified version of the filename to be given as an output file. Here's an example:

ffmpeg -i M2U0001.MPG M2U0001_fixed.MPG

I had the idea of using xargs and sed, but this is as far as I got:

ls -1 *.MPG | xargs -I{} ffmpeg -i {} `echo {} | sed -r 's/[0-9]{2,}/&_fixed/'`

But this results in the original filename being output in both positions. Am I totally going about this the wrong way?

I found that if I echo the filename directly to the embedded chunk like this it works:

echo M2U0001.MPG | sed -r 's/[0-9]{2,}/&_fixed/'

Or alternatively:

for i in *.MPG ; do ffmpeg -i $i `basename $i .MPG`_fixed.MPG ; done

Thank joshbaptiste for the hint.

  • This works perfectly. Thanks for the demonstration of a for-loop in a one-liner. – Matthew Nov 12 '11 at 12:21
  • be aware of the fact that this command will fail on filenames that contain spaces – Alex Nov 13 '11 at 0:38
  • True, I assumed file names are in that particular format, thanks for pointing it out. – cYrus Nov 13 '11 at 10:30
find . -iname "*.mpg" -exec sh -c "ffmpeg -i {} `echo {} | sed -e 's/\./_fixed\./'`" \;
  • 1
    This example does the same thing as my code. The original filename is used as both the input and output. – Matthew Nov 12 '11 at 12:17

for i in $(ls) should not be used - the ls(1) output should not be used for parsing via scripts, etc. due to word splitting and is a common mistake I see in Bash scripts at my job.

In this case parameter expansion works fine and is not susceptible to word splitting errors.

for i in *.MPG; do ffmpeg -i "$i" "${i%%.*}"_fixed.MPG ; done

Reference: http://mywiki.wooledge.org/BashPitfalls


Here is a specific variation:

ls -1 src/*.c | xargs -i sh -c "echo;gcc -fpreprocessed -dD -E {} 2>&1 | grep -wi -e one -e two -e three -n | sed 's:^:{}\::'" | cat -s

A list of C source files,

ls -1 src/*.c

is piped to xargs, which executes the preprocessor in a child shell,

gcc -fpreprocessed -dD -E {} 2>&1

which is subsequently piped into a desired grep command

grep -wi -e one -e two -e three -n

which is then piped into sed to prefix each line with the current file name:

sed 's:^:{}\::'

Finally, all the repeated blank lines are collapsed to single lines using cat:

cat -s

This works on a Red Hat Linux 6 system, but I assume it is general enough for other *nix systems.

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.