shell dynamically answer to terminal prompt

2019-08-02 09:23发布

questions.sh

#!/bin/bash

declare -a animals=("dog" "cat")
declare -a num=("1" "2" "3")

for a in "${animals[@]}"
do
    for n in "${num[@]}"
    do
        echo "$n $a ?"
        read REPLY
        echo "Your answer is: $REPLY"
    done
done

responder.sh

#!/usr/bin/expect -f

set timeout -1

spawn ./questions.sh

while true {

    expect {
        "*dog*" { send -- "bark\r" }

        "^((?!dog).)*$" { send -- "mew\r" }
    }


}

expect eof

running: './responder.sh'

expected outcome:

1 dog ?
bark
Your answer is: bark
2 dog ?
bark
Your answer is: bark
3 dog ?
bark
Your answer is: bark
1 cat ?
mew
Your answer is: mew
2 cat ?
mew
Your answer is: mew
3 cat ?
mew
Your answer is: mew

actual outcome: hang at 'cat' question and not responding...

1 dog ?
bark
Your answer is: bark
2 dog ?
bark
Your answer is: bark
3 dog ?
bark
Your answer is: bark
1 cat ?

tried and searched multiple ways but still not working. thank you very much.

2条回答
Ridiculous、
2楼-- · 2019-08-02 10:09

The expect program hangs because you match the first "dog", send bark, then you expect eof with an infinite timeout. Of course you don't have "eof" because the shell script is waiting for input.

You need to use the exp_continue command for your loops, not while:

#!/usr/bin/expect -f
set timeout -1
spawn ./questions.sh
expect {
    -re {dog \?\r\n$}        { send -- "bark\r"; exp_continue }
    -re {(?!dog)\S+ \?\r\n$}  { send -- "mew\r";  exp_continue }
    eof
}

I made the patterns much more specific: either "dog" or "not dog" followed by a space, question mark and end-of-line characters.

The exp_continue commands will keep the code looping within the expect command until "eof" is encountered.


We can make the pattern a little DRYer:

expect {
    -re {(\S+) \?\r\n$} { 
        if {$expect_out(1,string) eq "dog"} then {send "bark\r"} else {send "mew\r"} 
        exp_continue 
    }
    eof
}
查看更多
劫难
3楼-- · 2019-08-02 10:25

(This is not a direct answer to your question. Just FYI.)

You can write Expect-like scripts with shell code only. For your example:

The question.sh:

[STEP 109] # cat question.sh
declare -a animals=("dog" "cat")
declare -a num=("1" "2" "3")

for a in "${animals[@]}"
do
    for n in "${num[@]}"
    do
        echo "$n $a ?"
        read REPLY
        echo "Your answer is: $REPLY"
    done
done

The responder.sh:

[STEP 110] # cat responder.sh
export SEXPECT_SOCKFILE=/tmp/qa.sock
sexpect spawn bash question.sh

while true; do
    sexpect expect -cstring -re '(dog|cat) [?][\r\n]+'
    ret=$?
    if [[ $ret == 0 ]]; then
        anim=$(sexpect expect_out -index 1)
        if [[ $anim == dog ]]; then
            sexpect send -cstring 'bark\r'
        else
            sexpect send -cstring 'meow\r'
        fi
    else
        if ! sexpect chkerr -errno $ret -is eof; then
            sexpect close
        fi
        sexpect wait
        break
    fi
done

Run it:

[STEP 111] # bash responder.sh
1 dog ?
bark
Your answer is: bark
2 dog ?
bark
Your answer is: bark
3 dog ?
bark
Your answer is: bark
1 cat ?
meow
Your answer is: meow
2 cat ?
meow
Your answer is: meow
3 cat ?
meow
Your answer is: meow
[STEP 112] #
查看更多
登录 后发表回答