Question

I am trying to have tcl escape a string under MSys/MinGW (from mingw.org). The version of tcl comes from mingw.org (via mingw-get, as of Dec 2013).

In bash, I can do it for strings (simplified examples):

printf %q 'a \b $2'
printf '%q' "a \ b \' \" \$2"

which return:

a\ \\b\ \$2
a\ \\b\ \\\'\ \"\ \$2

respectively.

However, I can't make the second example work inside tclsh or tcl, if the string is stored in a variable or read from file. The things I tried are:

% set s {a \b ' "  $2}
% printf %q $s
/usr/bin/printf: %q: invalid conversion specification
child process exited abnormally

(I guess this is caused by the msys printf.exe not playing nicely with tclsh).

%exec bash -c "printf %q $s"
/usr/bin/bash: -c: line 0: unexpected EOF while looking for matching `''
/usr/bin/bash: -c: line 1: syntax error: unexpected end of file

(here, I don't even know how to properly quote $s, considering the single and double quotes inside $s). Any idea how to make this work?

Thanks

-- Update --

The answer @Donal Fellows works fine for the original string. I have:

% exec bash -c {printf %q "$0"} $s
a\ \\b\ \'\ \"\ \ \$2

But it didn't work for the test string I first encountered. The answer does not seem to work if there is an end-line in the string.

% set s1 {a \b ' "
 $2 }
% exec bash -c {printf %q "$0"} $s1 

I was expecting something like:

a\ \\b\ \'\ \"\ \n\ \$2

Strangely, the above exec returns:

$'a \\b \' "  \n $2 '

which seems to be messed up. There is an unwanted leading $', and the double quote " is not escaped properly. And blanks are not escaped to \ as before. Any ideas?

-- Update 2 --

As @Donal Fellows explained, the above string $'a \\b \' " \n $2 ' is a bash special form for quotation. One final question I have is on how to escape/make use of this special form in tcl. For example:

set s { ls
  echo '"' }
set ss [exec bash -c {printf %q "$0"} $s]

returns:

$' ls\n  echo \'"\' '

I could use this special form string in bash (msys/mingw):

eval $' ls\n  echo \'"\' '

which executes two lines of commands (ls and echo '"'), and returns, e.g.:

a.txt
"

However, I couldn't figure out how to make this work in tclsh.

% exec bash -c {eval $ss}

returns nothing at all. I tried a few other combinations. None worked. Any pointers on how to escape/unescape this one?

Thanks

Was it helpful?

Solution

printf is both a command in msys and a builtin in bash. Only the bash builtin supports %q. This means that invoking it from Tcl is rather more tricky than normal, as you've got to negotiate bash's quoting as well. This requires this rather awkward construction:

exec bash -c {printf %q "$0"} $s

The part inside the braces is shell syntax, but the rest is Tcl syntax; by doing it like this, it will work with (and so correctly shell-quote) any input. Note that Tcl's braces work just like bourne shell single quotes, except they're nestable.


If you then want to use the special form in bash, you then substitute it into the bash script in Tcl:

set quoted [exec bash -c {printf %q "$0"} $s]
exec bash -c "eval $quoted"

(This is OK. The printf %q does exactly the quoting to make this work; that's its purpose.)

If you've got a complicated script that will be using the quoted fragment where you don't want to do this, you end up with passing it in as an argument again and using another eval where necessary:

exec bash -c {
    # You can get *really* complicated in here!
    eval eval "$0"
} $quoted

But if this is going on, it might be time to consider whether your approach is the right one…

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top