Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

find . \( -name . -o -prune \) -type f -print # does not recurse, pls explain

116 views
Skip to first unread message

Am Nym

unread,
Nov 2, 2012, 9:50:49 PM11/2/12
to
From web searching:

find . \( -name . -o -prune \) -type f -print
find . \( -name . -o -prune \) -type d -print

and some quick checks, these commands list files or dirs in ".", and none
in subdirs of ".". (Pls let me know if and why this is not a good idiom to use.)

--
The purpose of this post is to understand why the above
find works. I think prune is like 'next' in awk or perl.
I do not understand what "-name ." is doing.

Theory:

A file may not be named "."; find therefore sees -name "."
as referring to the dirname of the pathname being examined;
so "-name ." will be true for files in ".", but not for subdirs.

Can anyone come up w/better explanation? My theory does not explain
why "find . \( -name . -o -prune \) -type d -print" works.

--
thanks

Alan Curry

unread,
Nov 2, 2012, 11:51:54 PM11/2/12
to
In article <wroehkb...@msgid.invalid.invalid>,
Am Nym <xtd...@gmail.com> wrote:
>From web searching:
>
> find . \( -name . -o -prune \) -type f -print
> find . \( -name . -o -prune \) -type d -print
>
>and some quick checks, these commands list files or dirs in ".", and none
>in subdirs of ".". (Pls let me know if and why this is not a good idiom
>to use.)

It's a little bit tricky, but I don't see anything wrong with it. If you just
want subdirectories of the current directory, expanding the glob "*/" is
probably better. If you have GNU find, -maxdepth 1 is easier to read.

>The purpose of this post is to understand why the above
>find works. I think prune is like 'next' in awk or perl.
>I do not understand what "-name ." is doing.

Since you started with "find .", the first file to be tested is "." which
matches the "-name ." predicate. That's on the left side of the -o operator,
which sort-circuits, so the -prune operator is not applied.

The "-name ." predicate will be false for everything else, regardless of
whether it's a file or directory, so the -prune will apply.

When the file being tested is not a directory, -prune is a no-op returning
true. When the file being tested *is* a directory, -prune disables recursion
into that directory, and returns true.

So except for the initial test of the top of the hierarchy, "-name ." is
false and "-prune" is true and recursion is disabled for all directories.

FALSE OR TRUE = TRUE so the parenthesized expression is true, and processing
continues with the -type. (There's an implicit AND between predicates that
don't have a -o between them.)

--
Alan Curry

Thomas 'PointedEars' Lahn

unread,
Nov 3, 2012, 3:34:21 AM11/3/12
to
Alan Curry wrote:

> Am Nym <xtd...@gmail.com> wrote:
>> From web searching:
>>
>> find . \( -name . -o -prune \) -type f -print
>> find . \( -name . -o -prune \) -type d -print
>>
>> and some quick checks, these commands list files or dirs in ".", and none
>> in subdirs of ".". (Pls let me know if and why this is not a good idiom
>> to use.)
> […]
>> The purpose of this post is to understand why the above
>> find works. I think prune is like 'next' in awk or perl.
>> I do not understand what "-name ." is doing.
>
> Since you started with "find .", the first file to be tested is "." which
> matches the "-name ." predicate. That's on the left side of the -o
> operator, which sort-circuits, so the -prune operator is not applied.

No. Each directory on a Unixoid system contains a hidden file named `.'
which points to itself. (And a hidden file named `..' pointing to its parent
directory.)

The first `.' in the find(1) command line, indeed all positional arguments
before the first option is/are considered to be the directory path(s) from
where to start the search; in this case the current directory. It is
equivalent to `./'. (That positional argument is optional in GNU find and
defaults to the current directory.)

