'send' does not result in the command being successfully executed in the shell - shell

Consider the following scenario:
#! /usr/bin/expect --
#start of the script
proc A {host_ip} {
global spawn_id
spawn ssh $host_ip
}
#pattern match using expect statements on the
#output of the spawned ssh process in A
proc B {} {
expect -ex "$" { send "ls -l\r"; puts "did get this far" } #<------- (1)
expect -ex "$" { } #<------- (2)
#find a filename of interest from the output of 'ls' command earlier
regexp "((a-b\\S+\\s+)+)" $expect_out(buffer) matched file_of_interest #<------- (3)
#the following reliably succeeds in sending the intended command, but is obviously pointless
expect {
-ex "$" { send "ls -l\r"; exp_continue} #<------- (4)
}
}
#call the routines
A $some_ip
B
#end of the script
My main problem is that at (1) 'send' does not result in 'ls' command being sent, or at least I do not see any evidence of it being sent, but the 'puts' statement still gets executed. The 'puts' statement after 'send' at (1) was placed for debugging purposes only. At (2) I match the prompt with the hope that expect_out(buffer) will contain the output of the 'ls' command. I do not understand why (4) succeeds in sending the command, but (1) does not, consquently, expect_out(buffer) does not contain the desired data.
The 'send' command at (1) should work, but it does not. The 'send' at (4) works, but it is pointless. Is there a way to find out why the 'send' at (1) does not succeed? Any suggestion on improving the reliablity of 'send' at 1.
Thanks.

Related

Send command invoked first but outputs Puts command string First

Have a expect script:
....
spawn -noecho sshpass -e ssh -e "~" -o "KexAlgorithms diffie-hellman-group1-sha1" -o "HostKeyAlgorithms ssh-dss" -o "Ciphers aes256-cbc" HostIp
expect -timeout 30 "XXXXX> " {
send "YYYYY\r"
} \
timeout {
puts "\n\n!!!Failure: Go to YYYYY Environment!!!\n\r";
interact {
\001 {
send_user "\n\nUser interaction completed.\n\n"
return
}
}
}
puts "\nCorrect Envrionment\n"
...
the scripts outputs:
XXXXX>
Correct Envrionment
YYYYY
when the expected output should be
XXXXX> YYYYY
Correct Envrionment
as state in this post got the ideia why "puts" is faster in its output then "send" even if "send" is invoked first, but how to force the "puts" output to show after the "send" output?
the First answer in that post refers to
flush stdout
but thats tcl don't see that command flush in expect and that would make the "puts" output even faster when the probleam here is it already to fast...
the second answer is good where explains well the behaiver and why that is expected but the solutions don't seem good in the long run is there some kind of flag to indicate that puts should run only after send as been completed?

Sending commands to "application's shell" using "bash script" [duplicate]

This question already has answers here:
Is it possible to make a bash shell script interact with another command line program?
(6 answers)
Closed 1 year ago.
I have a program JLinkExe which opens it's own prompt uponn execution. So I normally run it like this:
JLinkExe
and then type the commands at it's prompt that appears:
J-Link>
There are many applications with it's own prompt and I am interested in a general method that would enable me to send commands to any kind of application that has it's own prompt.
I already tried two methods. They both try to send commands in this order:
connect
Enter
Enter
Enter
Enter
erase
loadbin program.bin , 0x0
r
q
but both fail. Here is the first method:
{ echo 'connect';
echo '';
echo '';
echo '';
echo '';
echo 'erase';
echo 'loadbin program.bin , 0x0';
echo 'r';
echo 'q'; } | JLinkExe
And the second method (source):
JLinkExe <<EOF
connect
erase
loadbin program.bin , 0x0
r
q
EOF
I found these method on the internet but I don't understand why they fail. Especially the first one that worked in the past...
Can anyone propose any better / working / universally applicable method?
I think it might be because here-docs do not wait for output. Unfortunately for you I switched company, thus can't test my code below.
#! /bin/bash
expect <<-EOF
set timeout -1
spawn JLinkExe
expect "J-Link> " { send "connect\r" }
expect "J-Link> " { send "\r" }
expect "J-Link> " { send "\r" }
expect "J-Link> " { send "\r" }
expect "J-Link> " { send "\r" }
expect "J-Link> " { send "erase\r" }
expect "J-Link> " { send "loadbin program.bin , 0x0\r" }
expect "J-Link> " { send "r\r" }
expect "J-Link> " { send "q\r" }
expect eof
catch wait result
exit [lindex \$result 3]
EOF
exit $?
Except waits until J-Link> turns up and then sends the command through the connection.
If it doesn't work please notify me. I'll try to help you after the weekend :-)
EDIT:
A: Why did you wrap everything in expect 2>&1 <<-EOF and EOF?
You can add expect in the shebang, but I often use it as part of my Bash scripts. My knowledge of Bash is better.
B: Why a -EOF instead of EOF?
That's because <<-EOF allows leading tabs when you want to end the here-doc. You can indent it in functions for instance.
C: Why did you redirect stderr to stdout (2>&1)?
In your case I should've removed this. I took the code from one of my other answer about expect and tailored it to your needs.
D: What does catch wait result and exit [lindex \$result 3] do after we catch the eof?
Nice question, I had to look this one up a little myself:
lindex takes 4rd argument in \$result and exits the here-doc (0 is arg 1).
\$result is set by catch wait result.
Catch takes the output of wait and puts that into result.
Wait returns four integers:
First: pid of process that's being waited on.
Second: spawn ID.
Third: -1 for errors, 0 otherwise.
Forth: Exit status of the program as set by the OS.
Sources:
https://linux.die.net/man/1/expect
https://www.tcl.tk/man/tcl/TclCmd/catch.html
https://www.tcl.tk/man/tcl/TclCmd/lindex.html
Note that you have to escape the $ in the here-doc, otherwise Bash tries to process it. Hence \$result.
E: Why you exit with exit $?
Bash exits a script with the last known error code. Although you can leave it implicitly, I like to add it anyhow. It keeps the script more readable for beginners.

