Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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?

share|improve this question
4  
While 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. –  Warren Young Aug 5 '11 at 3:27
2  
+1 for heroic one-liners being entertaining –  jw013 Aug 5 '11 at 3:41
1  
@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 if find can't do this, then why is find so revered(?) as the tool-to-use for finding files?... I've subseqeuntly found that find ./ -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 used find (in less than a year of Linux) as locate has done almost all I need, so it's unknown territory. –  Peter.O Aug 5 '11 at 6:08
1  
The -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;) ) –  rozcietrzewiacz Aug 5 '11 at 10:51

3 Answers 3

up vote 4 down vote accepted

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.]
share|improve this answer
    
I 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 Aug 5 '11 at 5:54
    
Thanks, 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 your find example just doesn't work (still) –  Peter.O Aug 5 '11 at 6:31
    
I 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 –  Peter.O Aug 5 '11 at 6:36
    
I 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. –  jw013 Aug 5 '11 at 6:40
    
Okay.. that's an interesting and good point (about using globs)... –  Peter.O Aug 5 '11 at 6:43

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
share|improve this answer
1  
You've given a good overview of the situation, but all of the shown find methods get an error when a file/directory contains spaces... The md5sum .*/*.ini works fine... I'm starting to get the general feel that * Warren Young's* comment about things getting 'complex' for find happens early on in the game :), but I assume find 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 Aug 5 '11 at 10:13

At least I managed to nest 2 find commands:

find ~ -maxdepth 1 -type d -name '.*' -execdir \
    find {} -maxdepth 1 -type f -name '*.ini' \;

But I didn't solve to invoke another -exec(dir) - call from there.

share|improve this answer
    
Yes, that is my problem exactly :) –  Peter.O Aug 5 '11 at 10:22
    
Yes, but not nesting 3 is not the same as not nesting 2. :) –  user unknown Aug 11 '11 at 20:07

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.