ivan_pozdeev
ivan_pozdeev

Reputation: 35998

Escape an array of parameters for use in `eval` for `flock`

I need to insert an array of arguments into an eval string executed via bash -c.

In this particular case, it's not possible to pass them separately as proper arguments (it's for an flock invocation which doesn't accept arguments for a -c script).

Smth like this:

flock f.lock -c 'do_stuff '"${ARGS[@]}"'; do_other_stuff'

How do I quote them properly so that they are correctly parsed into a sequence of arguments, even if they contain spaces or Bash special syntax?

Upvotes: 3

Views: 315

Answers (2)

ivan_pozdeev
ivan_pozdeev

Reputation: 35998

Use Parameter transformation (new in Bash 4.4) with the Q (quote) operator which is specifically designed for this:

Q      The expansion is a string that is the value of parameter quoted
       in a format that can be reused as input.
$ ARGS=("foo bar" 'baz\n'\' '$xyzzy')

$ echo 'do_stuff '"${ARGS[*]@Q}"'; do_other_stuff'
do_stuff 'foo bar' 'baz\n'\''' '$xyzzy'; do_other_stuff

Note the use of * instead of the usual @ since the code needs to be a single argument. Using @ leads to erroneous behavior in some cases:

$ bash -xc "echo ${ARGS[@]@Q}; do_other_stuff"
+ echo 'foo bar'
foo bar

$ bash -xc "echo ${ARGS[*]@Q}; do_other_stuff"
+ echo 'foo bar' 'baz\n'\''' '$xyzzy'
foo bar baz\n' $xyzzy
+ do_other_stuff

You can even use this syntax for $*:

'do_stuff '"${*@Q}"' other args; do other stuff'

Upvotes: 1

KamilCuk
KamilCuk

Reputation: 140900

Don't! It is going to be error prone and give you pain. Just:

{
     flock 9
     do_stuff "${ARGS[@]}"
     do_other_stuff
} 9>f.lock

Anyway, split the operation into two:

  • first, safely transfer the environment to the subshell
  • then execute what you want to execute in a normal way

And it's just:

bash -c "$(declare -p ARGS)"'; do_stuff "${ARGS[@]}"; do_other_stuff'

Upvotes: 4

Related Questions