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"
.)