Rule:
You can't use backticks in a find's exec.
First of all in order to have a working example I created a subdir with a couple of more subdirs in it and the following find statement lists all subdirs named 'dd'.
mkdir -p aa/dd aa/bb/dd aa/cc/dd find aa -name dd aa/bb/dd aa/cc/dd aa/ddA slightly more complex use of find is something like
find aa -name dd -exec dirname {} \;which lists all directories which contain a 'dd' subdir or file
aa aa/bb aa/ccMy naive knowledge of find led me to believe that an equivalent command is
find aa -name dd -exec echo `dirname {}` \;Very wrong. Here is the result:
. . .So obviously '{}' is not supplied to 'dirname' as expected and none of the directory names is printed,
A little googling in comp.unix.shell revealed that I hadn't understood how 'exec' is invoked.
You cannot use any shell-type substitution or redirection in the 'exec' clause since 'exec'
executes the 'exec()' system call.
A hacker's way of doing it is to invoke another shell in the 'exec' clause.
Since the shell does not know about the '{}' parameter it has to be supplied as a positional parameter in a rather tricky way:
find aa -name dd -exec /bin/sh -c 'echo `dirname {}`' \;does not work.
find aa -name dd -exec /bin/sh -c 'echo `dirname $0`' {} \;works as expected but note that '{}' is seen as argument $0.
And with csh instead of sh you'll have to change the arg number to $1:
find aa -name dd -exec /bin/csh -c 'echo `dirname $1`' {} \;Originally this problem came up when I wanted to rename all subdirs of a certain name to something else so
find aa -name dd -exec mv {} `dirname {}`/zz \;would have been nice and short but it doesn't work.
find aa -name dd -exec /bin/sh -c 'mv $0 `dirname $0`/zz' {} \;does it now for me.
Solutions with 'xargs'
Here is an invocation with 'xargs' (note that '-i' provides individual args (as {}) rather than one long arg list):find aa -name dd -exec dirname {} \; | xargs -i mv {}/dd {}/zzThe general problem is that I need a 'dirname' to get the parent dir and I need a 'mv' to rename the dir.
Putting all of this into the 'xargs' section also means that I have to invoke another shell e.g.
find aa -name dd | xargs -i /bin/sh -c 'mv $1 `dirname $1`/zz' {} {}which is of similar complexity than the 'find-only' solution.
Simpler but one more in the pipeline:
find aa -name dd | xargs -i dirname {} | xargs -i mv {}/dd {}/zz
oh man thanks
ReplyDelete