[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Specialized disk directory tools



On Mon, Sep 15, 2025 at 11:22:04 -0400, duh wrote:
> I can be a little slow so  the [ find ... -iname ".*" -o ... ] did not work
> for me probably because I do not use 'find' enough to know what to do with
> the trailing ...

It's not trivial.  It would help if you stated *exactly* what result
you want, but let's go with one possible interpretation:

  "I want to see all the regular files from this directory or any of
  its subdirectories, except that I want to exclude any files that begin
  with a dot, or any directories that begin with a dot."

The goal here is to avoid recursing into the excluded directories,
because that would be a waste of time.  For that, we want the -prune
directive, which is quite tricky to use correctly.  Let's go step by
step.

We can write -name ".*" to match either a file or directory whose name
starts with a dot.  (We don't need -iname which is the case-insensitive
variant.)

We can extend that to -name ".*" -prune to get the pruning done.  This
is kind of like an "if/then" statement in find.  "If the name matches
this pattern, -prune it."

Now, here's where it gets tricky.  If we just do the obvious thing:

hobbit:~$ mkdir /tmp/x && cd /tmp/x
hobbit:/tmp/x$ mkdir -p d1/.d2/d3 d4 d5
hobbit:/tmp/x$ touch .f0 d1/f1 d1/.d2/f2 d1/.d2/d3/f3 d4/f4 d5/.f5
hobbit:/tmp/x$ find . -name ".*" -prune
.

That fails.  "." is matched by the ".*" pattern, so we prune the entire
directory hierarchy.  Clearly that's not what we want.  So let's sidestep
that issue entirely:

hobbit:/tmp/x$ find "$PWD" -name ".*" -prune
/tmp/x/.f0
/tmp/x/d5/.f5
/tmp/x/d1/.d2

Right, well, we've definitely identified the objects beginning with dot,
but how come the -prune didn't cause the files not to be printed?  Well,
because that's not what -prune does.  The -prune directive is "always
true" according to the documentation.  It simply stops find from recursing
into those directories.

So, how can we tell find to NOT print what has been pruned?  We have
to rely on the exit status of the -name operator that we used before
it.  That's the one which tells us what to print.

What we have right now are two directives chained with an implied "AND"
operator:   -name ".*" AND -prune   and the second one is always true
if it's triggered.  The key here is that the chained group CAN have
a non-true exit status, if the -prune isn't triggered.

If find is traversing an object that fails the -name ".*" check, then
the -name generates a "false" result, and the -prune is never evaluated.
Therefore the entire expression is false at that point.

So, we can do this:

hobbit:/tmp/x$ find "$PWD" -name ".*" -prune -o -print
/tmp/x
/tmp/x/d5
/tmp/x/d4
/tmp/x/d4/f4
/tmp/x/d1
/tmp/x/d1/f1

Better.  However, it's still printing directories, and we only wanted it
to print files.  We need to add a -type f check, so that only regular
files are printed.

The key here is that we mustn't do the -type f check too early, or we
might stop recursing into directories, or break the -prune or something.
We wouldn't want that.  We only want the -type check to be done *after*
the pruning is finished.

We can add it here:

hobbit:/tmp/x$ find "$PWD" -name ".*" -prune -o -type f -print
/tmp/x/d4/f4
/tmp/x/d1/f1

Now, let's tackle the issue we sidestepped earlier: we don't want
absolute pathnames, but also we don't want to prune ".".  Only everything
else that begins with a dot.

For this, we're going to expand our -name check.  The pruning should
only be done for objects that pass the -name check AND a second check:

hobbit:/tmp/x$ find . -name ".*" ! -path . -prune -o -type f -print
./d4/f4
./d1/f1

And there we have our final solution.  Let's read it out step by step
to make sure it's clear what's happening:

    find .        # Recurse starting in the current directory.
    -name ".*"    # If the current object's name begins with dot ...
    ! -path .     # ... but isn't exactly "." ...
    -prune        # ... then prune it.
    -o            # If the pruning didn't happen, then ...
    -type f       # ... if the current object is a regular file ...
    -print        # ... print its name and a newline to stdout.


Reply to: