From 9eee7f1610c27b19d09f29c84e04b3ffe2a02c92 Mon Sep 17 00:00:00 2001 From: koalaman Date: Wed, 18 Mar 2015 17:26:42 -0700 Subject: [PATCH] Created SC2014 (markdown) --- SC2014.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 SC2014.md diff --git a/SC2014.md b/SC2014.md new file mode 100644 index 0000000..4cbaf27 --- /dev/null +++ b/SC2014.md @@ -0,0 +1,24 @@ +## This will expand once before find runs, not per file found. + +### Problematic code: + + find . -name '*.tar' -exec tar xf {} -C "$(dirname {})" \; + +### Correct code: + + find . -name '*.tar' -exec sh -c 'tar xf "$1" -C "$(dirname "$1")"' _ {} \; + +### Rationale: + +Bash evaluates any command substitutions before the command they feature in is executed. In this case, 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, we can write a tiny script and inline it with `sh -c`. We 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 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 . + +### Exceptions: + +If you don't care (or if you prefer) that it's only expanded once, like when dynamically selecting the executable to be used by all invocations, you can ignore this message. \ No newline at end of file