Send command invoked first but outputs Puts command string First - expect

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?

Related

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.

Read program output from stdin instead of using spawn

I'm trying to write a shell function that spawns a ssh process and authentificates with a password. Then, I'd like to use the spawned process to do further stuff with expect.
Here's the function I have so far:
ssh_cmd() {
ssh $USER#$HOST 2>&1 | expect -c "
log_user 0
set timeout 5
expect password: {
send \"$PASS\n\"
sleep 2
log_user 1
}
"
}
And then I'd like to use given function in other places to interact with the ssh process like this:
ssh_cmd | expect -c "
expect '#' {
send pwd\n
send exit\n
}
expect eof
"
However, running the ssh_cmd function with -d option for expect I get the following result:
expect version 5.45.4
expect: does "" (spawn_id exp0) match glob pattern "password:"? no
ubnt#ui1's password: expect: timed out
From what I understand, the output of ssh does not get piped correctly. I know the common way to do this would be to use spawn, but that would mean the process would get killed after expect exits and I could not have a generic function that authentificates ssh sessions and keeps the process alive for further usage.
What you're designing won't work. Expect needs the process to be spawned from within the expect interpreter using the spawn command. Passing the command's stdout into expect is insufficient.
You could try this:
ssh_cmd() {
# a default bit of code if user does not provide one.
# you probably want some checking and emit an error message.
local user_code=${1:-set timeout 1; send "exit\r"; expect eof}
expect -c "
log_user 0
set timeout 5
spawn ssh -l $USER $HOST
expect password: {
send \"$PASS\n\"
sleep 2
log_user 1
}
$user_code
"
}
and then invoke it like:
ssh_cmd '
expect "#" {
send pwd\r
send exit\r
}
expect eof
'
Note that single quotes have no special meaning in expect, they are just plain characters: you probably don't want to expect the prompt to be the 3 character pattern '#'

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$

'send' does not result in the command being successfully executed in the 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.

expect error in bash expression

I have the following expect command that i use in my bash script in order to set passwordless ssh connection.
VAR=$(expect -c '
spawn ssh-copy-id -i '"$SSH_KEY_PATH_PUB $REMOTE_HOST_USER#$REMOTE_HOST_IP "'
expect "*?assword:*"
send "'"$REMOTE_HOST_PASSWD"'\r";
expect {
"Permission denied, please try again." {
exit '"$WRONG_PASSWORD"'
}
}
')
I am expecting password string as "?assword:" in order to send password.But whatever i write for the expect block it works.if i write
expect "xxxx"
expect "yyyy"
it also works, so i think it does not enter that block?.What is the solution
it could be that before a match is made, it reaches the end of the steam or a timeout occurs. Even if no match is found, expect will return and execution will continue, sending the password.
From the expect manual, under COMMAND, we have a section that explains the expect command:
waits until one of the patterns matches the output of a spawned process, a
specified time period has passed, or an end-of-file is seen.
To fix this, you should place the send code in expect's body:
VAR=$(expect -c '
spawn ssh-copy-id -i '"$SSH_KEY_PATH_PUB $REMOTE_HOST_USER#$REMOTE_HOST_IP "'
expect "*?assword:*" {
send "'"$REMOTE_HOST_PASSWD"'\r"
}
expect {
"Permission denied, please try again." {
exit '"$WRONG_PASSWORD"'
}
}
')
Hope this helps =)

Resources