Search code examples
rubystringbashescaping

How can I escape a Ruby string containing newlines (and possibly other special characters) to a valid shell variable value?


I need to write a Ruby function which generates a correct Bash export statement given a variable name and the value it should be assigned to. My current attempt is

def export(variable, string)
  "export #{variable}=#{Shellwords.escape(string)}"
end

However, it doesn't behave quite as I expect. E.g.

[2] pry(main)> export("X", "a\nb")
"export X=a'\n'b"

and then

export X=a'\n'b
echo "$X"

in the shell gives

a\nb

instead of the desired

a
b

If it matters (I don't expect it to, but just in case) the statement will be written into a file which will be sourced later.


Solution

  • You're copy-pasting the auto-echoed string from irb, not the value of said string. The auto-echoing is showing you a Ruby string literal with Ruby escapes left in, so you get:

    irb(main)> Shellwords.shellescape("a\nb")
    => "a'\n'b"
    

    If you printed that string (e.g. with puts) to get its raw value (with the escapes printed as their real characters), e.g.

    irb(main)> puts Shellwords.shellescape("a\nb")
    a'
    'b
    => nil
    

    and copy-pasted that to make:

    export X=a'
    'b
    echo "$X"
    

    it would work just fine.

    Similarly, writing "export X=#{Shellwords.escape(string)}" to a file (via puts or other actual I/O APIs that write the string's value, not its representation) for later use would work just fine, because that would be inserting the string's value, and the file would end up with a raw newline between the single-quotes, not \n.