How to make expect's expect command fail if the output isn't matched?

Given the very simple script script.expect
#!/usr/bin/expect
spawn bash
expect "#"
send "/bin/false; echo \"process returned with $?\"\r"
expect -exact "process returned with 0"
send -- "exit\r"
expect eof
I don't seem to be able how the script can not fail since /bin/false will cause the echo command to print process returned with 1, thus process returned with 0 can never be matched on the expect command. I expect expect script.expect to fail with return code 1 after expect -exact "process returned with 0".
#!/usr/bin/expect
spawn bash
expect "#"
send "/bin/true; echo \"process returned with $?\"\r"
expect -exact "process returned with 0" {
send -- "exit\r"
expect eof
exit 0
}
exit 1
Even if I change the logic of my "application" in order to be able to test it with a positive/logically negated flow the outcome is still unexplainable.
I worked through
How to make expect command in expect program script to wait for exact string matching
https://www.thegeekstuff.com/2010/10/expect-examples
https://unix.stackexchange.com/questions/66520/error-handling-in-expect
https://unix.stackexchange.com/questions/79310/expect-script-within-bash-exit-codes?rq=1
and have no clue why expect is behaving this way.
In your first script, the expect -exact... command is "succeeding" with a timeout. The default timeout is 10 seconds, and the default action on timeout is to do nothing. So the commands waits 10 seconds, matches timeout, and returns, so we continue with the next command.
You can explicitly match for timeout:
expect {
-exact "process returned with 0" {}
timeout { puts "timeout!"; exit 1 }
}
To avoid the wait to timeout, you can use a regexp that will match whether $? is 0 or 1 (or other numbers). If you put part of the regexp in a capture group (), you can then find it in built-in variable $expect_out(1,string):
expect -re {process returned with ([0-9]+)}
set returncode $expect_out(1,string)
puts "we got $returncode"
exit $returncode
Note, the regexp uses {} style quotes, because "" quotes dont allow you to use [] inside them.

Expect script return value

I'm including simple Expect commands within a Bash script (I know I could be just writing a pure Expect script, but I would like to get it to work from within Bash).
The script is below:
#!/bin/bash
OUTPUT=$(expect -c '
spawn ssh mihail911#blah.org
expect "password:"
send "dog\r"
')
Upon ssh'ing to the above address, it will return something of the form mihail911's password: on the prompt, so I think my expect line is valid.
When I run this my script does not print anything. It does not even show the password: prompt. In general, even if I manually provide an incorrect password, I will receive a Incorrect password-type response prompt. Why is nothing printing and how can I get my script to execute properly?
I have tried debugging by using the -d flag and it seems to show that at least the first expect prompt is being matched properly.
In addition, what values should I expect in the OUTPUT variable? When I echo this variable, it simply prints the first the first command of the expect portion of the script and then mihail911's password:. Is this what it's supposed to be printing?
Use:
#!/bin/bash
OUTPUT=$(expect -c '
# To suppress any other form of output generated by spawned process
log_user 0
spawn ssh dinesh#xxx.xxx.xx.xxx
# To match some common prompts. Update it as per your needs.
# To match literal dollar, it is escaped with backslash
set prompt "#|>|\\$"
expect {
eof {puts "Connection rejected by the host"; exit 0}
timeout {puts "Unable to access the host"; exit 0;}
"password:"
}
send "root\r"
expect {
timeout {puts "Unable to access the host"; exit 0;}
-re $prompt
}
send "date\r"
# Matching only the date cmd output alone
expect {
timeout { puts "Unable to access the host";exit 0}
-re "\n(\[^\r]*)\r"
}
send_user "$expect_out(1,string)\n"
exit 1
')
echo "Expect's return value: $?"; # Printing value returned from 'Expect'
echo "Expect Output: $OUTPUT"
Output:
dinesh#MyPC:~/stackoverflow$ ./Meric
Expect's return value: 1
Expect Output: Wed Sep 2 09:35:14 IST 2015
dinesh#MyPC:~/stackoverflow$

Expect call shell script

I am trying to call a shell script and store the result in an expect variable. get_pw.sh accepts 2 args and decrypts the file using the provided md5hash. If I execute ./get_pw.sh file.test md5hash from the bash prompt it returns the password string as expected. When called from expect, the password does not get returned. The expect debug shows:
expect: does "" (spawn_id exp0) match regular expression "[^\s]"?
So it looks like the script is not returning the password string when called from expect. Relevant code:
#!/usr/bin/expect
send "./get_pw.sh file.test md5hash \r"
expect -re {[^\s]} {
set password $expect_out(0,string)
}
puts "The password is: $password"
You need to spawn a command first before you can send input and expect output from it.
To set an expect variable to the output of a command, use
set varname [exec command]
If you must do this with expect,
log_user 0
spawn -noecho get_pw.sh file hash
expect eof
set passwd [string trimright $expect_out(buffer) "\r\n"]
puts $passwd
Jens's answer looks pretty good by now ...

Resources