Limit POSIX find to specific depth?












12














I noticed recently that POSIX specifications for find do not include the -maxdepth primary.



For those unfamiliar with it, the purpose of the -maxdepth primary is to restrict how many levels deep find will descend. -maxdepth 0 results in only command line arguments being processed; -maxdepth 1 would only handle results directly within the command line arguments, etc.



How can I get the equivalent behavior to the non-POSIX -maxdepth primary using only POSIX-specified options and tools?



(Note: Of course I can get the equivalent of -maxdepth 0 by just using -prune as the first operand, but that doesn't extend to other depths.)










share|improve this question






















  • @StevenPenny, FreeBSD's -depth -2, -depth 1... approach could be seen as better than GNU's -maxdepth/-mindepth
    – Stéphane Chazelas
    Dec 14 '16 at 16:33










  • @StéphaneChazelas either way - POSIX find should have one or the other; else it is crippled
    – Steven Penny
    Dec 14 '16 at 17:07






  • 1




    At least for -maxdepth/-mindepth, there are reasonable alternatives (note that -path is a recent addition to POSIX). The alternatives for -timexy or -mtime -3m (or -mmin -3) are a lot more cumbersome. Some like -execdir/-delete have no reliable alternative.
    – Stéphane Chazelas
    Dec 14 '16 at 17:18








  • 2




    @StevenPenny, feel free to log a ticket at austingroupbugs.net to request it be added. I've seen things get added without the need for a sponsor when there was a strong justification. A probably better course of action would be to get as many implementations add it first so POSIX would just have to specify the existing which is generally less contentious.
    – Stéphane Chazelas
    Dec 14 '16 at 17:22










  • @StéphaneChazelas in my case I ended up just naming the files directly, but thank you; I might file a ticket if this comes up again
    – Steven Penny
    Dec 14 '16 at 17:31
















12














I noticed recently that POSIX specifications for find do not include the -maxdepth primary.



For those unfamiliar with it, the purpose of the -maxdepth primary is to restrict how many levels deep find will descend. -maxdepth 0 results in only command line arguments being processed; -maxdepth 1 would only handle results directly within the command line arguments, etc.



How can I get the equivalent behavior to the non-POSIX -maxdepth primary using only POSIX-specified options and tools?



(Note: Of course I can get the equivalent of -maxdepth 0 by just using -prune as the first operand, but that doesn't extend to other depths.)










share|improve this question






















  • @StevenPenny, FreeBSD's -depth -2, -depth 1... approach could be seen as better than GNU's -maxdepth/-mindepth
    – Stéphane Chazelas
    Dec 14 '16 at 16:33










  • @StéphaneChazelas either way - POSIX find should have one or the other; else it is crippled
    – Steven Penny
    Dec 14 '16 at 17:07






  • 1




    At least for -maxdepth/-mindepth, there are reasonable alternatives (note that -path is a recent addition to POSIX). The alternatives for -timexy or -mtime -3m (or -mmin -3) are a lot more cumbersome. Some like -execdir/-delete have no reliable alternative.
    – Stéphane Chazelas
    Dec 14 '16 at 17:18








  • 2




    @StevenPenny, feel free to log a ticket at austingroupbugs.net to request it be added. I've seen things get added without the need for a sponsor when there was a strong justification. A probably better course of action would be to get as many implementations add it first so POSIX would just have to specify the existing which is generally less contentious.
    – Stéphane Chazelas
    Dec 14 '16 at 17:22










  • @StéphaneChazelas in my case I ended up just naming the files directly, but thank you; I might file a ticket if this comes up again
    – Steven Penny
    Dec 14 '16 at 17:31














12












12








12


3





I noticed recently that POSIX specifications for find do not include the -maxdepth primary.



For those unfamiliar with it, the purpose of the -maxdepth primary is to restrict how many levels deep find will descend. -maxdepth 0 results in only command line arguments being processed; -maxdepth 1 would only handle results directly within the command line arguments, etc.



How can I get the equivalent behavior to the non-POSIX -maxdepth primary using only POSIX-specified options and tools?



(Note: Of course I can get the equivalent of -maxdepth 0 by just using -prune as the first operand, but that doesn't extend to other depths.)










share|improve this question













I noticed recently that POSIX specifications for find do not include the -maxdepth primary.



For those unfamiliar with it, the purpose of the -maxdepth primary is to restrict how many levels deep find will descend. -maxdepth 0 results in only command line arguments being processed; -maxdepth 1 would only handle results directly within the command line arguments, etc.



How can I get the equivalent behavior to the non-POSIX -maxdepth primary using only POSIX-specified options and tools?



(Note: Of course I can get the equivalent of -maxdepth 0 by just using -prune as the first operand, but that doesn't extend to other depths.)







find posix






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Apr 11 '16 at 6:14









Wildcard

22.6k961164




22.6k961164












  • @StevenPenny, FreeBSD's -depth -2, -depth 1... approach could be seen as better than GNU's -maxdepth/-mindepth
    – Stéphane Chazelas
    Dec 14 '16 at 16:33










  • @StéphaneChazelas either way - POSIX find should have one or the other; else it is crippled
    – Steven Penny
    Dec 14 '16 at 17:07






  • 1




    At least for -maxdepth/-mindepth, there are reasonable alternatives (note that -path is a recent addition to POSIX). The alternatives for -timexy or -mtime -3m (or -mmin -3) are a lot more cumbersome. Some like -execdir/-delete have no reliable alternative.
    – Stéphane Chazelas
    Dec 14 '16 at 17:18








  • 2




    @StevenPenny, feel free to log a ticket at austingroupbugs.net to request it be added. I've seen things get added without the need for a sponsor when there was a strong justification. A probably better course of action would be to get as many implementations add it first so POSIX would just have to specify the existing which is generally less contentious.
    – Stéphane Chazelas
    Dec 14 '16 at 17:22










  • @StéphaneChazelas in my case I ended up just naming the files directly, but thank you; I might file a ticket if this comes up again
    – Steven Penny
    Dec 14 '16 at 17:31


















  • @StevenPenny, FreeBSD's -depth -2, -depth 1... approach could be seen as better than GNU's -maxdepth/-mindepth
    – Stéphane Chazelas
    Dec 14 '16 at 16:33










  • @StéphaneChazelas either way - POSIX find should have one or the other; else it is crippled
    – Steven Penny
    Dec 14 '16 at 17:07






  • 1




    At least for -maxdepth/-mindepth, there are reasonable alternatives (note that -path is a recent addition to POSIX). The alternatives for -timexy or -mtime -3m (or -mmin -3) are a lot more cumbersome. Some like -execdir/-delete have no reliable alternative.
    – Stéphane Chazelas
    Dec 14 '16 at 17:18








  • 2




    @StevenPenny, feel free to log a ticket at austingroupbugs.net to request it be added. I've seen things get added without the need for a sponsor when there was a strong justification. A probably better course of action would be to get as many implementations add it first so POSIX would just have to specify the existing which is generally less contentious.
    – Stéphane Chazelas
    Dec 14 '16 at 17:22










  • @StéphaneChazelas in my case I ended up just naming the files directly, but thank you; I might file a ticket if this comes up again
    – Steven Penny
    Dec 14 '16 at 17:31
















@StevenPenny, FreeBSD's -depth -2, -depth 1... approach could be seen as better than GNU's -maxdepth/-mindepth
– Stéphane Chazelas
Dec 14 '16 at 16:33




@StevenPenny, FreeBSD's -depth -2, -depth 1... approach could be seen as better than GNU's -maxdepth/-mindepth
– Stéphane Chazelas
Dec 14 '16 at 16:33












@StéphaneChazelas either way - POSIX find should have one or the other; else it is crippled
– Steven Penny
Dec 14 '16 at 17:07




@StéphaneChazelas either way - POSIX find should have one or the other; else it is crippled
– Steven Penny
Dec 14 '16 at 17:07




1




1




At least for -maxdepth/-mindepth, there are reasonable alternatives (note that -path is a recent addition to POSIX). The alternatives for -timexy or -mtime -3m (or -mmin -3) are a lot more cumbersome. Some like -execdir/-delete have no reliable alternative.
– Stéphane Chazelas
Dec 14 '16 at 17:18






At least for -maxdepth/-mindepth, there are reasonable alternatives (note that -path is a recent addition to POSIX). The alternatives for -timexy or -mtime -3m (or -mmin -3) are a lot more cumbersome. Some like -execdir/-delete have no reliable alternative.
– Stéphane Chazelas
Dec 14 '16 at 17:18






2




2




@StevenPenny, feel free to log a ticket at austingroupbugs.net to request it be added. I've seen things get added without the need for a sponsor when there was a strong justification. A probably better course of action would be to get as many implementations add it first so POSIX would just have to specify the existing which is generally less contentious.
– Stéphane Chazelas
Dec 14 '16 at 17:22




@StevenPenny, feel free to log a ticket at austingroupbugs.net to request it be added. I've seen things get added without the need for a sponsor when there was a strong justification. A probably better course of action would be to get as many implementations add it first so POSIX would just have to specify the existing which is generally less contentious.
– Stéphane Chazelas
Dec 14 '16 at 17:22












@StéphaneChazelas in my case I ended up just naming the files directly, but thank you; I might file a ticket if this comes up again
– Steven Penny
Dec 14 '16 at 17:31




@StéphaneChazelas in my case I ended up just naming the files directly, but thank you; I might file a ticket if this comes up again
– Steven Penny
Dec 14 '16 at 17:31










3 Answers
3






active

oldest

votes


















6














You can use -path to match a given depth and prune there. Eg



find . -path '*/*/*' -prune -o -type d -print


would be maxdepth 1, as * matches the ., */* matches ./dir1, and */*/* matches ./dir1/dir2 which is pruned. If you use an absolute starting directory you need to add a leading / to the -path too.






share|improve this answer





















  • Hmmm, tricky. Couldn't you just remove one layer of /* from the end of the pattern, take out the -ooperator, and get the same result?
    – Wildcard
    Apr 11 '16 at 15:33










  • No, because * matches / as well, so the dir a/b/c/d/e would fit -path */*, sadly.
    – meuh
    Apr 11 '16 at 15:42










  • But a/b/c/d/e would never be reached, because -prune would be applied to a/b....
    – Wildcard
    Apr 11 '16 at 15:46






  • 1




    Sorry, I misread that -prune and -o were removed. If you keep the -prune the problem is that the */* will not match anything at a level above the maxdepth, eg the single directory a.
    – meuh
    Apr 11 '16 at 15:55



















7














@meuh's approach is inefficient as his -maxdepth 1 approach still lets find read the content of directories at level 1 to later ignore them otherwise. It will also not work properly with some find implementations (including GNU find) if some directory names contain sequences of bytes that don't form valid characters in the user's locale (like for file names in a different character encoding).



find . ( -name . -o -prune ) -extra-conditions-and-actions


is the more canonical way to implement GNU's -maxdepth 1 (or FreeBSD's -depth -2).



Generally though, it's -depth 1 you want (-mindepth 1 -maxdepth 1) as you don't want to consider . (depth 0), and then it's even simpler:



find . ! -name . -prune -extra-conditions-and-actions


For -maxdepth 2, that becomes:



find . ( ! -path './*/*' -o -prune ) -extra-conditions-and-actions


And that's where you run in the invalid character issues.



For instance, if you have a directory called Stéphane but that é is encoded in the iso8859-1 (aka latin1) charset (0xe9 byte) as was most common in Western Europe and the America up until the mid 2000s, then that 0xe9 byte is not a valid character in UTF-8. So, in UTF-8 locales, the * wildcard (with some find implementations) will not match Stéphane as * is 0 or more characters and 0xe9 is not a character.



$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . ( ! -path './*/*' -o -prune )
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith


My find (when the output goes to a terminal) displays that invalid 0xe9 byte as ? above. You can see that St<0xe9>phane/Chazelas was not pruned.



You can work around it by doing:



LC_ALL=C find . ( ! -path './*/*' -o -prune ) -extra-conditions-and-actions


But note that that affects all the locale settings of find and any application it runs (like via the -exec predicates).



$ LC_ALL=C find . ( ! -path './*/*' -o -prune )
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith


Now, I really get a -maxdepth 2 but note how the é in the second Stéphane properly encoded in UTF-8 is displayed as ?? as the 0xc3 0xa9 bytes (considered as two individual undefined characters in the C locale) of the UTF-8 encoding of é are not printable characters in the C locale.



And if I had added a -name '????????', I would have gotten the wrong Stéphane (the one encoded in iso8859-1).



To apply to arbitrary paths instead of ., you'd do:



find some/dir/. ! -name . -prune ...


for -mindepth 1 -maxdepth 1 or:



find some/dir/. ( ! -path '*/./*/*' -o -prune ) ...


for -maxdepth 2.



I would still do a:



(cd -P -- "$dir" && find . ...)


First because that makes the paths shorter which makes it less likely to run into path too long or arg list too long issues but also to work around the fact that find can't support arbitrary path arguments (except with -f with FreeBSD find) as it will choke on values of $dir like ! or -print...





The -o in combination with negation is a common trick to run two independent sets of -condition/-action in find.



If you want to run -action1 on files meeting -condition1 and independently -action2 on files meeting -condition2, you cannot do:



find . -condition1 -action1 -condition2 -action2


As -action2 would only be run for files that meet both conditions.



Nor:



find . -contition1 -action1 -o -condition2 -action2


As -action2 would not be run for files that meet both conditions.



find . ( ! -condition1 -o -action1 ) -condition2 -action2


works as ( ! -condition1 -o -action1 ) would resolve to true for every file. That assumes -action1 is an action (like -prune, -exec ... {} +) that always returns true. For actions like -exec ... ; that may return false, you may want to add another -o -something where -something is harmless but returns true like -true in GNU find or -links +0 or -name '*' (though note the issue about invalid characters above).






share|improve this answer



















  • 1




    Someday I will run into a bunch of Chinese files and I'll be very glad I've read your many answers about locale and valid characters. :)
    – Wildcard
    Dec 14 '16 at 23:37






  • 2




    @Wildcard, you (and even more so a Chinese person) is more likely to run into problem with British, French... file names than Chinese file names as Chinese filenames are more often encoded in UTF-8 than file names of alphabetical scripts that can generally be covered by a single-byte charset which was the norm up until relatively recently. There are other multi-byte charsets to cover Chinese character, but I'd expect Chinese people would have switched to UTF-8 earlier than westerners as those charsets have a number of nasty issues. See also the edit for an example.
    – Stéphane Chazelas
    Dec 15 '16 at 9:22



















0














I ran into an issue where I needed a way to limit depth when searching multiple paths (instead of just .).



For example:



$ find dir1 dir2 -name myfile -maxdepth 1


This led me to an alternate approach using -regex. The gist is:



-regex '(<list of paths | delimited>)/<filename>'


So, the above would be:



$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD


Without a filename:



$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD


Finally, for -maxdepth 2 the regex changes to: '(dir1|dir2)/([^/]*/){0,1}[^/]*$'






share|improve this answer





















  • This question asks for a standard (as in POSIX) solution though. Also -maxdepth would work with multiple search paths.
    – Kusalananda
    Dec 19 '18 at 8:56











Your Answer








StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f275637%2flimit-posix-find-to-specific-depth%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























3 Answers
3






active

oldest

votes








3 Answers
3






active

oldest

votes









active

oldest

votes






active

oldest

votes









6














You can use -path to match a given depth and prune there. Eg



find . -path '*/*/*' -prune -o -type d -print


would be maxdepth 1, as * matches the ., */* matches ./dir1, and */*/* matches ./dir1/dir2 which is pruned. If you use an absolute starting directory you need to add a leading / to the -path too.






share|improve this answer





















  • Hmmm, tricky. Couldn't you just remove one layer of /* from the end of the pattern, take out the -ooperator, and get the same result?
    – Wildcard
    Apr 11 '16 at 15:33










  • No, because * matches / as well, so the dir a/b/c/d/e would fit -path */*, sadly.
    – meuh
    Apr 11 '16 at 15:42










  • But a/b/c/d/e would never be reached, because -prune would be applied to a/b....
    – Wildcard
    Apr 11 '16 at 15:46






  • 1




    Sorry, I misread that -prune and -o were removed. If you keep the -prune the problem is that the */* will not match anything at a level above the maxdepth, eg the single directory a.
    – meuh
    Apr 11 '16 at 15:55
















6














You can use -path to match a given depth and prune there. Eg



find . -path '*/*/*' -prune -o -type d -print


would be maxdepth 1, as * matches the ., */* matches ./dir1, and */*/* matches ./dir1/dir2 which is pruned. If you use an absolute starting directory you need to add a leading / to the -path too.






share|improve this answer





















  • Hmmm, tricky. Couldn't you just remove one layer of /* from the end of the pattern, take out the -ooperator, and get the same result?
    – Wildcard
    Apr 11 '16 at 15:33










  • No, because * matches / as well, so the dir a/b/c/d/e would fit -path */*, sadly.
    – meuh
    Apr 11 '16 at 15:42










  • But a/b/c/d/e would never be reached, because -prune would be applied to a/b....
    – Wildcard
    Apr 11 '16 at 15:46






  • 1




    Sorry, I misread that -prune and -o were removed. If you keep the -prune the problem is that the */* will not match anything at a level above the maxdepth, eg the single directory a.
    – meuh
    Apr 11 '16 at 15:55














6












6








6






You can use -path to match a given depth and prune there. Eg



find . -path '*/*/*' -prune -o -type d -print


would be maxdepth 1, as * matches the ., */* matches ./dir1, and */*/* matches ./dir1/dir2 which is pruned. If you use an absolute starting directory you need to add a leading / to the -path too.






share|improve this answer












You can use -path to match a given depth and prune there. Eg



find . -path '*/*/*' -prune -o -type d -print


would be maxdepth 1, as * matches the ., */* matches ./dir1, and */*/* matches ./dir1/dir2 which is pruned. If you use an absolute starting directory you need to add a leading / to the -path too.







share|improve this answer












share|improve this answer



share|improve this answer










answered Apr 11 '16 at 7:43









meuh

31.5k11854




31.5k11854












  • Hmmm, tricky. Couldn't you just remove one layer of /* from the end of the pattern, take out the -ooperator, and get the same result?
    – Wildcard
    Apr 11 '16 at 15:33










  • No, because * matches / as well, so the dir a/b/c/d/e would fit -path */*, sadly.
    – meuh
    Apr 11 '16 at 15:42










  • But a/b/c/d/e would never be reached, because -prune would be applied to a/b....
    – Wildcard
    Apr 11 '16 at 15:46






  • 1




    Sorry, I misread that -prune and -o were removed. If you keep the -prune the problem is that the */* will not match anything at a level above the maxdepth, eg the single directory a.
    – meuh
    Apr 11 '16 at 15:55


















  • Hmmm, tricky. Couldn't you just remove one layer of /* from the end of the pattern, take out the -ooperator, and get the same result?
    – Wildcard
    Apr 11 '16 at 15:33










  • No, because * matches / as well, so the dir a/b/c/d/e would fit -path */*, sadly.
    – meuh
    Apr 11 '16 at 15:42










  • But a/b/c/d/e would never be reached, because -prune would be applied to a/b....
    – Wildcard
    Apr 11 '16 at 15:46






  • 1




    Sorry, I misread that -prune and -o were removed. If you keep the -prune the problem is that the */* will not match anything at a level above the maxdepth, eg the single directory a.
    – meuh
    Apr 11 '16 at 15:55
















Hmmm, tricky. Couldn't you just remove one layer of /* from the end of the pattern, take out the -ooperator, and get the same result?
– Wildcard
Apr 11 '16 at 15:33




Hmmm, tricky. Couldn't you just remove one layer of /* from the end of the pattern, take out the -ooperator, and get the same result?
– Wildcard
Apr 11 '16 at 15:33












No, because * matches / as well, so the dir a/b/c/d/e would fit -path */*, sadly.
– meuh
Apr 11 '16 at 15:42




No, because * matches / as well, so the dir a/b/c/d/e would fit -path */*, sadly.
– meuh
Apr 11 '16 at 15:42












But a/b/c/d/e would never be reached, because -prune would be applied to a/b....
– Wildcard
Apr 11 '16 at 15:46




But a/b/c/d/e would never be reached, because -prune would be applied to a/b....
– Wildcard
Apr 11 '16 at 15:46




1




1




Sorry, I misread that -prune and -o were removed. If you keep the -prune the problem is that the */* will not match anything at a level above the maxdepth, eg the single directory a.
– meuh
Apr 11 '16 at 15:55




Sorry, I misread that -prune and -o were removed. If you keep the -prune the problem is that the */* will not match anything at a level above the maxdepth, eg the single directory a.
– meuh
Apr 11 '16 at 15:55













7














@meuh's approach is inefficient as his -maxdepth 1 approach still lets find read the content of directories at level 1 to later ignore them otherwise. It will also not work properly with some find implementations (including GNU find) if some directory names contain sequences of bytes that don't form valid characters in the user's locale (like for file names in a different character encoding).



find . ( -name . -o -prune ) -extra-conditions-and-actions


is the more canonical way to implement GNU's -maxdepth 1 (or FreeBSD's -depth -2).



Generally though, it's -depth 1 you want (-mindepth 1 -maxdepth 1) as you don't want to consider . (depth 0), and then it's even simpler:



find . ! -name . -prune -extra-conditions-and-actions


For -maxdepth 2, that becomes:



find . ( ! -path './*/*' -o -prune ) -extra-conditions-and-actions


And that's where you run in the invalid character issues.



For instance, if you have a directory called Stéphane but that é is encoded in the iso8859-1 (aka latin1) charset (0xe9 byte) as was most common in Western Europe and the America up until the mid 2000s, then that 0xe9 byte is not a valid character in UTF-8. So, in UTF-8 locales, the * wildcard (with some find implementations) will not match Stéphane as * is 0 or more characters and 0xe9 is not a character.



$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . ( ! -path './*/*' -o -prune )
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith


My find (when the output goes to a terminal) displays that invalid 0xe9 byte as ? above. You can see that St<0xe9>phane/Chazelas was not pruned.



You can work around it by doing:



LC_ALL=C find . ( ! -path './*/*' -o -prune ) -extra-conditions-and-actions


But note that that affects all the locale settings of find and any application it runs (like via the -exec predicates).



$ LC_ALL=C find . ( ! -path './*/*' -o -prune )
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith


Now, I really get a -maxdepth 2 but note how the é in the second Stéphane properly encoded in UTF-8 is displayed as ?? as the 0xc3 0xa9 bytes (considered as two individual undefined characters in the C locale) of the UTF-8 encoding of é are not printable characters in the C locale.



And if I had added a -name '????????', I would have gotten the wrong Stéphane (the one encoded in iso8859-1).



To apply to arbitrary paths instead of ., you'd do:



find some/dir/. ! -name . -prune ...


for -mindepth 1 -maxdepth 1 or:



find some/dir/. ( ! -path '*/./*/*' -o -prune ) ...


for -maxdepth 2.



I would still do a:



(cd -P -- "$dir" && find . ...)


First because that makes the paths shorter which makes it less likely to run into path too long or arg list too long issues but also to work around the fact that find can't support arbitrary path arguments (except with -f with FreeBSD find) as it will choke on values of $dir like ! or -print...





The -o in combination with negation is a common trick to run two independent sets of -condition/-action in find.



If you want to run -action1 on files meeting -condition1 and independently -action2 on files meeting -condition2, you cannot do:



find . -condition1 -action1 -condition2 -action2


As -action2 would only be run for files that meet both conditions.



Nor:



find . -contition1 -action1 -o -condition2 -action2


As -action2 would not be run for files that meet both conditions.



find . ( ! -condition1 -o -action1 ) -condition2 -action2


works as ( ! -condition1 -o -action1 ) would resolve to true for every file. That assumes -action1 is an action (like -prune, -exec ... {} +) that always returns true. For actions like -exec ... ; that may return false, you may want to add another -o -something where -something is harmless but returns true like -true in GNU find or -links +0 or -name '*' (though note the issue about invalid characters above).






share|improve this answer



















  • 1




    Someday I will run into a bunch of Chinese files and I'll be very glad I've read your many answers about locale and valid characters. :)
    – Wildcard
    Dec 14 '16 at 23:37






  • 2




    @Wildcard, you (and even more so a Chinese person) is more likely to run into problem with British, French... file names than Chinese file names as Chinese filenames are more often encoded in UTF-8 than file names of alphabetical scripts that can generally be covered by a single-byte charset which was the norm up until relatively recently. There are other multi-byte charsets to cover Chinese character, but I'd expect Chinese people would have switched to UTF-8 earlier than westerners as those charsets have a number of nasty issues. See also the edit for an example.
    – Stéphane Chazelas
    Dec 15 '16 at 9:22
















7














@meuh's approach is inefficient as his -maxdepth 1 approach still lets find read the content of directories at level 1 to later ignore them otherwise. It will also not work properly with some find implementations (including GNU find) if some directory names contain sequences of bytes that don't form valid characters in the user's locale (like for file names in a different character encoding).



find . ( -name . -o -prune ) -extra-conditions-and-actions


is the more canonical way to implement GNU's -maxdepth 1 (or FreeBSD's -depth -2).



Generally though, it's -depth 1 you want (-mindepth 1 -maxdepth 1) as you don't want to consider . (depth 0), and then it's even simpler:



find . ! -name . -prune -extra-conditions-and-actions


For -maxdepth 2, that becomes:



find . ( ! -path './*/*' -o -prune ) -extra-conditions-and-actions


And that's where you run in the invalid character issues.



For instance, if you have a directory called Stéphane but that é is encoded in the iso8859-1 (aka latin1) charset (0xe9 byte) as was most common in Western Europe and the America up until the mid 2000s, then that 0xe9 byte is not a valid character in UTF-8. So, in UTF-8 locales, the * wildcard (with some find implementations) will not match Stéphane as * is 0 or more characters and 0xe9 is not a character.



$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . ( ! -path './*/*' -o -prune )
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith


My find (when the output goes to a terminal) displays that invalid 0xe9 byte as ? above. You can see that St<0xe9>phane/Chazelas was not pruned.



You can work around it by doing:



LC_ALL=C find . ( ! -path './*/*' -o -prune ) -extra-conditions-and-actions


But note that that affects all the locale settings of find and any application it runs (like via the -exec predicates).



$ LC_ALL=C find . ( ! -path './*/*' -o -prune )
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith


Now, I really get a -maxdepth 2 but note how the é in the second Stéphane properly encoded in UTF-8 is displayed as ?? as the 0xc3 0xa9 bytes (considered as two individual undefined characters in the C locale) of the UTF-8 encoding of é are not printable characters in the C locale.



And if I had added a -name '????????', I would have gotten the wrong Stéphane (the one encoded in iso8859-1).



To apply to arbitrary paths instead of ., you'd do:



find some/dir/. ! -name . -prune ...


for -mindepth 1 -maxdepth 1 or:



find some/dir/. ( ! -path '*/./*/*' -o -prune ) ...


for -maxdepth 2.



I would still do a:



(cd -P -- "$dir" && find . ...)


First because that makes the paths shorter which makes it less likely to run into path too long or arg list too long issues but also to work around the fact that find can't support arbitrary path arguments (except with -f with FreeBSD find) as it will choke on values of $dir like ! or -print...





The -o in combination with negation is a common trick to run two independent sets of -condition/-action in find.



If you want to run -action1 on files meeting -condition1 and independently -action2 on files meeting -condition2, you cannot do:



find . -condition1 -action1 -condition2 -action2


As -action2 would only be run for files that meet both conditions.



Nor:



find . -contition1 -action1 -o -condition2 -action2


As -action2 would not be run for files that meet both conditions.



find . ( ! -condition1 -o -action1 ) -condition2 -action2


works as ( ! -condition1 -o -action1 ) would resolve to true for every file. That assumes -action1 is an action (like -prune, -exec ... {} +) that always returns true. For actions like -exec ... ; that may return false, you may want to add another -o -something where -something is harmless but returns true like -true in GNU find or -links +0 or -name '*' (though note the issue about invalid characters above).






share|improve this answer



















  • 1




    Someday I will run into a bunch of Chinese files and I'll be very glad I've read your many answers about locale and valid characters. :)
    – Wildcard
    Dec 14 '16 at 23:37






  • 2




    @Wildcard, you (and even more so a Chinese person) is more likely to run into problem with British, French... file names than Chinese file names as Chinese filenames are more often encoded in UTF-8 than file names of alphabetical scripts that can generally be covered by a single-byte charset which was the norm up until relatively recently. There are other multi-byte charsets to cover Chinese character, but I'd expect Chinese people would have switched to UTF-8 earlier than westerners as those charsets have a number of nasty issues. See also the edit for an example.
    – Stéphane Chazelas
    Dec 15 '16 at 9:22














