1

I have written a find command to copy some source files:

find ./lib ./tools -type f -regex '.*\.\(cpp\|c\|h\)$' -exec cp --parents \{\} /tmp/potato \; -print

This works well, but I also want to add the trailing -print optionally, so I do something like this:

function deploy_source_code {
    exec_cmd="find ./lib ./tools -type f -regex '.*\.\(cpp\|c\|h\)$' -exec cp --parents \{\} ${args[destdir]} \;"
    if [ "${args[verbose]}" = "true" ]; then
        exec_cmd="${exec_cmd} -print"
    fi  
    ${exec_cmd}
}

But it happens to fail with error:

find: missing argument to `-exec'

I don't know why it fails and I would appreciate any advice. Thanks in advance!

2

Either use an array and execute it as the arguments to a simple command.

deploy_source_code() {
  exec_cmd=(find ./lib ./tools -type f -regex '.*\.\(cpp\|c\|h\)$' 
    -exec cp --parents {} "${args[destdir]}" \;)
  if [ "${args[verbose]}" = "true" ]; then
    exec_cmd+=(-print)
  fi  
  "${exec_cmd[@]}"
}

Or a string and evaluate it as shell code:

deploy_source_code() {
  exec_cmd="find ./lib ./tools -type f -regex '.*\.\(cpp\|c\|h\)$' \
    -exec cp --parents {} \"\${args[destdir]}\" \;"
  if [ "${args[verbose]}" = "true" ]; then
    exec_cmd+=" -print"
  fi  
  eval "$exec_cmd"
}

Note that in the code above, it's important to make sure the ${args[destdir]} is not expanded at the time of assignment as otherwise its content would be passed to eval to be evaluated as shell code! It can be hard to use eval safely, I'd go with the array approach especially considering you're already using associative arrays.

In your approach, you were using the split+glob operator on your string to make the arguments of a simple command. That didn't work because the last argument to find was \; instead of just ; (and you'd also have problems with the literal quote characters passed to find). You could use the split+glob operator as:

deploy_source_code() {
  exec_cmd="find ./lib ./tools -type f -regex .*\.\(cpp\|c\|h\)$ \
    -exec cp --parents {} ${args[destdir]} ;"
  if [ "${args[verbose]}" = "true" ]; then
    exec_cmd+=" -print"
  fi
  IFS=" " # split on space
  set -f  # disable glob
  $exec_cmd # invoke the split+glob operator
}

But that means it won't work if ${args[destdir]} contains space characters. You could always replace those spaces with a character that is less likely to occur in ${args[destdir]} like : or newline.

  • thanks! I decided to use the eval command – XNor Sep 29 '16 at 13:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.