Thanks for detailed explanation, 在 2021年2月8日 週一 11:47,Bob Proulx <b...@proulx.com> 寫道:
> 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 > case. > > $ 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. > > Bob >