Search code examples
bashtclexpect

for loop expect results differents between interactive and script


At this link I've read the for loop management for except https://tcl.tk/man/tcl8.6/TclCmd/for.htm

If you call expect and paste the code in in you get the correct behavoir :

@gigi:~$ expect
expect1.1> for {set x 0} {$x<10} {incr x} {
    puts "x is $x"
}+> +> 
x is 0
x is 1
x is 2
x is 3
x is 4
x is 5
x is 6
x is 7
x is 8
x is 9
expect1.2> 

Instead if you pass it from bash script it goes to error:

^@gigi:~expect -d << EOD
> for {set x 0} {$x<10} {incr x} {
>     puts "x is $x"
> }
> EOD
expect version 5.45.4
argv[0] = expect  argv[1] = -d  
set argc 0
set argv0 "expect"
set argv ""
executing commands from command file
missing operand at _@_
in expression "_@_<10"
    (parsing expression "<10")
    invoked from within
"for {set x 0} {<10} {incr x} {
    puts "x is "

Why this example work different?


Solution

  • The problem you've got is that bash has expanded the variables in the here-doc that you put the script in before passing it to expect. From the bash manpage:

    The format of here-documents is:

    [n]<<[-]word
            here-document
    delimiter

    No parameter and variable expansion, command substitution, arithmetic expansion, or pathname expansion is performed on word. If any part of word is quoted, the delimiter is the result of quote removal on word, and the lines in the here-document are not expanded. If word is unquoted, all lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion, the character sequence \<newline> is ignored, and \ must be used to quote the characters \, $, and `.

    This means that one workaround is to use expect -d << 'EOD' instead of expect -d << EOD.

    expect -d << 'EOD'
        for {set x 0} {$x<10} {incr x} {
            puts "x is $x"
        }
    EOD
    

    Another (inferior!) is to backslash-quote the $ characters; it's inferior because real expect scripts often contain their own backslashes and things can get very ugly and complicated. However, the best is to do this:

    Put the expect script in its own file, say myfile.exp, and call it like this: expect -d -f myfile.exp. Trying to put Tcl code inside Bash scripts like that is asking for trouble.

    Note that this does mean that passing variables from bash is a little more awkward. But you gain by the vastly increased sanity of coding.