7












7








7






@meuh's approach is inefficient as his -maxdepth 1 approach still lets find read the content of directories at level 1 to later ignore them otherwise. It will also not work properly with some find implementations (including GNU find) if some directory names contain sequences of bytes that don't form valid characters in the user's locale (like for file names in a different character encoding).



find . ( -name . -o -prune ) -extra-conditions-and-actions


is the more canonical way to implement GNU's -maxdepth 1 (or FreeBSD's -depth -2).



Generally though, it's -depth 1 you want (-mindepth 1 -maxdepth 1) as you don't want to consider . (depth 0), and then it's even simpler:



find . ! -name . -prune -extra-conditions-and-actions


For -maxdepth 2, that becomes:



find . ( ! -path './*/*' -o -prune ) -extra-conditions-and-actions


And that's where you run in the invalid character issues.



For instance, if you have a directory called Stéphane but that é is encoded in the iso8859-1 (aka latin1) charset (0xe9 byte) as was most common in Western Europe and the America up until the mid 2000s, then that 0xe9 byte is not a valid character in UTF-8. So, in UTF-8 locales, the * wildcard (with some find implementations) will not match Stéphane as * is 0 or more characters and 0xe9 is not a character.



$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . ( ! -path './*/*' -o -prune )
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith


My find (when the output goes to a terminal) displays that invalid 0xe9 byte as ? above. You can see that St<0xe9>phane/Chazelas was not pruned.



