Is it possible to nest a 'find -exec' within another 'find -exec'?
Solution 1
I would try using a single find like:
find .*/ -maxdepth 1 -type f -name '*.ini' -execdir md5sum {} +
or even (no find
at all, just shell globbing)
md5sum .*/*.ini
although this lacks the -type f
check so only works if you have no directories/non-files ending in .ini
. If you do you could use
for x in .*/*.ini; do
if [ -f "$x" ]; then
md5sum "$x"
fi
done
which would however lose the advantage of only needing one md5sum invocation.
Edit
For a general and safe method of chaining find
, you can do something like
find <paths> <args> -print0 | xargs -0 -I{.} find {.} <args for second find> [etc.]
Solution 2
Your original problem does not require calling find recursively but I suppose that was not the point.
I believe it is not possible to call find recursively in the way you want.
The following is not calling find recursively (or nesting, whatever it is called) either, but can't you just take a result set of the first find and feed it to the second one? This is how I would instinctively do:
find `find ./ -maxdepth 1 -type d -name '.*'` \
-maxdepth 1 -type f -name '*.ini' -exec md5sum {} \;
You could also use xargs
for executing the second find.
Update:
I wanted to add that because most UNIX utilities take several file name arguments instead of one, you can usually avoid the -exec
altogether:
md5sum `find \`find ./ -maxdepth 1 -type d -name '.*'\` -maxdepth 1 -type f -name '*.ini'`
When nesting backticks you just add backslashes \
before the inner ones.
If we imagine that md5sum
takes only one filename argument, we can always wrap it in a for
loop:
for f in `find \`find ./ -maxdepth 1 -type d -name '.*'\` -maxdepth 1 -type f -name '*.ini'`
do
md5sum $f
done
Note that this becomes more difficult if file/directory names starting with -
or containing a space are involved. UNIX utilities do not play nicely with them. In that case adding ./
, --
or quotes is needed.
Obviously the original example is not a good one, because we could just do:
md5sum .*/*.ini
Peter.O
Updated on September 18, 2022Comments
-
Peter.O almost 2 years
Something like the following is what I what I'm after, but my code doesn't work, no matter how I escape
{}
and+
;
find ./ -maxdepth 1 -type d -name '.*' -exec \ find {} -maxdepth 1 -type f -name '*.ini' -exec \ md5sum \{\} \\; \;
After seeing this Unix-&-Linux question, I found that the following code works, but it isn't nesting find as such, and I suspect there is a better way to do this particular job.
find ./ -maxdepth 1 -type d -name '.*' \ -exec bash -c 'for x; do find "$x" -maxdepth 1 -type f -name "*.ini" \ -exec md5sum \{\} \;; \ done' _ {} \+
Is there some way to nest
find -exec
without the need to invoke a shell (as above), with all its whacky quoteing and escape constraints?Or can this be done directly in a single find command, using a blend of its many parameters?
-
Warren Young almost 13 yearsWhile it may be possible to do what you're asking, when things get that complex, I switch to shell or Perl scripts. Your second code snippet is pretty much doing this, only with the shell script inline. Heroic one-liners are entertaining, but they're hard to understand, and thus hard to maintain. Unless this is a one-shot deal that you nevertheless somehow end up getting good at, I can't see a good reason to do it other than the intellectual challenge.
-
Peter.O almost 13 years@Warren Young: I certainly don't think the concept is complex, but I assume you mean there is no simple way to do in with
find
, but iffind
can't do this, then why isfind
so revered(?) as the tool-to-use for finding files?... I've subseqeuntly found thatfind ./ -maxdepth 2 -path '.*/*.ini' -type f -exec md5sum {} \+
works fine in my situation (jw013's reference to-prune
led me to this in the man page), but I wonder if it is a robust method(in genera). I've never really usedfind
(in less than a year of Linux) aslocate
has done almost all I need, so it's unknown territory. -
rozcietrzewiacz almost 13 yearsThe
-path
test is exactly what I was going to suggest. With this, you should be able to do all that you want (sorry for the Ace Of Base association;) )
-
-
Peter.O almost 13 yearsI get an error with the
-f
(f ?), and then another error with-execdir
.. When I replace -execdir with -exec, and/or also replacing md5sum with print, I get nothing.. -
Peter.O almost 13 yearsThanks, for the alternative... but I'm more after a way of doing this using
find
... It's not so much that I just want this example resolved, I'm looking for insights into the ways of find-fu ... maybe there more fluff than fu in find.. (I don't know, because I've virtually never used it), and this is the first situation I've really wanted to use it (for image files actually) and the -exec feature I've heard so much about seems to be not as all-powerful as its rep(?) alludes to... (+1 for the alternatives, though) .. but yourfind
example just doesn't work (still) -
Peter.O almost 13 yearsI get this error for the
find
command: ...find: The relative path
~/bin' is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove that entry from $PATH` .... So maybe it will work, but I must say I've been trying to get rid of that ~/bin for a while now... I"ll have to take as more serious look at it... I don't know where I"ve set it... any ideas where It may be lurking; the~/bin
in my PATH -
jw013 almost 13 yearsI think the power of
find
is best appreciated in situations which can't be done entirely with globs, but as those kinds of situations are rare I don't normally need find much. -
Peter.O almost 13 yearsOkay.. that's an interesting and good point (about using globs)...
-
jw013 almost 13 yearsThat's a built-in security feature of
find
. You shouldn't have relative paths inPATH
if you want to usefind -exec
, and it's not a great idea anyways. I'd check~/.profile
,~/.bash_profile
and/etc/profile
. -
Peter.O almost 13 yearsYou've given a good overview of the situation, but all of the shown
find
methods get an error when a file/directory contains spaces... Themd5sum .*/*.ini
works fine... I'm starting to get the general feel that * Warren Young's* comment about things getting 'complex' forfind
happens early on in the game :), but I assumefind
comes into its own when the condition tests are more intricate, but as far as nesting -exec goes, I've pretty well dropped the idea, as it seems there are simpler ways to do it.. (but perl isn't "simple" to me (yet)... -
Peter.O almost 13 yearsYes, that is my problem exactly :)
-
Peter.O almost 13 yearsI've finally sorted out the
~/bin
in my path problem, and I've now tried yourfind
examples.. The last one, with xargs, is certainly up to the task, as were the non-find methods.. well done! thanks... -
user unknown almost 13 yearsYes, but not nesting 3 is not the same as not nesting 2. :)
-
Luke Savefrogs almost 3 yearsThe
find ... | xargs ...
method did the trick for me... I was trying to achieve exactly that. I needed a general solution that would look for a folder and THEN look for a specific file in that folder and execute a command on it