Command substitutions will run *before* the enclosing command instead of within itSH-2014
Problematic code:
find . -name '*.tar' -exec tar xf {} -C "$(dirname {})" \;
Preferred code:
find . -name '*.tar' -exec sh -c 'tar xf "$1" -C "$(dirname "$1")"' _ {} \;
Bash evaluates any command substitutions before the command they feature in is executed.
In the given example, the command is find
. This means that $(dirname {})
will run before find
runs, and not while find
runs.
To run shell code for each file, you can write a tiny script and inline it with sh -c
. Add _
as a dummy argument that becomes $0
, and a filename
argument that becomes $1
in the inlined script:
$ sh -c 'echo "$1 is in $(dirname "$1")"' _ "mydir/myfile"
mydir/myfile is in mydir
This command can be executed by find -exec
, with {}
as the filename argument. It executes a shell which interprets the inlined script once for each file.
Note that the inlined script is single quoted, again to ensure that the expansion does not happen prematurely.
Exception:
If you don't care that it's only expanded once, like when dynamically selecting the executable to be used by all invocations, you can ignore this message.