You can work around it by doing:



LC_ALL=C find . ( ! -path './*/*' -o -prune ) -extra-conditions-and-actions


But note that that affects all the locale settings of find and any application it runs (like via the -exec predicates).



$ LC_ALL=C find . ( ! -path './*/*' -o -prune )
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith


Now, I really get a -maxdepth 2 but note how the é in the second Stéphane properly encoded in UTF-8 is displayed as ?? as the 0xc3 0xa9 bytes (considered as two individual undefined characters in the C locale) of the UTF-8 encoding of é are not printable characters in the C locale.



And if I had added a -name '????????', I would have gotten the wrong Stéphane (the one encoded in iso8859-1).



To apply to arbitrary paths instead of ., you'd do:



find some/dir/. ! -name . -prune ...


for -mindepth 1 -maxdepth 1 or:



find some/dir/. ( ! -path '*/./*/*' -o -prune ) ...


for -maxdepth 2.



I would still do a:



(cd -P -- "$dir" && find . ...)


First because that makes the paths shorter which makes it less likely to run into path too long or arg list too long issues but also to work around the fact that find can't support arbitrary path arguments (except with -f with FreeBSD find) as it will choke on values of $dir like ! or -print...





The -o in combination with negation is a common trick to run two independent sets of -condition/-action in find.



