Find does not have any escape mechanism
This fact will not allow you to insert {}
inside an existing -exec
and/or -execdir
option. The substitution is made with plain strncpy()
. The exec
/execdir
is performed inside GNU-find (which I'm using as reference) through bc_push_arg
. For the {} +
form we have:
/* "+" terminator, so we can just append our arguments after the
* command and initial arguments.
*/
execp->replace_vec = NULL;
execp->ctl.replace_pat = NULL;
execp->ctl.rplen = 0;
execp->ctl.lines_per_exec = 0; /* no limit */
execp->ctl.args_per_exec = 0; /* no limit */
/* remember how many arguments there are */
execp->ctl.initial_argc = (end-start) - 1;
/* execp->state = xmalloc(sizeof struct buildcmd_state); */
bc_init_state (&execp->ctl, &execp->state, execp);
/* Gather the initial arguments. Skip the {}. */
for (i=start; i<end-1; ++i)
{
bc_push_arg (&execp->ctl, &execp->state,
argv[i], strlen (argv[i])+1,
NULL, 0,
1);
}
It just appends everything at the end, since you can't have more than one instance of {}
in the {} +
form. And for the {} ;
we have:
/* Semicolon terminator - more than one {} is supported, so we
* have to do brace-replacement.
*/
execp->num_args = end - start;
execp->ctl.replace_pat = "{}";
execp->ctl.rplen = strlen (execp->ctl.replace_pat);
execp->ctl.lines_per_exec = 0; /* no limit */
execp->ctl.args_per_exec = 0; /* no limit */
execp->replace_vec = xmalloc (sizeof(char*)*execp->num_args);
/* execp->state = xmalloc(sizeof(*(execp->state))); */
bc_init_state (&execp->ctl, &execp->state, execp);
/* Remember the (pre-replacement) arguments for later. */
for (i=0; i<execp->num_args; ++i)
{
execp->replace_vec[i] = argv[i+start];
}
So we have execp->ctl.replace_pat = "{}";
. This is all in parser.c
The above is substituted as:
size_t len; /* Length in ARG before `replace_pat'. */
char *s = mbsstr (arg, ctl->replace_pat);
if (s)
{
len = s - arg;
}
else
{
len = arglen;
}
if (bytes_left <= len)
break;
else
bytes_left -= len;
strncpy (p, arg, len);
p += len;
arg += len;
arglen -= len;
In bc_do_insert()
in buildcmd.c
.
So no, there is no way of escaping the {}
. Yet, some versions of find will not substitute {}/foo
but only {}
itself so you may be able to use two different versions of find together with an -exec sh -c 'someCommad {}'
.
Assuming that gfind
is GNU-find and afind
is AIX find you probably can achieve:
afind . -type d -execdir test -f foo.jpg \
-exec sh -c 'gfind . -name "*.m4a" -exec someCommand {} \;' \;
But that would be a horrible hack.
Decent workaround
The problem you are facing is that you are performing globing to get the all files of a type in a directory. In other words, you are first finding a directory and globbing all files of that directory to be part of a single command line.
This is the kind of problem -execdir
is meant to solve. It will run the command in the directory that contain the found file. For example:
$ mkdir -p a/a b/b c/c d e
$ touch a/a/foo.m4a b/b/foo.m4a
$ touch a/a/bar.m4a b/b/bar.m4a c/c/foobar.m4a
$ touch a/a/yay.jpg c/c/yay.jpg
$ find . -type f -name '*.m4a' -execdir test -e yay.jpg \; \
-execdir echo someCommand --arg yay.jpg {} +
someCommand --arg yay.jpg ./foobar.m4a
someCommand --arg yay.jpg ./bar.m4a ./foo.m4a
Moreover, I'm using the {} +
form instead of the {} ;
form for the exec
. This will place all files found (in the directory it is running in) into the same command line.
Note that the command did not run in b/b
because the test -e
prevented it.