Sujet : Executing Shell Pipelines with “find -exec”
De : ldo (at) *nospam* nz.invalid (Lawrence D'Oliveiro)
Groupes : comp.unix.shell comp.os.linux.miscDate : 27. Apr 2024, 10:22:29
Autres entêtes
Organisation : A noiseless patient Spider
Message-ID : <v0ig4l$96sa$1@dont-email.me>
User-Agent : Pan/0.155 (Kherson; fc5a80b8)
The “find” command <
https://manpages.debian.org/1/find.en.html> offers
a great variety of ways to filter out files. And if none of them is
quite good enough, you can use the “-exec” option to execute an
arbitrary command.
Unfortunately, it must be an arbitrary *single* command.
For example, I wanted to identify .blend files created by Blender
versions after 3.4. I have a command called “blendfile_version”
<
https://gitlab.com/ldo/blender-useful/> which will output some basic
version information about a .blend file in JSON format. From this,
it’s easy enough to extract the version string with
blendfile_version «blendfile» | jq -r .version
and then filter out older versions with
test $(blendfile_version «blendfile» | jq -r .version) -gt 304
But this command cannot be used as is with -exec! That is, if I try
find . -name \*.blend -exec test $(blendfile_version {} | jq -r .version) -gt 304 \; -print
that won’t work, because -exec will not feed the command to a shell to
handle the command substitution, pipelining etc.
However, you can explicitly invoke a shell and give it a one-shot
command with “sh -c”. While the command string must be a single word,
it is possible to have multiple argument words following this command.
So if the command happens to be “eval”, that gives you your ability to
feed the separate words of the -exec command to the shell, thus:
find . -name \*.blend -exec sh -c 'eval "${@}"' \
sh test \$\( blendfile_version {} \| jq -r .version \) -gt 304 \; -print
This works, but it assumes that the find command will only substitute
the “{}” sequence with the file name if it occurs as a separate word.
In fact, GNU find will perform this substitution even if the sequence
occurs as part of a word. This allows the -exec command to be
simplified somewhat, by getting rid of the “eval” stage:
find . -name \*.blend -exec \
sh -c '[ $(blendfile_version {} | jq -r .version ) \> 304 ]' \; \
-print
And there we have it. Some commands have a “quiet” option, where you
give them a criterion to match, and they return an exit status
indicating success/failure to match (e.g. “grep -q”). Having this may
make use with find just a little bit easier, but as you can see, it’s
not strictly necessary.