If you want to run -action1 on files meeting -condition1 and independently -action2 on files meeting -condition2, you cannot do:



find . -condition1 -action1 -condition2 -action2


As -action2 would only be run for files that meet both conditions.



Nor:



find . -contition1 -action1 -o -condition2 -action2


As -action2 would not be run for files that meet both conditions.



find . ( ! -condition1 -o -action1 ) -condition2 -action2


works as ( ! -condition1 -o -action1 ) would resolve to true for every file. That assumes -action1 is an action (like -prune, -exec ... {} +) that always returns true. For actions like -exec ... ; that may return false, you may want to add another -o -something where -something is harmless but returns true like -true in GNU find or -links +0 or -name '*' (though note the issue about invalid characters above).






share|improve this answer














@meuh's approach is inefficient as his -maxdepth 1 approach still lets find read the content of directories at level 1 to later ignore them otherwise. It will also not work properly with some find implementations (including GNU find) if some directory names contain sequences of bytes that don't form valid characters in the user's locale (like for file names in a different character encoding).



find . ( -name . -o -prune ) -extra-conditions-and-actions


is the more canonical way to implement GNU's -maxdepth 1 (or FreeBSD's -depth -2).



Generally though, it's -depth 1 you want (-mindepth 1 -maxdepth 1) as you don't want to consider . (depth 0), and then it's even simpler:



find . ! -name . -prune -extra-conditions-and-actions


For -maxdepth 2, that becomes:



find . ( ! -path './*/*' -o -prune ) -extra-conditions-and-actions


And that's where you run in the invalid character issues.



For instance, if you have a directory called Stéphane but that é is encoded in the iso8859-1 (aka latin1) charset (0xe9 byte) as was most common in Western Europe and the America up until the mid 2000s, then that 0xe9 byte is not a valid character in UTF-8. So, in UTF-8 locales, the * wildcard (with some find implementations) will not match Stéphane as * is 0 or more characters and 0xe9 is not a character.



$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . ( ! -path './*/*' -o -prune )
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith


My find (when the output goes to a terminal) displays that invalid 0xe9 byte as ? above. You can see that St<0xe9>phane/Chazelas was not pruned.



You can work around it by doing:



LC_ALL=C find . ( ! -path './*/*' -o -prune ) -extra-conditions-and-actions


But note that that affects all the locale settings of find and any application it runs (like via the -exec predicates).



$ LC_ALL=C find . ( ! -path './*/*' -o -prune )
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith


Now, I really get a -maxdepth 2 but note how the é in the second Stéphane properly encoded in UTF-8 is displayed as ?? as the 0xc3 0xa9 bytes (considered as two individual undefined characters in the C locale) of the UTF-8 encoding of é are not printable characters in the C locale.



And if I had added a -name '????????', I would have gotten the wrong Stéphane (the one encoded in iso8859-1).



To apply to arbitrary paths instead of ., you'd do:



find some/dir/. ! -name . -prune ...


for -mindepth 1 -maxdepth 1 or:



find some/dir/. ( ! -path '*/./*/*' -o -prune ) ...


for -maxdepth 2.



I would still do a:



(cd -P -- "$dir" && find . ...)


First because that makes the paths shorter which makes it less likely to run into path too long or arg list too long issues but also to work around the fact that find can't support arbitrary path arguments (except with -f with FreeBSD find) as it will choke on values of $dir like ! or -print...





The -o in combination with negation is a common trick to run two independent sets of -condition/-action in find.



If you want to run -action1 on files meeting -condition1 and independently -action2 on files meeting -condition2, you cannot do:



find . -condition1 -action1 -condition2 -action2


As -action2 would only be run for files that meet both conditions.



Nor:



find . -contition1 -action1 -o -condition2 -action2


As -action2 would not be run for files that meet both conditions.



find . ( ! -condition1 -o -action1 ) -condition2 -action2


works as ( ! -condition1 -o -action1 ) would resolve to true for every file. That assumes -action1 is an action (like -prune, -exec ... {} +) that always returns true. For actions like -exec ... ; that may return false, you may want to add another -o -something where -something is harmless but returns true like -true in GNU find or -links +0 or -name '*' (though note the issue about invalid characters above).







share|improve this answer














share|improve this answer



share|improve this answer








edited Dec 29 '16 at 3:46









cuonglm

102k23201301




102k23201301










answered Dec 14 '16 at 16:31









Stéphane Chazelas

299k54564913




299k54564913








  • 1




    Someday I will run into a bunch of Chinese files and I'll be very glad I've read your many answers about locale and valid characters. :)
    – Wildcard
    Dec 14 '16 at 23:37






  • 2




    @Wildcard, you (and even more so a Chinese person) is more likely to run into problem with British, French... file names than Chinese file names as Chinese filenames are more often encoded in UTF-8 than file names of alphabetical scripts that can generally be covered by a single-byte charset which was the norm up until relatively recently. There are other multi-byte charsets to cover Chinese character, but I'd expect Chinese people would have switched to UTF-8 earlier than westerners as those charsets have a number of nasty issues. See also the edit for an example.
    – Stéphane Chazelas
    Dec 15 '16 at 9:22














  • 1




    Someday I will run into a bunch of Chinese files and I'll be very glad I've read your many answers about locale and valid characters. :)
    – Wildcard
    Dec 14 '16 at 23:37






  • 2




    @Wildcard, you (and even more so a Chinese person) is more likely to run into problem with British, French... file names than Chinese file names as Chinese filenames are more often encoded in UTF-8 than file names of alphabetical scripts that can generally be covered by a single-byte charset which was the norm up until relatively recently. There are other multi-byte charsets to cover Chinese character, but I'd expect Chinese people would have switched to UTF-8 earlier than westerners as those charsets have a number of nasty issues. See also the edit for an example.
    – Stéphane Chazelas
    Dec 15 '16 at 9:22








