2

I am trying to write an expect script that reacts to input from reading a pipe. Consider this example in file "contoller.sh":

#!/usr/bin/env expect

spawn bash --noprofile --norc

set timeout 3
set success 0
send "PS1='Prompt: '\r"
expect {
  "Prompt: " { set success 1 }
}
if { $success != 1 } { exit 1 }

proc do { cmd } {
  puts "Got command: $cmd"
  set success 0
  set timeout 3
  send "$cmd\r"
  expect {
    "Prompt: " { set success 1 }
  }
  if { $success != 1 } { puts "oops" }
}

set cpipe [open "$::env(CMDPIPE)" r]
fconfigure $cpipe -blocking 0
proc read_command {} {
  global cpipe
  if {[gets $cpipe cmd] < 0} {
    close $cpipe
    set cpipe [open "$::env(CMDPIPE)" r]
    fconfigure $cpipe -blocking 0
    fileevent $cpipe readable read_command
  } else {
    if { $cmd == "exit" } {
      exp_close
      exp_wait
      exit 0
    } elseif { $cmd == "ls" } {
      do ls
    } elseif { $cmd == "pwd" } {
      do pwd
    }
  }
}

fileevent $cpipe readable read_command
vwait forever;

Suppose you do:

export CMDPIPE=~/.cmdpipe
mkfifo $CMDPIPE
./controller.sh

Now, from another terminal try:

export CMDPIPE=~/.cmdpipe
echo ls >> ${CMDPIPE}
echo pwd >> ${CMDPIPE}

In the first terminal the "Got command: ls/pwd" lines are printed immediately as soon as you press enter on each echo command, but there is no output from the spawned bash shell (no file listing and current directory). Now, try it once more:

echo ls >> ${CMDPIPE}

Suddenly output from the first two commands appears but 3rd command (second ls) is not visible. Keep going and you will notice that there is a "lag" in displayed output which seems to be "buffered" and then dumped at once later.

Why is this happening and how can I fix it?

0

1 Answer 1

3

According to fifo(7):

Normally, opening the FIFO blocks until the other end is opened also.

So, in the proc read_command, it's blocking on set cpipe [open "$::env(CMDPIPE)" r] and does not get the chance to display the spawned process's output until you echo ... >> ${CMDPIPE} again.

To work it around, you can open the FIFO (named pipe) in non-blocking mode:

set cpipe [open "$::env(CMDPIPE)" {RDONLY NONBLOCK} ]

This is also mentioned in fifo(7):

A process can open a FIFO in nonblocking mode. In this case, opening for read-only will succeed even if no one has opened on the write side yet ...

The following is the simplified version of your code and it works fine for me (tested on Debian 9.6).

spawn bash --norc
set timeout -1

expect -re {bash-[.0-9]+[#$] $}
send "PS1='P''rompt: '\r"
#         ^^^^
expect "Prompt: "

proc do { cmd } {
    send "$cmd\r"
    if { $cmd == "exit" } {
        expect eof
        exit
    } else {
        expect "Prompt: "
    }
}

proc read_command {} {
    global cpipe
    if {[gets $cpipe cmd] < 0} {
        close $cpipe
        set cpipe [open cpipe {RDONLY NONBLOCK} ]
        fileevent $cpipe readable read_command
    } else {
        do $cmd
    }
}

set cpipe [open cpipe {RDONLY NONBLOCK} ]
fileevent $cpipe readable read_command
vwait forever

enter image description here

Sign up to request clarification or add additional context in comments.

9 Comments

I tried your suggestion but unfortunately behavior is the same. Please note that I was setting non-blocking mode already with "fconfigure $cpipe -blocking 0". Still even with your way of opening the pipe nothing changes.
I should add that if you copy-paste the example into a couple of bash terminals you will see that the [ puts "Got command: $cmd" ] statement is called immediately after you run echo to send a command. So the read-readiness seems fine. It's just the output of the actual command missing. This seems to me like it's a buffering problem, or something to do with an event loop in Tcl and/or expect which I am unaware of (don't know much tcl).
i tried it myself and it worked fine for me. it's open which blocks so fconfigure -blocking 0 would not help here.
This is the change that fixes it: send "PS1='P''rompt: '\r"
FYI, I see the same behavior as you on Ubuntu. Updating my Cygwin which was 1.7-based replicated the same on my Windows box. So indeed there were 2 issues and you addressed both.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.