Background spawned process in Expect
Asked Answered
H

2

15

I'm using expect to start an application on my server:

#!/usr/bin/expect

set timeout -1

spawn "bin/start-all.sh"
expect {
    -re "Found MongoDB in" { send "y\r"; exp_continue }
    -re "Found Hadoop in" { send "y\r"; exp_continue }
    -re "Going to start Hadoop" { interact }
}

I can access the application on my server in the few seconds while the script is running, but as soon as it ends the application becomes unavailable.

I've run expect in debug mode and get the following output towards the end:

expect: does "vendors area. Do you want to start it? [y/n] y\r\n" (spawn_id exp6) match regular expression "Found MongoDB in"? Gate "Found MongoDB in"? gate=no
"Found Hadoop in "? Gate "Found Hadoop in "? gate=no
"Going to start Hadoop"? Gate "Going to start Hadoop"? gate=no
Going to start Hadoop...

expect: does "vendors area. Do you want to start it? [y/n] y\r\nGoing to start Hadoop...\r\n" (spawn_id exp6) match regular expression "Found MongoDB in"? Gate "Found MongoDB in"? gate=no
"Found Hadoop in "? Gate "Found Hadoop in "? gate=no
"Going to start Hadoop"? Gate "Going to start Hadoop"? gate=yes re=yes
expect: set expect_out(0,string) "Going to start Hadoop"
expect: set expect_out(spawn_id) "exp6"
expect: set expect_out(buffer) "vendors area. Do you want to start it? [y/n] y\r\nGoing to start Hadoop"
tty_raw_noecho: was raw = 0  echo = 1
interact: received eof from spawn_id exp6
tty_set: raw = 0, echo = 1
tty_set: raw = 5, echo = 0

I've tried using exit 0, interact, exp_continue, disconnect, sleep 10 under the last pattern, as well as expecting eof but nothing seems to be working. I've also tried running expect start-all.exp & but that doesn't work either.

When I run bin/start-all.sh manually, the script starts the necessary processes and then exits. However with expect those processes seem to get killed. How would I fix this issue?

Hibernaculum answered 29/7, 2013 at 4:43 Comment(5)
There's a lot of detail, but it isn't clear exactly what you're trying to achieve.Hideout
It's a long shot, but have you tried expect eof at the end?Edgeways
I'm afraid I'm not familiar with this start-all.sh script. It's possible that it's doing something strange when run in Expect's PTY resulting in it terminating prematurely.Utilitarian
Any solution to this? I have the same issue: trying to interact with spawned process, and when it reaches a certain point, background it and exit. I also tried expect_background and exit 0 in the expect script but none get the parent expect script to end/exit. Only an interrupt (CTRL-C) ends it but I'm concerned it will also interrupt the spawned process (which I don't want to happen).Disremember
@Lucas, looks like the answer below already helped several people. Does it solve your problem? If yes, can you accept it?Disremember
D
14

I had the same problem and figured this out.

When expect exits, it sends a SIGHUP (hangup signal) to the spawned subprocess. By default, this SIGHUP causes termination of the spawned process.

If you want the underlying process not to die from SIGHUP you have two easy options. Both work well:

1) Ask expect to make the underlying process ignore SIGHUP in the spawn line like this:

#!/usr/bin/expect -f
...
spawn -ignore HUP command args...
...
expect_background

2) Do it yourself - ignore SIGHUP in the underlying process itself:

Here's working script demonstrating method 2:

#!/usr/bin/expect -f
#
# start a process and background it after it reaches a certain stage
#
spawn perl -e "\$SIG{HUP} = 'IGNORE'; for (\$a='A';; \$a++) {print qq/value is \$a\\n/; sleep 1;}"

set timeout 600

# Detailed log so we can debug (uncomment to enable)
# exp_internal -f /tmp/expect.log 0

# wait till the subprocess gets to "G"
expect -ex "value is G"

send_user "\n>>> expect: got G\n"

# when we get to G, background the process
expect_background

send_user ">>> spawned process backgrounding successful\n"
exit 0

Here's a running example:

$ ./expect-bg
spawn perl -e $SIG{HUP} = 'IGNORE'; for ($a='A';; $a++) {print qq/value is $a\n/; sleep 1;}
value is A
value is B
value is C
value is D
value is E
value is F
value is G

>>> expect: got G
>>> spawned process backgrounding successful

And as expected in ps output, the perl process is backgrounded and alive.

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
hankm     6700  0.0  0.0  17696  2984 ?        Ss   18:49   0:00 perl -e $SIG{HUP} = 'IGNORE'; for ($a='A';; $a++) {print qq/value is $a\n/; sleep 1;}
Disremember answered 13/9, 2015 at 2:4 Comment(0)
U
0

One thing I've found to work in almost all situations where programs run strangely in Expect is to spawn a shell in a screen instance and run the program from there.

spawn screen bash
send "bin/start-all.sh\r"

Try doing that and seeing if it resolves your premature eof issue.

Utilitarian answered 29/8, 2013 at 19:24 Comment(2)
With this solution you could end up having a lot of screen sessions running after executing the expect script multiple times.Acclivity
@Acclivity Not necessarily. According to the original question, when running the script manually it happily exits at the end of the script without killing the started child processes. If running the same script in a Screen session behaves like running the script manually, you should be able to kill the Screen session at the end without also killing those processes. Those Screen sessions do not need to be long running.Utilitarian

© 2022 - 2024 — McMap. All rights reserved.