1

I do not generally use Solaris, but today I need to craft a find command to execute an operation on identified files using the shell. I am finding that the {} characters are not getting substituted and cannot find an alternative.

For example:

 bash-3.2# find / -exec sh -c "echo {}" \;

This causes it to print {} for each file instead of the file name.

MelBurslan
  • 6,966
Craig
  • 19

2 Answers2

2

As written in the man page and in the standard, {} must be in a separate argument.

find / -exec sh -c 'echo $1' dummy '{}' \;

works as expected.

Note that the parameter dummy is needed as the shell assigns the first argument after the command argument for sh -c cmd to become $0 and the next parameter becomes $1.

schily
  • 19,173
  • 1
    And this is the link to the said standard: If a utility_name or argument string contains the two characters "{}", but not just the two characters "{}", it is implementation-defined whether find replaces those two characters or uses the string without change. It seems GNU find replaces "{}" characters that are combined with other characters, Solaris find does not. – Andrew Henle Mar 21 '16 at 20:45
  • gfind does not behave orthogonal, gfind . -exec echo "aaa {}" \; works as expected, but gfind . -exec echo "aaa {}" + behaves as if gfind . -exec echo "{}" + was called. – schily Mar 22 '16 at 17:37
  • Well, IEEE 1003.1 doesn't mandate that an implementation's definition must be consistent... – Andrew Henle Mar 22 '16 at 20:27
  • I would clarify, for the inexperienced reader seeking for knowledge, that dummy is required by sh, and not by find, since sh -c takes one mandatory argument, the command string here represented by 'echo $1', and optionally other arguments, where the first, dummy in this case, is available in the command as $0, and the following ones, in this case only '{}' (which is not required to be quoted, as far as I can say), as $1, $2, ... – Enlico Mar 29 '20 at 09:39
2

The only standard way to use find -exec … is to pass {} as a separate argument. The behavior when an argument contains {} is not standardized. It seems that you're used to the GNU behavior where {} is substituted in a substring. The find command on Solaris only substitutes {} when an argument consists only of {}.

The GNU behavior is not particularly useful, and sometimes annoying, because substituting a file name inside an argument is brittle. Unless you have known constraints on the file names, there's no way to know where a file name starts and ends. For example, with GNU find, find / -exec sh -c "echo {}" \; does not print the file names in general. It only prints the file names when they don't contain any shell special character. If you run it in a directory containing a file called ;rm -r ~, say goodbye to your files.

The reliable (and portable) way to call a shell from find -exec is to pass the file names as an argument to the shell.

find … -exec sh -c 'echo "$0"' {} \;

In most cases you can pass arguments in batches and iterate over the arguments in the shell. It's somewhat faster. Note that the very first argument after the shell code is $0 which is not includes in "$@".

find … -exec sh -c 'for x; do echo "$x"; done' _ {} +

See also Why does my shell script choke on whitespace or other special characters?