Pipe Output with Expect Spawn

expect command variable with pipes and special characters not working

Use lindex instead of lrange:

#!/usr/bin/expect -f
set password [lindex $argv 0]
set ipaddr [lindex $argv 1]
set command [lindex $argv 2]
... ...
send -- "$command\r"

And call your script this way:

./myexpectscript password ip "echo hello world"

expect in bash script with special characters 2 parts

In your last line block, try manually adding a \ before each special character that needs to be escaped. That shouldn't be much work.

Also, use == for the equality check, i.e.:

echo -n $pass | while read -n 1 c; do [[ "$c" == [!@#$%^&*().] ]] && echo -n "\\"; echo -n $c; done

What characters should be escaped in Expect, and how?

The way you're declaring your expect script, in a double quoted string in the shell, is the main source of your pain. If you don't rely on interpolating shell variables, you can use a single-quoted here-doc:

setup_multicraft=$(expect <<'END_OF_EXPECT'

set timeout 10
spawn ./setup.sh

expect {Run each Minecraft server under its own user? (Multicraft will create system users): [y]/n}
send "y\r"

# ...
END_OF_EXPECT
)

Much tidier, no?

In Tcl (and expect) command substitution is done in square brackets (equivalent to shell backticks), so if you want to use brackets in a double quoted string, they need to be escaped.

In Tcl, you can use curly braces to prevent variable expansion and command substitution (equivalent to shell single quotes). I've demonstrated that here.

The entire syntax of Tcl is described here. You would benefit from a quick run-through of the Tcl tutorial.

expect output only stdout of the command and nothing else

Capturing the output from sent commands is a bit of a pain in expect.

Here's a more general case that does not rely on the log_user setting, it captures the output with a regular expression:

#!/usr/bin/expect
log_user 0
spawn bash

# set the prompt to a known value
send "PS1='>'\r"
expect -re {>$}

# send a command: we don't know what the output is going to be
send "echo \$RANDOM\r"

# capture the portion of the output that occurs just before the prompt
expect -re "\r\n(.*?)\r\n>$"
puts "output is: $expect_out(1,string)"

send "exit\r"
expect eof

A thought just occurred to me: if the command does not require any interaction, then expect is overkill: just use exec

set output [exec bash -c {echo $RANDOM}]

Tcl / Expect script driven by name pipe blocks/buffers output unexpectedly

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

Sample Image



Related Topics



Leave a reply



Submit