How do I redirect stdout from a program continuously into a spawned process using expect? - bash

Need to open telnet, send a few commands and then send stdout from pocketsphinx.
Currently expect will wait until the program is finished and then output everything to the telnet process.
I need pocketsphix to continuously feed the spawned telnet process.
This is what I have so far:
#!/usr/bin/expect -d
set send_human {.1 .3 1 .05 2}
spawn telnet 192.168.1.104 23
expect “*”
send "\x01"; send "2\r"
expect “:”
send -h "hello world\r"
send -h "goodbye world\r"
send -h "Test Test Test\r"
send -- [exec pocketsphinx_continuous -infile speech.wav 2> /dev/null ]\n

You can use expect command interact for connecting together two spawned processes.
By default, interact expects the user to be writing stdin and reading stdout of the Expect process
itself. The -u flag (for "user") makes interact look for the user as the process named by its argument
(which must be a spawned id).
This allows two unrelated processes to be joined together without using an explicit loop. To aid in
debugging, Expect diagnostics always go to stderr (or stdout for certain logging and debugging information).
For the same reason, the interpreter command will read interactively from stdin.
For example
set send_human {.1 .3 1 .05 2}
spawn telnet 192.168.1.104 23
expect “*”
send "\x01"; send "2\r"
expect “:”
send -h "hello world\r"
send -h "goodbye world\r"
send -h "Test Test Test\r"
set sid_telnet $spawn_id
spawn pocketsphinx_continuous -infile speech.wav 2> /dev/null
interact -u $sid_telnet

Related

How to send a SIGTSTP signal to a process spawned by an expect script

I wrote an expect script like this:
#!/usr/bin/expect -f
spawn sql "user=xx dbname=xx"
interact
After I entered the sql client, I can't send the SIGTSTP signal by ctrl + z to make the current process suspend and go to the background.
The terminal will only show:
=> ^Z
What should I do to make ctrl + z achieve the above purpose?
The manual of expect gives the recipe:
During interact, raw mode is used so that all characters may be passed to the current process. If the current process does not catch job control signals, it will stop if sent a stop signal (by default ^Z). To restart it, send a continue signal (such as by "kill -CONT "). If you really want to send a SIGSTOP to such a process (by ^Z), consider spawning csh first and then running your program. On the other hand, if you want to send a SIGSTOP to Expect itself, first call interpreter (perhaps by using an escape character), and then press ^Z.
So, you may be able to do something like:
#!/usr/bin/expect -f
spawn /bin/sh
exp_send "psql hostaddr=xxxx port=xxxx user=xx dbname=xx\r"
interact
For example, let's consider the following interactive shell script named interact.sh:
#!/bin/sh
read -p "First name: " fname
read -p "Last name: " lname
echo "you entered: $fname $lname"
And the following expect script named script.exp to automate the previous one:
#!/usr/bin/expect -f
spawn /bin/sh
exp_send "./interact.sh\r"
interact
We launch the latter:
$ ./script.exp
spawn /bin/sh
./interact.sh
$ ./interact.sh
First name: Stack
Last name: ^Z (we entered CTRL-Z here)
[1]+ Stopped(SIGTSTP) ./interact.sh
sh-4.4$ jobs
[1]+ Stopped(SIGTSTP) ./interact.sh
sh-4.4$ fg
./interact.sh
Overflow
you entered: Stack Overflow
$ exit
exit
$

Using expect on stderr (sshuttle as an example)

There is this program called sshuttle that can connects to a server and create a tunnel.
I wish to create a bash function that sequentially:
opens a tunnel to a remote server (sshuttle -r myhost 0/0),
performs 1 arbitrary commandline,
kill -s TERM <pidOfTheAboveTunnel>.
A basic idea (that works but the 5 seconds delay is a problem) is like sshuttle -r myhost 0/0 & ; sleep 5 ; mycommand ; kill -s TERM $(pgrep sshuttle)
Could expect be used to expect the string "c : Connected to server." that is received from stderr here? My attempts as a newbie were met with nothing but failure, and the man page is quite impressive.
When you use expect to control another program, it connects to that program through a pseudo-terminal (pty), so expect sees the same output from the program as you would on a terminal, in particular there is no distinction between stdout and stderr. Assuming that your mycommand is to be executed on the local machine, you could use something like this as an expect (not bash) script:
#!/usr/bin/expect
spawn sshuttle -r myhost 0/0
expect "Connected to server."
exec mycommand
exec kill [exp_pid]
close
The exec kill may not be needed if sshuttle exits when its stdin is closed, which will happen on the next line.

