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,836
  • 2
  • 24
  • 35
Craig
  • 19
  • 1

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
  • 18,806
  • 5
  • 38
  • 60
  • 1
    And this is the link to the [said standard](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html): *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?

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175