As always, find(1) searches recursively from that directory but because of
`-name .' only considers the contents of the directory named so, IOW that
current directory. Then …

> The "-name ." predicate will be false for everything else, regardless of
> whether it's a file or directory, so the -prune will apply.
>
> When the file being tested is not a directory, -prune is a no-op returning
> true. When the file being tested *is* a directory, -prune disables
> recursion into that directory, and returns true.

Essentially true, but ISTM that it would have been better to just refer to
the man page (here: of GNU find):

| -prune True; if the file is a directory, do not descend into it. If
| depth is given, false; no effect. Because -delete implies -depth,
| you cannot usefully use -prune and -delete together.
| […]
| expr1 -o expr2
| Or; expr2 is not evaluated if expr1 is true.

--
PointedEars

Twitter: @PointedEars2
Please do not Cc: me. / Bitte keine Kopien per E-Mail.

Am Nym

unread,
Nov 3, 2012, 11:42:52 AM11/3/12
to
Thomas 'PointedEars' Lahn <Point...@web.de> writes:

Thanks to both of you for your help! (I'm stuck w/only HP-UX find.)
One more comment below:

> Alan Curry wrote:
>
>> Am Nym <xtd...@gmail.com> wrote:
>>> From web searching:
>>>
>>> find . \( -name . -o -prune \) -type f -print
>>> find . \( -name . -o -prune \) -type d -print
>>>
>>> and some quick checks, these commands list files or dirs in ".", and none
>>> in subdirs of ".". (Pls let me know if and why this is not a good idiom
>>> to use.)
>> […]
>>> The purpose of this post is to understand why the above
>>> find works. I think prune is like 'next' in awk or perl.
>>> I do not understand what "-name ." is doing.
>>
>> Since you started with "find .", the first file to be tested is "." which
>> matches the "-name ." predicate. That's on the left side of the -o
>> operator, which sort-circuits, so the -prune operator is not applied.
>
> No. Each directory on a Unixoid system contains a hidden file named `.'
> which points to itself. (And a hidden file named `..' pointing to its parent
> directory.)
>
> The first `.' in the find(1) command line, indeed all positional arguments
> before the first option is/are considered to be the directory path(s) from
> where to start the search; in this case the current directory.

I assume the \(-name . -o prune \) is considered "before the first option", so
find further refines only it's list of dirs to decend into
from \(-name . -o prune \); ie it's not considering filenames until
"-type" above.

--snip
> As always, find(1) searches recursively from that directory but because of
> `-name .' only considers the contents of the directory named so, IOW that
> current directory.
--snip

related example that might be useful to others:

/tmp $ touch a b c
/tmp $ mkdir foo
/tmp $ cd foo
/tmp/foo $ touch e f g
/tmp/foo $ cd ..
/tmp $ mkdir bar
/tmp $ touch bar/{h,i,j}
/tmp $ find . \( -name . -o -name foo -o -prune \) -print
.
./a
./b
./bar
./c
./foo
./foo/e
./foo/f
./foo/g

Thomas 'PointedEars' Lahn

unread,
Nov 3, 2012, 2:11:12 PM11/3/12
to
Am Nym wrote:
^^^^^^
If that is not your real name, you better change that to it.

> Thanks to both of you for your help! (I'm stuck w/only HP-UX find.)

You are welcome.

> Thomas 'PointedEars' Lahn <Point...@web.de> writes:
>> Alan Curry wrote:
>>> Am Nym <xtd...@gmail.com> wrote:
>>>> From web searching:
>>>> find . \( -name . -o -prune \) -type f -print
>>>> find . \( -name . -o -prune \) -type d -print
>> […]
>> The first `.' in the find(1) command line, indeed all positional
>> arguments before the first option is/are considered to be the directory
>> path(s) from where to start the search; in this case the current
>> directory.
>
> I assume the \(-name . -o prune \) is considered "before the first
> option", so find further refines only it's list of dirs to decend into
> from \(-name . -o prune \); ie it's not considering filenames until
> "-type" above.

No, find(1) is a very special command (one of those that do not fully comply
with the POSIX Utility Syntax Guidelines as specified in IEEE 1003.1,
section 12.2). There are options as you know them from other commands
("options"), and there are predicates that are also options ("predicates")
but are treated like positional arguments.

While options may be in any order (except that a warning is issued if they
precede a positional argument or a predicate), positional arguments and
predicates are processed in-order (to allow for short-circuit evaluation
with -o[r], and post-processing like -exec, -printf, -print, and -ls).
Predicates can be grouped with `( … )' to change precedence; you need to
quote or escape find's `(' and `)' because they are part of subshell syntax
in the shell. Therefore, `\(-name . -o prune \)' does not work, but
`\( -name . -o prune \)' does.

So in this case, there are *no* (real) options. There is the first
positional argument: `.'. The rest of the positional arguments are
predicates.

RTFineM.

> --snip
>> As always, find(1) searches recursively from that directory but because
>> of `-name .' only considers the contents of the directory named so, IOW
>> that current directory.
> --snip
>
> related example that might be useful to others:
>
> /tmp $ touch a b c
> /tmp $ mkdir foo
> /tmp $ cd foo
> /tmp/foo $ touch e f g
> /tmp/foo $ cd ..
> /tmp $ mkdir bar
> /tmp $ touch bar/{h,i,j}
> /tmp $ find . \( -name . -o -name foo -o -prune \) -print
> .
> ./a
> ./b
> ./bar
> ./c
> ./foo
> ./foo/e
> ./foo/f
> ./foo/g

ACK.

Alan Curry

unread,
Nov 3, 2012, 2:22:55 PM11/3/12
to
In article <1861033.x...@PointedEars.de>,
Thomas 'PointedEars' Lahn <use...@PointedEars.de> wrote:
>Alan Curry wrote:
>
>> Am Nym <xtd...@gmail.com> wrote:
>>> From web searching:
>>>
>>> find . \( -name . -o -prune \) -type f -print
>>> find . \( -name . -o -prune \) -type d -print
>>>
>>> and some quick checks, these commands list files or dirs in ".", and none
>>> in subdirs of ".". (Pls let me know if and why this is not a good idiom
>>> to use.)
>> […]
>>> The purpose of this post is to understand why the above
>>> find works. I think prune is like 'next' in awk or perl.
>>> I do not understand what "-name ." is doing.
>>
>> Since you started with "find .", the first file to be tested is "." which
>> matches the "-name ." predicate. That's on the left side of the -o
>> operator, which sort-circuits, so the -prune operator is not applied.
>
>No. Each directory on a Unixoid system contains a hidden file named `.'
>which points to itself. (And a hidden file named `..' pointing to its parent
>directory.)

If you hadn't started with "No", I wouldn't think you were contradicting me.
Where's the conflict between what you wrote and what I wrote? I see no errors
in either (except the misspelled "short-circuit") and have no idea why you
chose to inject this idiot-level lecture on "." and ".."

>
>The first `.' in the find(1) command line, indeed all positional arguments
>before the first option is/are considered to be the directory path(s) from
>where to start the search; in this case the current directory. It is
>equivalent to `./'. (That positional argument is optional in GNU find and
>defaults to the current directory.)

This also doesn't conflict with what I said. Do you object to my description
of this as the "first file to be tested"? I stand by that. The paths given to
find as search start points *are* tested against the predicates, as you can
see here:

$ mkdir /tmp/emptydir
$ cd /tmp/emptydir
$ find . -name . -prune -print
.
$ find . -name foo -prune -print
$

The file named "." (which is a directory, I should't really waste time
pointing that out because everyone obviously already knows it) is tested by
the -name predicate, which decides whether it will pass through to the -prune
and -print predicates.

--
Alan Curry

Thomas 'PointedEars' Lahn

unread,
Nov 3, 2012, 2:31:45 PM11/3/12
to
Thomas 'PointedEars' Lahn wrote:

> […] find(1) is a very special command (one of those that do not fully
> comply with the POSIX Utility Syntax Guidelines as specified in IEEE
> 1003.1, section 12.2). There are options as you know them from other
> commands ("options"), and there are predicates that are also options
> ("predicates") but are treated like positional arguments.
>
> While options may be in any order (except that a warning is issued if they
> precede a positional argument or a predicate), […]
^^^^^^^^^^^^^^
I meant to say "or _are preceded by_ a predicate"; but I had mistaken GNU's
-maxdepth for a real option when it is in fact a special predicate:

| $ find -maxdepth 1 . \( -name . -o -prune \) -print
| find: paths must precede expression: .
| Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|
| exec] [path...] [expression]

