On 10/7/23 00:11, Keith Thompson wrote:
As of the latest version of findutils (commit e6e2d10a, Mon 2023-10-02),
the man and info documentation both incorrectly state that {} needs
to be escaped.

In the info documentation, section 3.3.1:
It replaces the string '{}' by the current file name being processed
everywhere it occurs in the command.  Both of these constructions need
to be escaped (with a '\') or quoted to protect them from expansion
by the shell.

("Both" refers to ';' and '{}'.)

In the man page, under "-exec command ;":
The string `{}' is replaced by the current file name being processed
everywhere it occurs in the arguments to the command, not just in
arguments where it is alone, as in some versions of find.  Both of
these constructions might need to be escaped (with a `\') or quoted
to protect them from expansion by the shell.

In the man page, under "-exec command {} +":
Only one instance of `{}' is allowed within the command, and it must
appear at the end, immediately before the `+'; it needs to be escaped
(with a `\') or quoted to protect it from interpretation by the shell.

In the man page, under "-execdir command {} +":
As with -exec, the {} should be quoted if find is being invoked from
a shell.

In all the shells I've tried, {} does *not* need to be escaped.
For example, in bash { is treated as a keyword, and is treated
specially only in a context where it's expected.  In other contexts,
it's just an ordinary string.

I've confirmed that `/bin/echo {}` prints `{}` with no diagnostic in
csh, tcsh, bash, ksh, dash, zsh, busybox sh, and fish.

(Escaping it unnecessarily is harmless, so this doesn't cause any
real problems other than encouraging a little extra typing.)

Thanks for the report.

I agree and see 2 different cases which the documentation should adhere
to more consistently:

a) code examples:
Here, '{}' and '+' do not have to be quoted or escaped -> remove it.
Still, ';' always has to be quoted/escaped (which was okay already.

b) mentioning of the strings '{}', ';' and '+' in the descriptions.
There, they always should get some markup (@samp{} in texi, .B in man).

The attached patch attempts to fix this.
Okay to push?

Thanks & have a nice day,
Berny
From abdf169fbbf87b3694213cee709bc21def9fe83e Mon Sep 17 00:00:00 2001
From: Bernhard Voelker <m...@bernhard-voelker.de>
Date: Sat, 14 Oct 2023 20:36:08 +0200
Subject: [PATCH] doc: change escaping/quoting of '{}', ';' and '+' more
 consistently

In shell context, the replacement pattern '{}' and '+' do not have to be
quoted or escaped (tested in csh, tcsh, bash, ksh, dash, zsh, busybox sh,
and fish).  In contrast, the semicolon ';' always has to be escaped/quoted.
Change all code examples to consistently avoid quoting/escaping for '{}'
and '+'.
On the other hand, the mentioning of the strings '{}', ';' and '+' in
describing sentences shall always get some markup in order to stand out
from regular words.

* doc/find.texi: Remove escaping/quoting of '{}' and '+' in examples.
Wrap them in @samp{} in text though.
Document that ';' has always to be escaped or quoted in shell context;
remove the wrong mentioning that '{}' has to be quoted.
* find/find.1: Likewise.

Reported by Keith Thompson in
https://lists.gnu.org/r/bug-findutils/2023-10/msg00002.html
---
 doc/find.texi | 38 +++++++++++++++++++++-----------------
 find/find.1   | 28 ++++++++++++++--------------
 2 files changed, 35 insertions(+), 31 deletions(-)

diff --git a/doc/find.texi b/doc/find.texi
index 6d3bf832..87cfcffc 100644
--- a/doc/find.texi
+++ b/doc/find.texi
@@ -1534,7 +1534,7 @@ This is different to @samp{-prune} because @samp{-prune} only applies
 to the contents of pruned directories, while @samp{-quit} simply makes
 @code{find} stop immediately.  No child processes will be left
 running.  Any command lines which have been built by @samp{-exec
-... \+} or @samp{-execdir ... \+} are invoked before the program is
+... +} or @samp{-execdir ... +} are invoked before the program is
 exited.  After @samp{-quit} is executed, no more files specified on
 the command line will be processed.  For example, @samp{find /tmp/foo
 /tmp/bar -print -quit} will print only @samp{/tmp/foo}.  One common
@@ -2318,17 +2318,18 @@ Execute @var{command}; true if @var{command} returns zero.  @code{find}
 takes all arguments after @samp{-execdir} to be part of the command until
 an argument consisting of @samp{;} is reached.  It replaces the string
 @samp{@{@}} by the current file name being processed everywhere it
-occurs in the command.  Both of these constructions need to be escaped
-(with a @samp{\}) or quoted to protect them from expansion by the
-shell.  The command is executed in the directory which @code{find}
-was searching at the time the action was executed (that is, @{@} will
+occurs in the command.
+In shell context, the latter @samp{;} construction usually needs to be escaped
+(with a @samp{\}) or quoted to protect it from expansion by the shell.
+The command is executed in the directory which @code{find}
+was searching at the time the action was executed (that is, @samp{@{@}} will
 expand to a file in the local directory).
 
 For example, to compare each C header file in or below the current
 directory with the file @file{/tmp/master}:
 
 @example
-find . -name '*.h' -execdir diff -u '@{@}' /tmp/master ';'
+find . -name '*.h' -execdir diff -u @{@} /tmp/master ';'
 @end example
 @end deffn
 
@@ -2371,6 +2372,9 @@ the matched file.
 While some implementations of @code{find} replace the @samp{@{@}} only
 where it appears on its own in an argument, GNU @code{find} replaces
 @samp{@{@}} wherever it appears.
+
+In shell context, the latter @samp{;} construction usually needs to be escaped
+(with a @samp{\}) or quoted to protect it from expansion by the shell.
 @end deffn
 
 
@@ -2880,7 +2884,7 @@ find bills -type f | xargs -I XX sort -o XX.sorted XX
 The equivalent command using @samp{find -execdir} is:
 
 @example
-find bills -type f -execdir sort -o '@{@}.sorted' '@{@}' ';'
+find bills -type f -execdir sort -o @{@}.sorted @{@} ';'
 @end example
 
 When you use the @samp{-I} option, each line read from the input is
@@ -2892,7 +2896,7 @@ uses, and you can also use an extra invocation of xargs to ensure that
 very long lines do not occur.  For example:
 
 @example
-somecommand | xargs -s 50000 echo | xargs -I '@{@}' -s 100000 rm '@{@}'
+somecommand | xargs -s 50000 echo | xargs -I @{@} -s 100000 rm @{@}
 @end example
 
 Here, the first invocation of @code{xargs} has no input line length
@@ -3066,7 +3070,7 @@ scripts) in the file @file{sbins} and the unstripped ones in
 
 @example
 find /usr/local -type f -perm /a=x \
-  \( -execdir unstripped '@{@}' \; -fprint ubins -o -fprint sbins \)
+  \( -execdir unstripped @{@} \; -fprint ubins -o -fprint sbins \)
 @end example
 
 
@@ -4478,7 +4482,7 @@ if the file name contains a character with the high bit set; the shell
 may strip it off.  A more reliable way is:
 
 @example
-find . -maxdepth 1 @var{tests} -okdir rm '@{@}' \;
+find . -maxdepth 1 @var{tests} -okdir rm @{@} \;
 @end example
 
 @noindent
@@ -4497,7 +4501,7 @@ you have found that its inode number is 12345.  This command prompts
 you for whether to remove it:
 
 @example
-find . -maxdepth 1 -inum 12345 -okdir rm -f '@{@}' \;
+find . -maxdepth 1 -inum 12345 -okdir rm -f @{@} \;
 @end example
 
 If you don't want to be asked, perhaps because the file name may
@@ -4508,7 +4512,7 @@ If you want to rename the file instead, you can use @code{mv} instead
 of @code{rm}:
 
 @example
-find . -maxdepth 1 -inum 12345 -okdir mv '@{@}' @var{new-file-name} \;
+find . -maxdepth 1 -inum 12345 -okdir mv @{@} @var{new-file-name} \;
 @end example
 
 @node Fixing Permissions
@@ -4719,7 +4723,7 @@ Unix introduced a slight variation, which involves terminating the
 command with @samp{+} instead:
 
 @smallexample
-find /var/tmp/stuff -mtime +90 -exec /bin/rm @{@} \+
+find /var/tmp/stuff -mtime +90 -exec /bin/rm @{@} +
 @end smallexample
 
 The above use of @samp{-exec} causes @code{find} to build up a long
@@ -4747,11 +4751,11 @@ normally cannot.
 The problem occurs because the @samp{-exec} action is defined by the
 POSIX standard to invoke its command with the same working directory
 as @code{find} had when it was started.  This means that the arguments
-which replace the @{@} include a relative path from @code{find}'s
+which replace the @samp{@{@}} include a relative path from @code{find}'s
 starting point down the file that needs to be deleted.  For example,
 
 @smallexample
-find /var/tmp/stuff -mtime +90 -exec /bin/rm @{@} \+
+find /var/tmp/stuff -mtime +90 -exec /bin/rm @{@} +
 @end smallexample
 
 might actually issue the command:
@@ -4763,7 +4767,7 @@ might actually issue the command:
 Notice the file @file{/var/tmp/stuff/passwd}.  Likewise, the command:
 
 @smallexample
-cd /var/tmp && find stuff -mtime +90 -exec /bin/rm @{@} \+
+cd /var/tmp && find stuff -mtime +90 -exec /bin/rm @{@} +
 @end smallexample
 
 might actually issue the command:
@@ -4787,7 +4791,7 @@ is the @samp{-execdir} action, which was introduced by the BSD family
 of operating systems.   The command,
 
 @smallexample
-find /var/tmp/stuff -mtime +90 -execdir /bin/rm @{@} \+
+find /var/tmp/stuff -mtime +90 -execdir /bin/rm @{@} +
 @end smallexample
 
 might delete a set of files by performing these actions:
diff --git a/find/find.1 b/find/find.1
index 08ae9d55..1c7311b7 100644
--- a/find/find.1
+++ b/find/find.1
@@ -1241,8 +1241,9 @@ file name being processed everywhere it occurs in the arguments to the
 command, not just in arguments where it is alone, as in some versions
 of
 .BR find .
-Both of these constructions might need to be escaped (with a `\e') or
-quoted to protect them from expansion by the shell.  See the
+In shell context, the latter `;' construction usually needs to be escaped
+(with a `\e') or quoted to protect it from expansion by the shell.
+See the
 .B EXAMPLES
 section for examples of the use of the
 .B \-exec
@@ -1265,10 +1266,9 @@ selected file name at the end; the total number of invocations of the
 command will be much less than the number of matched files.  The
 command line is built in much the same way that
 .B xargs
-builds its command lines.  Only one instance of `{}' is allowed within
-the command, and it must appear at the end, immediately before the `+';
-it needs to be escaped (with a `\e') or quoted to protect it from
-interpretation by the shell.
+builds its command lines.
+Only one instance of `{}' is allowed within the command, and it must appear at
+the end, immediately before the `+'.
 The command is executed in the starting directory.  If any invocation
 with the `+' form returns a non-zero value as exit status, then
 .B find
@@ -1292,9 +1292,10 @@ but the specified command is run from the subdirectory
 containing the matched file, which is not normally the directory in
 which you started
 .BR find .
-As with \-exec, the {} should be quoted if find is being invoked from
-a shell.
-This a much more secure method for invoking commands, as it avoids
+As with \-exec, the `;' should be quoted if
+.B find
+is being invoked from a shell.
+This is a much more secure method for invoking commands, as it avoids
 race conditions during resolution of the paths to the matched files.
 As with the
 .B \-exec
@@ -2282,14 +2283,13 @@ on every file in or below the current directory.
 .nf
 \&
 .in +4m
-.B $ find . \-type f \-exec file \(aq{}\(aq \e;
+.B $ find . \-type f \-exec file {} \e;
 .in
 \&
 .fi
-Notice that the braces are enclosed in single quote marks to protect them
-from interpretation as shell script punctuation.  The semicolon is
-similarly protected by the use of a backslash, though single quotes
-could have been used in that case also.
+Notice that the semicolon is protected by the use of a backslash to protect it
+from interpretation as shell script punctuation; single quotes could have been
+used in that case also.
 .PP
 In many cases, one might prefer the
 .B `\-exec\ \&...\&\ +`
-- 
2.42.0

Reply via email to