Run a command on every file, with a flag argument that depends on the filename

If all the files to be processed are in the same folder, you don't need to use find, and can make do with native shell globbing.

for foo in *.txt ; do
  mycommand "${foo}" -o "${foo%.txt}-processed.txt"
done

The shell idiom ${foo%bar} removes the smallest suffix string matching the pattern bar from the value of foo, in this case the .txt extension, so we can replace it with the suffix you want.


If you write a script to be run on various systems and portability is a concern, then nothing beats for loops and ${var%sfx} from user1404316's answer.

However, if you are looking for a convenient way to do similar things on your own system, then I heartily recommend GNU Parallel, which is basically xargs on steroids. Here's a solution for your particular task:

parallel mycommand {} -o {.}-processed.txt ::: *.txt

It will take a list of strings after ::: (shell would expand *.txt to a list of matching filenames) and run mycommand {} -o {.}-processed.txt for each string, replacing {} with input string (just like xargs) and {.} with filename without extension.

You can feed list of input strings via stdin (xargs-way), to pair it with find or locate, but I rarely need anything more than zsh's extended globs.

Take a look at GNU Parallel tutorial to get the idea of what it can do. I use it all the time for batch converts, extracting archives to sudirectories etc.


This adapts user1404316’s answer to work with find:

find . -type f -name '*.txt' \
   -exec sh -c 'for file do mycommand "$file" -o "${file%.txt}-processed.txt"; done' sh {} +

(You can type that all on one line; just leave out the \.  I broke it into two lines for readability.)


Another way to format it for readability, that makes the embedded shell script a little bit clearer:

find . -type f -name '*.txt' -exec sh -c '
  for file
  do
    mycommand "$file" -o "${file%.txt}-processed.txt";
  done
' sh {} +

Basically, it creates an unnamed shell script:

for file
do
    mycommand "$file" -o "${file%.txt}-processed.txt"
done

(that’s the string between the single quotes, '…', unrolled) and passes it to the shell as a command (sh -c) with the names of all your .txt files as parameters.  (You generally don’t need to quote {}, and you don’t need curly braces in "$file".)

Tags:

Shell

Find