| $ find . \( -name . -o -prune \) -print -maxdepth 1
| find: warning: you have specified the -maxdepth option after a non-option
| argument (, but options are not positional (-maxdepth affects tests
| specified before it as well as those specified after it). Please specify
| options before other arguments.
|
| .

| $ find . -maxdepth 1 \( -name . -o -prune \) -print
| .

(Yes, there is redundance. It is an *example*. And yes, GNU's Not Unix.)

--
PointedEars

Twitter: @PointedEars2
Please do not Cc: me. / Bitte keine Kopien er E-Mail.

Eric

unread,
Nov 3, 2012, 2:21:35 PM11/3/12
to
On 2012-11-03, Am Nym <xtd...@gmail.com> wrote:
<conversation snipped>
> I assume the \(-name . -o prune \) is considered "before the first
> option", so find further refines only it's list of dirs to decend into
> from \(-name . -o prune \); ie it's not considering filenames until
> "-type" above.

You really should look at the man page:

find [-H] [-L] [-P] [-D debugopts] [-Olevel] [path...] [expression]

(that's for GNU find, but they all have the path and the expression).

-type is not an option, it's a test, as is -name. -prune is an action.
If there were any options in the expression, they would be "processed
when the command line is parsed, while the tests don't do anything
until files are examined". (This is not about the command-line options
like -L, your find may not have any.)

Find starts with the given path(s), "." in your examples, then applies
the expression to each object it finds. If it is considering a directory
that is not subject to -prune, it recursively processes the directory
contents. There is no list, it looks at one item at a time and applies
the expression to it.

So what you see is:

start with .
-name . is true, the -o part is not processed
-type f is false, do not do anything else to .
but . is a directory so look at its contents

look at a file in .
-name . is false, so look at the -o part
-prune is a true no-op for a file, so just keep going
-type f is true
according to the implicit -print at the end, print the file name

look at a directory in .
-name . is false, so look at the -o part
-prune is true, remember that it applies to this directory
-type f is false, do not do anything else to the directory
but it is a directory
do not look at its contents because -prune applies

and so on for every file and directory in .

Eric
--
ms fnd in a lbry

Thomas 'PointedEars' Lahn

unread,
Nov 3, 2012, 2:52:20 PM11/3/12
to
Alan Curry wrote:

> Thomas 'PointedEars' Lahn <use...@PointedEars.de> wrote:
>> Alan Curry wrote:
>>> Am Nym <xtd...@gmail.com> wrote:
>>>> From web searching:
>>>>
>>>> find . \( -name . -o -prune \) -type f -print
>>>> find . \( -name . -o -prune \) -type d -print
>>>>
>>>> and some quick checks, these commands list files or dirs in ".", and
>>>> none
>>>> in subdirs of ".". (Pls let me know if and why this is not a good
>>>> idiom to use.)
>>> […]
>>>> The purpose of this post is to understand why the above
>>>> find works. I think prune is like 'next' in awk or perl.
>>>> I do not understand what "-name ." is doing.
>>> Since you started with "find .", the first file to be tested is "."
>>> which matches the "-name ." predicate. That's on the left side of the -o
>>> operator, which sort-circuits, so the -prune operator is not applied.
>> No. Each directory on a Unixoid system contains a hidden file named `.'
>> which points to itself. (And a hidden file named `..' pointing to its
>> parent directory.)
>
> If you hadn't started with "No", I wouldn't think you were contradicting
> me. Where's the conflict between what you wrote and what I wrote? I see no
> errors in either (except the misspelled "short-circuit") and have no idea
> why you chose to inject this idiot-level lecture on "." and ".."

The reason that `.' is found (and need _not_ be found *first* as you
claimed) is _not_ (as you claimed) that the first positional argument is
`.', but that `-name .' descends into the directory named `.' *in* `.'
(`./.', which is of course equivalent to `.').

>> The first `.' in the find(1) command line, indeed all positional
>> arguments before the first option is/are considered to be the directory
>> path(s) from where to start the search; in this case the current
>> directory. It is equivalent to `./'. (That positional argument is
>> optional in GNU find and defaults to the current directory.)
>
> This also doesn't conflict with what I said. Do you object to my
> description of this as the "first file to be tested"? I stand by that. The
> paths given to find as search start points *are* tested against the
> predicates,

Evidentially, for the -name predicate they are not (for that would be
superfluous if the paths were only `.', and error-prone if they were not).
Only the files that the directories specified by the paths *contain* are
tested with -name, of course. One of those files is `.' first-level in each
path.

However, the paths are tested when using the -path predicate, for example,
which can be found (according to GNU find's manpage) both in GNU find and
on the OP's HP-UX.

> as you can see here:
>
> $ mkdir /tmp/emptydir
> $ cd /tmp/emptydir
> $ find . -name . -prune -print
> .
> $ find . -name foo -prune -print
> $
>
> The file named "." […] is tested by the -name predicate, which decides
> whether it will pass through to the -prune and -print predicates.

Your conclusion is true, but your premise is false; as a result, your logic
is flawed. See also "Am Nym"'s example.

Thomas 'PointedEars' Lahn

unread,
Nov 3, 2012, 3:07:01 PM11/3/12
to
Eric wrote:

> On 2012-11-03, Am Nym <xtd...@gmail.com> wrote:
> <conversation snipped>
>> I assume the \(-name . -o prune \) is considered "before the first
>> option", so find further refines only it's list of dirs to decend into
>> from \(-name . -o prune \); ie it's not considering filenames until
>> "-type" above.
>
> You really should look at the man page:
>
> find [-H] [-L] [-P] [-D debugopts] [-Olevel] [path...] [expression]
>
> (that's for GNU find, but they all have the path and the expression).
>
> -type is not an option, it's a test, as is -name. -prune is an action.

Yes, that is one way to look at it. However, the more useful umbrella term
here is "predicate"; the manpage does describe those arguments as options
(unfortunately), but they are part of an expression based on predicate logic
after all.

For example, -type f as a "test" is true if and only if the file found is a
regular file. -prune as an "action" actually is a tautological predicate
(always true) with special meaning. -exec as an "action" is either true or
false depending on what the command run by -exec has as exit status; with
`-exec COMMAND \; -print' nothing will be printed if COMMAND is not
successful because `false AND x' is always `false'. And so on.

Alan Curry

unread,
Nov 3, 2012, 5:53:25 PM11/3/12
to
In article <4595675.4...@PointedEars.de>,
Thomas 'PointedEars' Lahn <use...@PointedEars.de> wrote:
>Alan Curry wrote:
>
>> Thomas 'PointedEars' Lahn <use...@PointedEars.de> wrote:
>>
>> If you hadn't started with "No", I wouldn't think you were contradicting
>> me. Where's the conflict between what you wrote and what I wrote? I see no
>> errors in either (except the misspelled "short-circuit") and have no idea
>> why you chose to inject this idiot-level lecture on "." and ".."
>
>The reason that `.' is found (and need _not_ be found *first* as you
>claimed) is _not_ (as you claimed) that the first positional argument is
>`.', but that `-name .' descends into the directory named `.' *in* `.'
>(`./.', which is of course equivalent to `.').

Well now at least I can see the difference between what you wrote and what I
wrote. Luckily for me your assertion is wrong.

>>
>> This also doesn't conflict with what I said. Do you object to my
>> description of this as the "first file to be tested"? I stand by that. The
>> paths given to find as search start points *are* tested against the
>> predicates,
>
>Evidentially, for the -name predicate they are not (for that would be
>superfluous if the paths were only `.', and error-prone if they were not).
>Only the files that the directories specified by the paths *contain* are
>tested with -name, of course. One of those files is `.' first-level in each
>path.

You've stated a testable hypothesis. This is good. In your theory of
operation, find will always find something to match "-name ." under the
starting directory. In my theory of operation, "-name ." will only match if
"." is actually given as an argument.

$ mkdir /tmp/emptydir
$ cd /tmp/emptydir
$ mkdir foo bar
$ find foo -name . -prune -print
$ find foo -name foo -prune -print
foo
$ find * -name . -prune -print
$ find * -name foo -prune -print
foo
$

The test shows that my theory was correct. When "." appears among the
arguments to find, the string "." is matched against the -name pattern. When
something else is given as an argument, that something is matched against the
-name pattern.

An addition to the experiment:

$ touch baz
$ find * -name 'ba*' -prune -print
bar
baz
$

The arguments given to find to start the search (here, a * which expands to the
3 arguments foo bar baz) are tested against the -name predicate and the ones
that match are passed through to -print. One of them is a directory, the
other one isn't. The -name predicate doesn't care about the difference
because it just matches the filename string against the pattern.

In conclusion: "find . -name ." does indeed match the . because it begins the
search by testing the first name in the path-list (in this case ".") against
the predicates.

--
Alan Curry

Am Nym

unread,
Nov 3, 2012, 7:39:49 PM11/3/12
to
Eric <er...@deptj.eu> writes:

> On 2012-11-03, Am Nym <xtd...@gmail.com> wrote:
> <conversation snipped>
>> I assume the \(-name . -o prune \) is considered "before the first
>> option", so find further refines only it's list of dirs to decend into
>> from \(-name . -o prune \); ie it's not considering filenames until
>> "-type" above.

Glad to be corrected on above.

--snip
> -type is not an option, it's a test, as is -name. -prune is an action.
> If there were any options in the expression, they would be "processed
> when the command line is parsed, while the tests don't do anything
> until files are examined".

Thanks, good to know.. think this is the relevant GNU textinfo node:

File: find.info, Node: find Expressions

> (This is not about the command-line options
> like -L, your find may not have any.)
>
> Find starts with the given path(s), "." in your examples, then applies
> the expression to each object it finds. If it is considering a directory
> that is not subject to -prune, it recursively processes the directory
> contents. There is no list, it looks at one item at a time and applies
> the expression to it.
>
> So what you see is:
>
> start with .
> -name . is true, the -o part is not processed
> -type f is false, do not do anything else to .
> but . is a directory so look at its contents
>
> look at a file in .
> -name . is false, so look at the -o part
> -prune is a true no-op for a file, so just keep going

Key point. I think this clears it up for me!

--snip

--
thanks again

--
key words:
nonrecursive find

Am Nym

unread,
Nov 3, 2012, 8:00:08 PM11/3/12
to
Thomas 'PointedEars' Lahn <Point...@web.de> writes:
> No, find(1) is a very special command (one of those that do not fully comply
> with the POSIX Utility Syntax Guidelines as specified in IEEE 1003.1,
> section 12.2).

Thanks for the reference, I'll look it over:

http://pubs.opengroup.org/onlinepubs/009696699/basedefs/xbd_chap12.html

> There are options as you know them from other commands
> ("options"), and there are predicates that are also options ("predicates")
> but are treated like positional arguments.
>
> While options may be in any order (except that a warning is issued if they
> precede a positional argument or a predicate), positional arguments and
> predicates are processed in-order (to allow for short-circuit evaluation
> with -o[r], and post-processing like -exec, -printf, -print, and -ls).
> Predicates can be grouped with `( … )' to change precedence; you need to
> quote or escape find's `(' and `)' because they are part of subshell syntax
> in the shell. Therefore, `\(-name . -o prune \)' does not work, but
> `\( -name . -o prune \)' does.
>
> So in this case, there are *no* (real) options. There is the first
> positional argument: `.'. The rest of the positional arguments are
> predicates.
>
> RTFineM.

will do, in Linux, this will show a relevant section:

info -n "find Expressions" -f find.info

--
thanks again

Am Nym

unread,
Nov 3, 2012, 9:42:21 PM11/3/12
to
Nice/enjoyed this thread - admittedly do not follow all of it.

So ..
find . -name . -o -prune -type f -print
is a simplier way to list only files in CWD:

$ mkdir bar; touch baz
$ touch bar/{a,b,c}
$ touch a s d f
$ find . -name . -prune -print
.
$ find . -name . -o -prune -type f -print
./s
./d
./baz
./a
./f

0 new messages