No result on standard output when running expect - bash

Whenever I try to run the script it doesn't show me any result on standard output.
#!/usr/bin/expect --
send [exec tail -f /var/opt/jboss/log/jbossall.log | grep -i "pattern"]
Please advise the reason.

The exec command never completes because you're using tail -f. From the info page for tail: "Loop forever trying to read more characters at the end of the file". Since exec does not complete, send is never invoked.
You probably need to do something like this:
spawn whatever
set whatever_id $spawn_id
spawn sh -c {tail -f /var/opt/jboss/log/jbossall.log | grep -i "pattern"}
set tail_id $spawn_id
while {1} {
expect -i $tail_id "(.*)\n"
send -i $whatever_id $expect_out(1,string)
}

Related

using dmenu to define variables

i'm trying to write a simple script to cut a segment of an audio file, set it to an image and render it to a video.
i want to use dmenu as a clean way of displaying my prompts for inputting variables.
all works well except it seems dmenu expects something to be piped into it, otherwise the script doesn't work.
here is how i have the relevant part of the code currently working
INPUT=$(ls | dmenu -i -p "Input file:")
START=$(echo " " | dmenu -i -p "Start time:")
DURATION=$(echo " " | dmenu -i -p "Duration:")
IMAGE=$(ls | dmenu -i -p "Image file:")
i would like to remove the echo command for $START and $DURATION, both because it is ugly (it leaves an unneeded coloured box of space in dmenu) and because it seems redundant. however, without it the script fails on these lines.
does dmenu always expect something to be piped into it?
perhaps i am doing something else completely wrong, this is only my 3rd ever shell script.
To get rid of colored spaces you can do this:
DURATION=$(:| dmenu -i -p "Duration:")
or
DURATION=$(dmenu -i -p "Duration:"< <(:))
: means true. So it's the same as writing:
DURATION=$(true| dmenu -i -p "Duration:")
DURATION=$(dmenu -i -p "Duration:"< <(true))
But you can also use the -n flag to echo for the same resultat:
DURATION=$(echo -n ''|dmenu -i -p "Duration:")
DURATION=$(dmenu -i -p "Duration:"< <(echo -n ''))
From the man page:
... which reads a list of newline-separated items from stdin
So yes, dmenu need stuff to be piped to it.

How to use expect in Shell script like nested If Else loop?

I have written below Shell script which is intended to get Model name from remote host by doing SSH and executing the command.
#!/bin/bash
> output.csv
IFS=","
echo "IP,Model Name" >> output.csv
while read ip
do
#echo "Current IP is: $ip"
model=expect -c 'spawn ssh username#'"$ip"' "show version | in cisco"; expect -re "The.*(yes/no)?"; send "yes\r"; expect -re ".*UNAUTH.*password:"; send "password\r";' | grep cisco
echo "$ip,$model" >> output.csv
done < Check_SSH.csv
When I execute below command manually, then it gives expected model name as output.
Command:
expect -c 'spawn ssh username#'"$ip"' "show version | in cisco"; expect -re "The.*(yes/no)?"; send "yes\r"; expect -re ".*UNAUTH.*password:"; send "password\r";' | grep cisco
But when its put into script like above it doesn't produce any output.
Also, there are MOTD (Message of the day) configured on most of the servers and "The authenticity of host..." message to adding server into .ssh/known_hosts, So I tried to handle them in script but Expect is failing to handle the situation when MOTD doesn't appear or when remote is already present in .ssh/known_hosts.
Any help is highly appreciated to get this script running.
Expected output:
IP,Model Name
8.8.8.8,C9407R
8.8.8.1,C9407R
8.8.8.2,C9407R
8.8.8.3,C9407R
First, you're missing the Command Substitution syntax to execute the expect code:
model=$(expect -c ...)
# ....^^.............^
Next, to optionally expect patterns, you need the expect {patt1 action1 patt2 action2 ...} form:
expect -c '
spawn ssh username#'"$ip"' "show version | in cisco"
expect {
-re "The.*(yes/no)?" {send "yes\r"; exp_continue}
-re ".*UNAUTH.*password:" {send "password\r"; exp_continue}
eof
}
'
That way, expect can match any of the patterns. The exp_continue command "loops" within the same expect command so you can match more than one of them. The eof pattern matches when ssh connection closes after the "show version ..." command has finished.
Newlines for readability.
Putting this together:
model=$(
expect -c '
spawn ssh username#'"$ip"' "show version | in cisco"
expect {
-re "The.*(yes/no)?" {send "yes\r"; exp_continue}
-re ".*UNAUTH.*password:" {send "password\r"; exp_continue}
eof
}
' | grep -i cisco
)
I have a feeling that there's more you need to do in the grep part, but you didn't show the output of just the expect command.
update:
use spawn -noecho ssh ... so expect will not print the spawn command.
then, you'll get whatever output ssh needs to show for the login process, and then the "show" command output:
if you're expecting exactly 1 line of output, you might want to change grep to tail -n 1.
otherwise, show the output you get and we can help you filter out the noise.
update 2: filtering out the noise
I'm going to assume that the regex pattern cisco (.*) processor is what you need to match:
model=$(
expect -c '
log_user 0
spawn ssh username#'"$ip"' "show version | in cisco"
expect {
-re "The.*(yes/no)?" {send "yes\r"; exp_continue}
-re ".*UNAUTH.*password:" {send "password\r"; exp_continue}
-re "cisco (.*) processor" {puts $expect_out(1,string)}
}
expect eof
'
)
log_user 0 turns off the spawned process's ability to write to stdout. Expect can still capture its output though.

