I'm trying to pipe the result of a find command to a bash script. This is to simplify (maybe automate) a process I have been working on.
This is the command I would like to run
find . -type f -iname '*.mp4' -exec echo {}|./indexer.sh \;
indexer.sh is ofc chmod +x so it can execute.
indexer.sh currently contains
#!/bin/zsh
read foo
echo "You entered '$foo'"And if I run $ echo foo | ./indexer.sh I get the output ofYou entered 'foo'
But when I run find . -type f -iname '*.mp4' -exec echo {}|./indexer.sh \; I receive the following error message:
find: -exec: no terminating ";" or "+"
You entered ''So how can I pipe the output of find, into my script?
14 Answers
I would rewrite it using a parameter instead of a read statement and piping,
find . -type f -iname '*.mp4' -exec ./indexer.sh {} \;with the following indexer.sh,
#!/bin/zsh
echo "You entered '$1'" 1 Your misconception is that echo {}|./indexer.sh is treated as a unit. It isn't. What went wrong is that your shell interprets the pipeline before it runs find. Therefore, it's running…
find . -type f -iname '*.mp4' -exec echo {}… and piping the result to
./indexer.sh \;As a result, find sees {} without the \; and fails. (indexer.sh sees a superfluous ; argument and ignores it.)
To fix your misconception, you would have to do…
find . -type f -exec sh -c 'echo "{}"|./indexer.sh' \;… since that's the only way to treat that pipeline as a single command.
Of course, that's a monstrosity. If you want to run indexer.sh once for each MP4 file, take the advice from @sudodus and avoid the pipe altogether.
The \; needs to be positioned before the pipe. Since i don't have zsh and your question is tagged with bash, i'll use bash for an example.
indexer.sh
#!/bin/bash
while read foo
do echo "You entered '$foo'"
doneexecution example
$ find . -type f -iname '*.mp4' -exec echo {} \; |./indexer.sh
You entered './subdirectory/d.mp4'
You entered './subdirectory/c.mp4'
You entered './b.mp4'
You entered './a.mp4'optimization
This use case of find could be optimized by removing the . directly after find and the whole -exec part at the end. The output of find would be the same without it.
$ find -type f -iname '*.mp4' |./indexer.sh
You entered './subdirectory/d.mp4'
You entered './subdirectory/c.mp4'
You entered './b.mp4'
You entered './a.mp4' 2 If you can't or don't want to change indexer.sh for whatever reason, you can also be explicit about your iteration with
find . -type f -iname '*.mp4' | while read file; do echo $file | ./indexer.sh; doneI use this pattern a lot. You may need to set IFS if you have spaces in your filenames, but I think you'd have that problem with the other solutions as well.