3

Consider this interactive script:

#!/usr/bin/env bash
set -eu
while true; do
    read -p '> '
    if [ "$REPLY" == quit ]; then
        break
    fi
    echo "'$REPLY'"
done

Now I want expect to interact with it:

#!/usr/bin/env bash
set -eu
echo '
    spawn ./1.sh
    expect >
    send cmd1\n
    expect {\n> }
    send quit\n
' | expect -d

But when I run it, it says:

...
expect: does " cmd1\r\n'cmd1'\r\n> " (spawn_id exp6) match glob pattern "\n> "? no
expect: timed out
send: sending "quit\n" to { exp6 }

Why doesn't it match? How can I detect appearing of a new prompt (finishing a command)?

2

In the expect tcl language, there is a difference between strings quoted with "" and {}. You can see this in the 2 examples:

$ expect -c 'puts "a\nb"'
a
b
~ $ expect -c 'puts {a\nb}'
a\nb

Your glob pattern {\n> } consists of 4 characters to match for, but the \n is not specially interpreted as an escape. If you use pattern "\n> " your match should work. Or you can use flag -re instead of the default glob pattern, and the two characters will be interpreted by the regexp code as an escape, -re {\n> }.

  • 1
    The thing is with double quotes it ignores all the whitespaces. I wonder if it's expect that receives stripped string, or it's the way it uses the string that results in whitespaces being stripped. Anyway, expect -ex "\n> " seems to do the trick. – x-yuri Sep 4 '17 at 7:04
  • You are right. This seems to be an undocumented "feature". The double quoted string is not stripped of whitespace if you precede it expicitly with the (default) option -gl. It also seems to stay intact if you have a globbing character in it (eg "?\n> "). The underlying tcl function string match does not act this way. – meuh Sep 4 '17 at 8:11
  • I seem to have found the reason: "If the arguments to the entire expect statement require more than one line, all the arguments may be "braced" into one so as to avoid terminating each line with a backslash." And: "The commands (the expect variants and interact) use a heuristic to decide if the list is actually one argument or many. The heuristic can fail only in the case when the list actually does represent a single argument which has multiple embedded \n's with non-whitespace characters between them... – x-yuri Sep 13 '17 at 21:54
  • ...This seems sufficiently improbable, however the argument "-nobrace" can be used to force a single argument to be handled as a single argument. This could conceivably be used with machine-generated Expect code." And some links to the source. – x-yuri Sep 13 '17 at 21:57
  • Excellent investigation. I think you should post this as a 2nd, better, answer, as people may not read all the comments. – meuh Sep 14 '17 at 6:11
2

tl;dr Add -ex flag.

The thing here is that expect makes it easier to pass complex arguments to some commands (expect/interact):

The commands that accepted arguments braced into a single list (the expect variants and interact) use a heuristic to decide if the list is actually one argument or many. The heuristic can fail only in the case when the list actually does represent a single argument which has multiple embedded \n's with non-whitespace characters between them. This seems sufficiently improbable, however the argument "-nobrace" can be used to force a single argument to be handled as a single argument. This could conceivably be used with machine-generated Expect code. Similarly, -brace forces a single argument to be handle as multiple patterns/actions.

(from description of expect command) If the arguments to the entire expect statement require more than one line, all the arguments may be "braced" into one so as to avoid terminating each line with a backslash. In this one case, the usual Tcl substitutions will occur despite the braces.

Let me give an example here:

#!/usr/bin/env bash
set -eu
f() {
    echo line 1
    sleep 1
    echo line 2
}
export -f f
echo '
    spawn bash -c f
    expect {
        {line 1} {puts 1}
        {line 2} {puts 2}
    }
    expect \
        {line 1} {puts 1} \
        {line 2} {puts 2}
' | expect

Output:

spawn bash -c f
line 1
1
line 2
2

In the repository I've found, the heuristic in the latest commit (May 2014) is as follows: if one argument is passed and there is a newline before first non-whitespace, the argument is considered to be multiple arguments braced into one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.