Search code examples
bashescapingsh

Bash: A technique to pass variable contents as such to `bash -c`


Suppose I have a bash variable ext. I would like to do (something like) bash -c "internal=$ext", where internal has the exact same contents as ext

  • bash -c "internal=$ext" will not work if ext has spaces inside. Moreover, code injection is also possible. ext="a b" will try to execute b
  • bash -c "internal='$ext'" will also not work if ext has a (single) quote. For instance, ext="'" will fail
  • bash -c "internal=\"$ext\"" will also not work if ext has a (double) quote. For instance, ext='"' will fail

I'd ideally like a technique that would work every possible string, and the contents of internal and ext to be exactly equal.

One possible solution is an out of band solution, where you can use the environment / some file to store ext and read from there. For instance, export ext ; bash -c "internal=\"\$ext\'" might work...

I'd not like to use this technique. This is because it might work for bash -c, it will fail for things like ssh, etc.

How can one go about this? Is there some existing function / command that can automatically escape the appropriate characters in ext?


Solution

  • Fortunately, there does seem to exist a command that does this automatic escaping for us! It is printf %q.

    Here's how that would work:

    ext=$(cat << "EOF"
    This is a wonky string
    t"'x?/?''$(
    # This is a comment
    > >
    EOF
    )
    
    # Save escaped text to variable
    printf -v escaped_ext %q "$ext"
    
    bash -c "int=$escaped_ext ; echo \"\$int\""