14

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 of You 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?

1
  • Notice that you didn't escape | from the shell, so find doesn't see anything after the {}. I don't think find -exec even uses a shell, though, so I wouldn't count on | or > working even if you did escape them. Jun 13, 2022 at 22:30

4 Answers 4

19

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
  • 1
    Or better, use printf 'You entered %s\n' "$@" to expand a whole array of args, so you can use find -exec ./indexer.sh {} + instead of starting a new shell for every file found. Jun 13, 2022 at 22:33
18

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.

7

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'"
done

execution 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
  • 2
    Why use find ... -exec echo {} | ... and not just find ... | ... (without exec and echo. On my system both produce the same result, is there something I am missing?
    – user000001
    Jun 11, 2022 at 17:30
  • 1
    @user000001 i added a paragraph about optimization. I'll leave the original version as part of the answer, because it addresses the error message find: -exec: no terminating ";" or "+", which is part of OP.
    – Sheldon
    Jun 12, 2022 at 10:40
0

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; done

I 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.

2
  • 1
    sudodus's answer (passing the filename as an arg to the script) avoids IFS problems, even for filenames containing newline. All the answers that suggest piping would want to do something like (IFS=$'\n'; find | whatever) to temporarily set IFS in a subshell. You would still want to quote any expansions, like echo "$file". That will work for any characters except newline in filenames. (Which doesn't typically happen normally, but copy-paste errors into file selector dialogs can happen.) Jun 13, 2022 at 22:37
  • You won't have an issue with spaces/special characters in filenames if you put quotes around "$file" and your script does the same with its parameters.
    – dolt
    Jun 14, 2022 at 18:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .