1

tried to have "find" to perform conditional depth search,
if the found is in current dir. and the found is a file then do explain its output with verbal, else then do output just like normally

$ find ~+ -maxdepth 1 \( -type f -printf 'File: %p\n' -o  -printf '%p\n' \) -o -mindepth 2 -printf '%p\n'

find: warning: you have specified the -mindepth option after a non-option argument (, but options are not positional (-mindepth affects tests specified before it as well as those specified after it)..

Why is it failed and How to solve such the demanded condition ?

1 Answers1

2

-maxdepth/-mindepth (a non-standard GNU extension, though now supported by quite a few other find implementations) are not condition predicates, they're global flags that affect the way find descends into directories.

It's possible to implement the effect of -maxdepth standardly with a combination of -path and -prune.

FreeBSD's find has -depth n/-n/+ to match files at depth n / < n / > n, so on FreeBSD or derivatives (macOS, DragonFly BSD...), it would just be:

find ~+ -depth 1 -type f -exec printf 'File: %s' {} ';' -o -print

here using -exec printf in place of the GNU-specific -printf.

Technically, printf could fail which would trigger -print. Using -exec ... {} + instead of -exec ... {} ';' would address that but affect the order of display. Or it could be changed to:

find ~+ -depth 1 -type f '(' -exec printf 'File: %s' {} ';' -o -true ')' -o -print

Or:

find ~+ '(' ! -depth 1 -o ! -type f ')' -print -o -exec printf 'File: %s' {} ';'

Standardly, -path can be used instead (though not as straightforwardly).

LC_ALL=C find ~+/. -path '*/./*/*' -print -o \
  -type f -printf 'File: %p\n' -o -print

Or to limit the depth to 2 (as in an earlier version of my answer where I thought your -mindepth 2 was -maxdepth 2)

LC_ALL=C find ~+/. -path '*/./*/*' -prune -print -o \
  -type f -printf 'File: %p\n' -o -print

(still not standard as -printf is GNU specific).

We append /. to the path (which is otherwise guaranteed not to occur in $PWD/~+), to mark the depth 0 point for find's -path.

You can't use -path "$PWD/*/*" instead (as in your suggested edit) as that wouldn't work properly for values of $PWD that contain wildcard characters or backslashes (since -path considers its argument as a wildcard pattern).

Compare:

$ mkdir -p '[1]/2/3/4'
$ touch '[1]/2/3/4/file'
$ cd '[1]'
$ LC_ALL=C find ~+ -path "$PWD/*/*" -print -o -type f -printf 'File: %p\n' -o -print
/tmp/[1]
/tmp/[1]/2
/tmp/[1]/2/3
/tmp/[1]/2/3/4
File: /tmp/[1]/2/3/4/file
$ LC_ALL=C find ~+/. -path '*/./*/*' -print -o -type f -printf 'File: %p\n' -o -print
/tmp/[1]/.
/tmp/[1]/./2
/tmp/[1]/./2/3
/tmp/[1]/./2/3/4
/tmp/[1]/./2/3/4/file

Another approach is to append //, though that's less portable as some find implementations remove those excess trailing /s.

You can pipe to sed 's:/\./:/:' to remove those /./s on output.

LC_ALL=C is needed with GNU find where * fails to match path components that contain sequence of bytes not forming valid characters.


While GNU find has no predicate to explicitly match on the depth of files, its -printf predicate can print that depth. So here, you could add that File: prefix to regular files at depth 1 with some post-processing:

find . -printf '%d%y,%p\0' | # print depth, type and path
  sed -z 's/^1f,/&File: /' | # add "File: " prefix for regulars at depth 1  
  cut -zd, -f2-            | # remove the depth and type
  tr '\0' '\n'               # NL delimited for user consumption
  • @nonoch, I rejected the edit as the argument of -path is taken as a pattern, so -path "$PWD/*/*" wouldn't work with arbitrary values of $PWD (those containing wildcards or backslashes specifically). – Stéphane Chazelas Oct 03 '20 at 09:28
  • @nonoch, I just realised you had -mindepth 2 and not -maxdepth 2 though, so I'll need to change that. – Stéphane Chazelas Oct 03 '20 at 09:31
  • @nonoch, as I said, -path "$PWD/*/*" wouldn't work with arbitrary values of $PWD. See comment above and my latest edit that should also fix my earlier misreading of your question. – Stéphane Chazelas Oct 03 '20 at 09:41
  • @nonoch, see example in my latest edit. – Stéphane Chazelas Oct 03 '20 at 09:55
  • @StéphaneChazelas -depth is a POSIX standard find primary that inverts the order of the output. It causes the directory content to be printed before the directory. This is not related to-mindepth at all. Also note that libfind implements -mindepth and -maxdelth as normal primaries that always yield in TRUE. – schily Oct 03 '20 at 22:41
  • @schily, I'm not talking of the -depth alone standard predicate here, but of the unrelated FreeBSD find's -depth n extension. The behaviour for find . -depth 1 is unspecified by POSIX. By GNU extension, I meant it was of GNU origin. -mindepth/-maxdepth is now supported by a few other implementations. – Stéphane Chazelas Oct 04 '20 at 05:38
  • GNU progams are frequently implementing badly designed interfaces (see the useless -amin primary in gfind compared to the enhanced -atime in libfind (see http://schilytools.sourceforge.net/man/man1/sfind.1.html) and FreeBSD, but the hard to parse -depth extension from FreeBSD is an unhappy decision. gfinds claim -mindepth to be an option is nonsense, since then it would need to appear before file type arguments. BTW: Even FreeBSD supports -mindepth, so it makes no sense to mention the FreeBSD mistake with -depth xxx. – schily Oct 04 '20 at 11:26
  • @schily, -mindepth/-maxdepth and -depth don't do the same thing. -mindepth/-maxdepth are global flags (like you say not options in that they are not processed like the -L, -H... ones) which affect the traversal and would be of no use in this Q&A. -depth n is a condition predicate that returns true if the file is at depth n but otherwise doesn't affect find's traversal of the directory tree (as long as you don't use -prune based on its result). – Stéphane Chazelas Oct 04 '20 at 12:28
  • @schily About GNU find and options, note that it's all about terminology. find initially didn't have any option in the getopt() sense of the term. GNU find classifies its predicates as Options, Test or Action ones and has done so since long before POSIX introduced getopt()-style options for it (one of which, -L, duplicates the -follow option predicate). I used flag in my answer to avoid the possible confusion. – Stéphane Chazelas Oct 04 '20 at 13:13
  • I still see no benefit from the FreeBSd enhancement and BTW: Whom do you like to explain the getopt() thing? – schily Oct 05 '20 at 13:56
  • @schily, -depth n matches at depth n. That's the predicate that answers exactly the OP's question. See edit where I make that more explicit. Without it, you have to use those convoluted approaches with -path and where you need to fix the locale to C with all the implications it has. That's the whole point of my answer. – Stéphane Chazelas Oct 05 '20 at 14:16