Why doesn't
find . -type f -exec echo $(file={}; echo ${file:0:5}) \;
give the first five characters of the file, while this works:
find . -type f -exec bash -c 'echo ${1:0:5}' funcname {} \;
Background:
I'm trying to batch convert a tree full of images to thumbnails, and I want to rename them on the fly by adding '_thumb' at the end of the filename but before the extension. This renaming process is easy for one file:
file='I am a picture.jpg'
mv \"$file\" \"${file%\.*}_thumb.${file##*\.}\"
(the second line expands to mv "I am a picture.jpg" "I am a picture_thumb.jpg"
)
but when I try to encapsulate this command in the -exec
parameter of find(1)
I cannot manipulate the filename given by find
(examples simplified):
find . -type f -exec ${{}:0:5}) \;
gives
bash: ${{}:0:5}: bad substitution
Using a subshell I get a bit further:
find . -type f -exec echo $(file={}; echo ${file:0:5}) \;
this does echo the filename, but does not execute the string manipulation for some reason.
I finally found the solution in this SO post:
find . -type f -exec bash -c 'echo ${1:0:5}' funcname {} \;
but I don't understand why this would work when the $(...)
construct does not.
Best Answer
exec
does what it says: usesexec()
. It doesn't involve a shell unless it's told to (-exec bash ...
), and if it did you would still need single quotes to keep your interactive shell from interpreting the variable interpolations. (The shell is not magic and does not know that you intended those to be interpolated by something else.)For example, when you use the following command:
your shell first performs process substitution by executing
$(file={}; echo ${file:0:5})
, which simply outputs{}
, then executes the final command: