Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is Flow Control in Bash?

Tags:

linux

bash

In this question the answer was

find . -type f -name \*.mp4 -exec sh -c 'ffprobe "$0" 2>&1 |
   grep -q 1920x1080 && echo "$0"' {} \;

which will output all mp4 files that are 1920x1080.

I don't understand why sh -c is there. If I remove it, then it doesn't find anything.

The author says

The new shell is required to handle the flow control inside the exec'd command.

but I guess I am missing some fundamental knowledge to understand the answer.

Question

Can anyone explain why sh -c have to be there, and why it only works then ffprobe is opened in a new shell?

like image 395
Sandra Schlichting Avatar asked Dec 16 '22 10:12

Sandra Schlichting


2 Answers

The -exec option takes a sequence of arguments:

find . -exec arg0 arg1 arg2 ... \;

If you put the arguments in quotes

find . -exec "arg0 arg1 arg2" \;

then "arg0 arg1 arg2" are treated as a single argument. It would expect a command called arg0 arg1 arg2, with the spaces, to exist on your system, instead of a command called arg0 with parameters of arg1 and arg2.

If you were to use find without the sh -c, then you would have this:

find . -type f -name \*.mp4 -exec 'ffprobe "{}" 2>&1 |
   grep -q 1920x1080 && echo "{}"' \;

This would mean that find would look for a command called ffprobe "$0" ...., passing no arguments -- there is no such command. There is a command called ffprobe, which takes arguments, and that is what you need. One possibility would be to do something like this:

find . -type f -name \*.mp4 -exec ffprobe '$0' 2>&1 |
   grep -q 1920x1080 && echo '{}' \;

However, that doesn't work, since the output redirection 2>&1 and the pipe | and the command sequence operator && would all be treated differently than what you want.

To get around this, they use another shell. This is similar to creating a script to do the work:

find . -type f -name \*.mp4 -exec myscript {} \;

But instead of a separate script, everything is all on one line.

like image 95
Vaughn Cato Avatar answered Dec 30 '22 05:12

Vaughn Cato


find executes the arguments given to exec as a command directly (that is it invokes the first argument as an application with the following arguments as arguments to that command) without doing any processing on it other than replacing {} with the file name. That is it does not implement any shell features like piping or input redirection. In your case the command contains pipes and input redirections. So you need to run the command through sh, so that sh handles those.

like image 41
sepp2k Avatar answered Dec 30 '22 04:12

sepp2k