linux expect in background

I use the following bash script to connect to pbx using telnet:
expect.sh:
#!/usr/bin/expect
spawn telnet [ip] 2300
expect -exact "-"
send "SMDR\r";
expect "Enter Password:"
send "PASSWORD\r";
interact
and created another script to redirect the result to a file:
#!/bin/bash
./expect.sh | tee pbx.log
I'm trying to run expect.sh at boot time so I added it to systemd. When I add it as service in /etc/systemd/system it runs but I can't get the results in the log file as if I run both scripts manually
any idea about how can I run it at boot time?
TIA
If you just want to permanently output everything received after providing your password, simply replace your interactive with expect eof, i.e. wait for end-of file which will happen when the connection is closed by the other end. You will probably also want to change the default timeout of 10 seconds with no data that will stop the command:
set timeout -1
expect eof

Why can't tranfer file into the remote vps with expect?

The expect has been installed, it_a_test is the vps password.
scp /home/wpdatabase_backup.sql root#vps_ip:/tmp
The command can transfer file /home/wpdatabase_backup.sql into my vps_ip:/tmp.
Now i rewrite the process into the following code:
#!/usr/bin/expect -f
spawn scp /home/wpdatabase_backup.sql root#vps_ip:/tmp
expect "password:"
send it_is_a_test\r
Why can't transfer my file into remote vps_ip with expect?
Basically, expect will work with two feasible commands such as send and expect. In this case, if send is used, then it is mandatory to have expect (in most of the cases) afterwards. (while the vice-versa is not required to be mandatory)
This is because without that we will be missing out what is happening in the spawned process as expect will assume that you simply need to send one string value and not expecting anything else from the session, making the script exits and causing the failure.
So, you just have to add one expect to wait for the closure of the scp command which can be performed by waiting for eof (End Of File).
#!/usr/bin/expect -f
spawn scp /home/wpdatabase_backup.sql root#vps_ip:/tmp
expect "password:"
send "it_is_a_test\r"
expect eof; # Will wait till the 'scp' completes.
Note :
The default timeout in expect is 10 seconds. So, if the scp completes, within 10 seconds, then no problem. Suppose, if the operation takes more than that, then expect will timeout and quit, which makes failure in scp transfer. So, you can set increase timeout if you want which can be modified as
set timeout 60; # Timeout is 1 min

ncurses - expect: sleep executes at wrong time

I have some ncurses apps that I need to automate to test repeatedly.
I am placing the "sleep" command between "send" commands. However, what i see is that all the sleep's are executed in the beginning before the screen loads. expect concatenates the sends (I see that at the screen bottom during sleep) then issues them together.
I have tried sending all keys with "send -s" or "send -h". That marginally helps. I've replaced "-f" on line 1 with "-b" - again a tiny difference.
Why isn't "sleep" pausing at the right time.
Incidentally, my programs have a getc() loop, so i can't use "expect" command. I tried that too.
#!/usr/bin/expect -f
spawn ruby testsplit.rb
#expect
set send_human {3 3 5 5 7}
set send_slow {10 1}
exp_send -s -- "--"
exec sleep 3
send -s "+"
send -s "="
sleep 1
send -h -- "-"
send -h -- "-"
sleep 1
send -h -- "v"
interact
I would guess that you need to wait for your ruby program to start up before you continue with the sends and sleeps. Is there any string the ruby program outputs when it has started (eg. "ready") ? If so, at the point where you have expect commented out I would try expect "ready" so that Expect will wait until the ruby program has started before continuing.

Resources