1




1




Someday I will run into a bunch of Chinese files and I'll be very glad I've read your many answers about locale and valid characters. :)
– Wildcard
Dec 14 '16 at 23:37




Someday I will run into a bunch of Chinese files and I'll be very glad I've read your many answers about locale and valid characters. :)
– Wildcard
Dec 14 '16 at 23:37




2




2




@Wildcard, you (and even more so a Chinese person) is more likely to run into problem with British, French... file names than Chinese file names as Chinese filenames are more often encoded in UTF-8 than file names of alphabetical scripts that can generally be covered by a single-byte charset which was the norm up until relatively recently. There are other multi-byte charsets to cover Chinese character, but I'd expect Chinese people would have switched to UTF-8 earlier than westerners as those charsets have a number of nasty issues. See also the edit for an example.
– Stéphane Chazelas
Dec 15 '16 at 9:22




@Wildcard, you (and even more so a Chinese person) is more likely to run into problem with British, French... file names than Chinese file names as Chinese filenames are more often encoded in UTF-8 than file names of alphabetical scripts that can generally be covered by a single-byte charset which was the norm up until relatively recently. There are other multi-byte charsets to cover Chinese character, but I'd expect Chinese people would have switched to UTF-8 earlier than westerners as those charsets have a number of nasty issues. See also the edit for an example.
– Stéphane Chazelas
Dec 15 '16 at 9:22











0














I ran into an issue where I needed a way to limit depth when searching multiple paths (instead of just .).



For example:



$ find dir1 dir2 -name myfile -maxdepth 1


This led me to an alternate approach using -regex. The gist is:



-regex '(<list of paths | delimited>)/<filename>'


So, the above would be:



$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD


Without a filename:



$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD


Finally, for -maxdepth 2 the regex changes to: '(dir1|dir2)/([^/]*/){0,1}[^/]*$'






share|improve this answer





















  • This question asks for a standard (as in POSIX) solution though. Also -maxdepth would work with multiple search paths.
    – Kusalananda
    Dec 19 '18 at 8:56
















0














I ran into an issue where I needed a way to limit depth when searching multiple paths (instead of just .).



For example:



$ find dir1 dir2 -name myfile -maxdepth 1


This led me to an alternate approach using -regex. The gist is:



-regex '(<list of paths | delimited>)/<filename>'


So, the above would be:



$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD


Without a filename:



$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD


Finally, for -maxdepth 2 the regex changes to: '(dir1|dir2)/([^/]*/){0,1}[^/]*$'






share|improve this answer





















  • This question asks for a standard (as in POSIX) solution though. Also -maxdepth would work with multiple search paths.
    – Kusalananda
    Dec 19 '18 at 8:56














0












0








0






I ran into an issue where I needed a way to limit depth when searching multiple paths (instead of just .).



For example:



$ find dir1 dir2 -name myfile -maxdepth 1


This led me to an alternate approach using -regex. The gist is:



-regex '(<list of paths | delimited>)/<filename>'


So, the above would be:



$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD


Without a filename:



$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD


Finally, for -maxdepth 2 the regex changes to: '(dir1|dir2)/([^/]*/){0,1}[^/]*$'






share|improve this answer












I ran into an issue where I needed a way to limit depth when searching multiple paths (instead of just .).



For example:



$ find dir1 dir2 -name myfile -maxdepth 1


This led me to an alternate approach using -regex. The gist is:



-regex '(<list of paths | delimited>)/<filename>'


So, the above would be:



$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD


Without a filename:



$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD


Finally, for -maxdepth 2 the regex changes to: '(dir1|dir2)/([^/]*/){0,1}[^/]*$'







share|improve this answer












share|improve this answer



share|improve this answer










answered Dec 19 '18 at 8:33









Alissa H

1




1












  • This question asks for a standard (as in POSIX) solution though. Also -maxdepth would work with multiple search paths.
    – Kusalananda
    Dec 19 '18 at 8:56


















  • This question asks for a standard (as in POSIX) solution though. Also -maxdepth would work with multiple search paths.
    – Kusalananda
    Dec 19 '18 at 8:56
















This question asks for a standard (as in POSIX) solution though. Also -maxdepth would work with multiple search paths.
– Kusalananda
Dec 19 '18 at 8:56




This question asks for a standard (as in POSIX) solution though. Also -maxdepth would work with multiple search paths.
– Kusalananda
Dec 19 '18 at 8:56


















draft saved

draft discarded




















































Thanks for contributing an answer to Unix & Linux Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f275637%2flimit-posix-find-to-specific-depth%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Morgemoulin

Scott Moir

Souastre