bashshelltclexpect

Does Tcl or Expect have the ability to quote arguments in a way that is parsable by a shell?


Consider the following short Expect program:

#!/usr/bin/expect

puts $::argc
puts $::argv

If I invoke it as follows, it correctly identifies that there are four elements, but the natural tcl representation of the argv array is not directly passable to a shell.

./Test.exp x y z "a b c"
4
x y z {a b c}

Is it possible to force TCL to output the arguments in a shell-friendly way so that I could pass them to another program via send?

Just to be explicit, I know I can directly pass the arguments to spawn or exec.

However, I would like to know if it is feasible to send the arguments to a spawned shell (e.g, bash), which requires correct shell quoting.

This is useful for sending a sequence of commands to bash using arguments passed to the expect script and being able to log the whole pseudo-interactive session.


Solution

  • The lazy way is to use bash itself to do the escaping. That way you don't have to account for every special character and edge case that can come up; the shell already knows about them and can handle them for you.

    #!/usr/bin/env tclsh
    
    # Take a list and return a single string with the elements of that list
    # escaped in a way that bash can split back up into individual elements
    proc quote_args {raw} {
        string map {"\n" " "} [exec bash -c {printf "%q\n" "$@"} bash {*}$raw]
    }
    
    # Demonstrate usage by calling a shell with a single argument including the
    # escaped arguments
    set quoted_argv [quote_args $argv]
    puts "Escaped: $quoted_argv"
    puts [exec bash -c "printf \">%s<\\n\" $quoted_argv"]
    

    Example usage:

    $ ./args.tcl x y z "a b c"
    Escaped: x y z a\ b\ c
    >x<
    >y<
    >z<
    >a b c<
    $ ./args.tcl x y z $'a b\nc' # Works with embedded newlines
    Escaped: x y z $'a b\nc'
    >x<
    >y<
    >z<
    >a b
    c<
    $ ./args.tcl 'a b$c' # And things that look like shell parameters that shouldn't be substituted
    Escaped: a\ b\$c
    >a b$c<