Bash output from expect script to two different files

I am trying to output to two different files using tee. My first file will basically be tail -f /myfile and my second output will be a subset of the first file. I have looked online that they were saying we can use `|
tee >(proc1) >(proc2)
I have tried the above but both my files are blank.
Here is what i have so far:
myscript.sh
ssh root#server 'tail -f /my/dir/text.log' | tee >(/mydir/my.log) >(grep 'string' /mydir/my.log > /mydir/mysecond.log)
myexpect.sh
#!/usr/bin/expect -f
set pass password
spawn /my/dir/myexpect.sh
expect {
"key fingerprint" {send "yes/r"; exp_contiue}
"assword: " {send "$pass\r"}
}
interact
In your script, there are some problems in the usage of tee,
tee >(/mydir/my.log): can be substitute with tee /mydir/my.log, since tee would write to stdout and files, i.e. /mydir/my.log
grep 'string' /mydir/my.log > /mydir/mysecond.log: as I mentioned, tee would also write to stdout, so no need to grep the string from file, you can grep from stdout directly. Use pipeline to do it.
So the whole command shall be modified as followed,
ssh root#server 'tail -f /my/dir/text.log | tee /mydir/my.log | grep --line-buffered "string" > /mydir/mysecond.log'
Edit:
For your further question
The command would hang because of tail -f was still waiting for output the growing file. If you don't want the command hanged, try to remove -f for tail.
Depends on the option -f existed for tail, you shall use two different way to allow the grep write file.
For tail case: grep can successfully write file
For tail -f case: --line-buffered for grep would use line buffering on output

How to interactive command in expect script

I have a small expect script, and I want to send command based on output.
this is example
#! /usr/bin/expect
spawn ssh root#hostname
expect "Password:"
send "12345\r"
expect "root#host:#"
send "ls -lrt" # depend on this output I need delete file
from here, if i have file list a,b,c,d
I want to send "rm a" but file name will change each time when I run script.
I don't know how script make wait until I put command, also I don't want to type rm command every time. I only want to type file name.(this is example, the real command is long, I don't want to type same long command every time.)
So what I want is that the script wait until I put only file name and after I type file name, it send "rm filename" and keep going rest of script.
please help..
this does not need to be interactive at all. I assume your requirement is to delete the oldest file. so do this:
ssh root#hostname 'stat -c "%Y:%n" * | sort -t: -k1,1n | head -1 | cut -d: -f2- | xargs echo rm'
# .. remove the "echo" if you're satisfied it finds the right file .................... ^^^^
Use expect_user:
#!/usr/bin/expect
spawn ssh root#hostname
expect "Password:"
send "12345\r"
expect "root#host:#"
send "ls -lrt" # depend on this output I need delete file
expect_user -re "(.*)\n" {
set filename $expect_out(1,string)
send "ls -al $filename\r" ;#// Substitute with desired command
}
expect eof

Performance bottleneck in expect script

I'm trying to find out the point in my script that is slowing down my entire process. I'm using the expect script to send a sed command to search and replace a line in a file. This takes anywhere from 2s to 20s to finish, when it shouldn't last more than a second. I am two expect scripts in parallel in two terminals. The first file, launchmpj.exp launches a qsub job that takes several seconds to start. The second file, launchneuron.exp waits for the qsub job to start and continues the script. When the qsub job starts, the launchmpj.exp sends a command that allows the second file, launchneuron.exp to know the qsub job started and to stop waiting.
Here's launchmpj.exp
#!/usr/bin/expect -f
set timeout -1
spawn ssh $::env(username)#server
expect "$ "
send "qsub -I -q berger -A lc_tb -l nodes=\$nbnodes -l walltime=24:00:00 -d .\r"
expect "$ "
send "cp \$PBS_NODEFILE node`sed -n '1p' nodequeue`\r"
expect "$ "
send "sed -i '/wait=on/ s//wait=off/' `sed -n '1p' qsubwaitqueue`\r"
expect "$ "
send "cd $::env(MPJ_HOME)/bin\r"
expect "$ "
send "sh $::env(MPJLAUNCH)\r"
expect "Process 6 ended"
Here is the second file launchneuron.exp
#!/usr/bin/expect -f
set timeout -1
spawn ssh $::env(username)#server
expect "$ "
send "set qsubwait = qsub`sed -n '\$p' queue`.sh\r"
expect "$ "
send "sh \$qsubwait\r"
expect "$ "
send "set nodefile = node`sed -n '1p' nodequeue`\r"
expect "$ "
send "ssh `sed -n '2'p \$nodefile`\r"
expect "$ "
send "cd $::env(NEURON_HOME)\r"
expect "$ "
send "nrniv -python $::env(NEURONPY)\r"
expect "$ "
As part of the process, I'm running a sed substitution on the file below. The execution of sed alone is very fast, which means it's not the bottleneck in the aforementioned script. However, when done from the expect script, that's when it takes a long time.
sed -i '/wait=on/ s//wait=off/' qsubwait.sh
File qsubwait.sh:
wait=on
echo "Waiting for qsub to start."
while [ $wait = on ]; do
eval `sed -n '1'p qsubwait.sh`
echo `sed -n '1'p qsubwait.sh`
done
Are you hitting expect's timeout value? Run your expect script with exp_internal 1 to see what expect is waiting for.

Resources