[Top][All Lists]

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

Re: find improvement wish

From: Bob Proulx
Subject: Re: find improvement wish
Date: Sun, 7 Feb 2021 20:47:04 -0700

Chun Yip Wong wrote:
> I prefer using find with script in this way: find $dir $pattern.where
> pattern=" -name *.sh ! -name *.txt ... etc."

There is a problem with insufficiently quoted shell file glob meta
characters in that pattern.  That's a problem unrelated to find.

> The built findutils version is 4.7. it fails to work with such message
> find: paths must precede expression: 'test.sh'
> find: possible unquoted pattern after predicate '-name'

And there is a warning about "possible unquoted pattern" but since we
can see the pattern we know that yes the pattern is not quoted and
that is an error of the command line.

> I know when there are multiple files, such as  a.txt b.txt c.txt, find .
> -name *.txt will cause such messages and solve with quoted the '*.txt'
> It is more convenient to define a pattern and no need to quote all
> extensions one by one, line by line using scripts.

The "*" file glob character is a shell meta-character.  The shell
being your command line shell.  One of bash, ksh, zsh, or possibly
even tcsh, csh, or possibly another.  The shell is not find.  The
shell reads the command and expands shell file glob patterns like
"*.txt" into files that match.  After the shell has expanded these
characters then the resulting multiple arguments are passed to the
command.  In this case the command find.

In order to observe this expansion the "echo" command is most easily
used.  If one uses echo they can see how the patterns are expanded.
Or if there is no match then how it is not expanded.  It is a data
dependent expansion based upon whether any files exist that match the
pattern or not.  Which is why any characters that happen to be shell
metacharacters must be quoted to prevent this expansion.  For use in
the fine -name option it is not desired to have them expanded and
therefore this must be prevented.

    $ touch a.txt b.txt c.txt
    $ pattern=" -name *.sh ! -name *.txt"
    $ echo find $dir $pattern
    find -name *.sh ! -name a.txt b.txt c.txt

And here we see that -name *.txt is expanded to be -name a.txt b.txt
c.txt when -name uses exactly one argument.  The "-name a.txt" is read
and then b.txt and c.txt is extra and not part of -name.

The expansion of the shell is useful for other commands that are not
find to use.  For example they are useful with ls.

    $ echo ls -log *.txt
    ls -log a.txt b.txt c.txt

    $ ls -log *.txt
    -rw-rw-r-- 1 0 Feb  7 20:36 a.txt
    -rw-rw-r-- 1 0 Feb  7 20:36 b.txt
    -rw-rw-r-- 1 0 Feb  7 20:36 c.txt

That is useful for ls and other commands but it is not useful for find
and therefore those file glob characters, and other shell meta
characters, must be quoted to prevent this expension.

> I prefer using find with script in this way: find $dir $pattern.where
> pattern=" -name *.sh ! -name *.txt ... etc."

Quote the arguments.  Since this is in a double quoted string then it
is convenient to use single quotes in the string.

    pattern=" -name '*.sh' ! -name '*.txt' ... etc."

And then the result will be this.  First a reminder of the problem

    $ pattern=" -name *.sh ! -name *.txt"; echo find $dir $pattern
    find -name *.sh ! -name a.txt b.txt c.txt

And then we fix it by adding quotes.

    $ pattern=" -name '*.sh' ! -name '*.txt'"; echo find $dir $pattern
    find -name '*.sh' ! -name '*.txt'

As some background information the "*" is called a file glob character
and the expansion is called globbing (see "man glob") because the "*"
matches a "glob" of file name characters.  A glob being a bunch of
characters.  It's a very casual word idiom for a bunch of characters.
This dates back to the earliest implementation of Unix.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]