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
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • @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`](https://www.freebsd.org/cgi/man.cgi?query=find&sektion=0&manpath=FreeBSD%2012.1-RELEASE%20and%20Ports&arch=default&format=html)'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. `gfind`s 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