Reputation: 132
Edit: I've figured it out by referencing the Exploring Expect book. Please don't hesitate to add to my answer or make other suggestions, though! I will mark this as answered when I am allowed to (2 days from now).
I've looked around and unfortunately I'm not able to find too much on using SSH with expect here on this site. I am relatively new to Expect but I have been teaching myself with the Exploring Expect book.
My question is: How can I use a single spawned SSH process for multiple tcl procedures? My workaround right now is to close the SSH connection at the end of procedure 1 and re-spawn a new SSH connection in procedure 2.
Example: (The example is simplified quite a bit and only includes the necessary components to demonstrate my question... My whole program is over 200 lines as of now)
;# Proc definition for procedure1
proc procedure1 {user host pw} {
spawn /usr/bin/ssh $user@$host
expect "Password:"
send "$pw\r"
expect "#" ;# This is my device's prompt
;# From here it does a bunch of stuff... sends commands to the SSH
;# session, captures output, builds arrays and lists, etc
send "exit" ;# Disconnects the SSH session
return $mylist ;# returns a list of numbers to be used in procedure 2
}
;# Proc definition for procedure2
proc procedure2 {resultofproc1 user host pw} {
spawn /usr/bin/ssh $user@$host
expect "Password:"
send "$pw\r"
expect "#" ;# This is my device's prompt
;# Proc 2 now continues on in the same device using the results (a
;# list) from proc1.
return
}
;# Procedure call for first procedure:
set resultofproc1 "[procedure1 $user $host $pw]"
;# Procedure call for second procedure:
procedure2 $resultofproc1 $user $host $pw
Instead of closing the SSH connection at the end of procedure1 and reopening the SSH connection at the beginning of procedure2, how can I send commands from procedure2 to the SSH connection opened in procedure1? Obviously I would have to remove the sending of exit to keep the connection open. Assuming this is possible... Is this even a good practice or am I better off separating connections between procedures? If so, can you modify my code example to demonstrate how?
From what I have gathered, I think it has to do with the spawn_id variable.. but I can't figure out how to implement that in my code. I am currently reviewing chapter 10 "Handling Multiple Processes" in the Exploring Expect book. I will report back if I can figure it out on my own.
Thanks for your assistance!
I have viewed these answers on stackoverflow:
Tcl Expect Keep SSH Spawn open
Expect Procedure for SSH Login
Using procedure for spawing SSH doesn't work properly with expect
Upvotes: 2
Views: 960
Reputation: 20688
You can use global to declare spawn_id
as a global var. According to expect's manual:
CAVEATS
...
Expect takes a rather liberal view of scoping. In particular, variables read by commands specific to the Expect program will be sought first from the local scope, and if not found, in the global scope. For example, this obviates the need to placeglobal timeout
in every procedure you write that usesexpect
. On the other hand, variables written are always in the local scope (unless aglobal
command has been issued). The most common problem this causes is whenspawn
is executed in a procedure. Outside the procedure,spawn_id
no longer exists, so the spawned process is no longer accessible simply because of scoping. Add aglobal spawn_id
to such a procedure.
...
You can also take advantage Tcl's upvar command. For example:
[STEP 101] $ cat foo.exp
proc expect_prompt {} {
upvar spawn_id spawn_id
expect -re {bash-[.0-9]+[#$] $}
}
proc open_conn {} {
upvar spawn_id spawn_id
spawn bash --noprofile --norc
expect_prompt
}
proc close_conn {} {
upvar spawn_id spawn_id
send "exit\r"
expect eof
}
proc send_cmd { cmd } {
upvar spawn_id spawn_id
send "$cmd\r"
expect_prompt
}
proc main {} {
open_conn
send_cmd "echo spawn_id=$spawn_id"
send_cmd "ps Tu"
close_conn
}
main
[STEP 102] $
Output:
[STEP 103] $ expect foo.exp
spawn bash --noprofile --norc
bash-4.4$ echo spawn_id=exp6
spawn_id=exp6
bash-4.4$ ps Tu
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
root 71513 0.0 0.0 2433012 788 s013 R+ 12:58PM 0:00.00 ps Tu
pynexj 71510 0.0 0.0 2445360 1572 s013 Ss 12:58PM 0:00.01 bash --noprofile --norc
bash-4.4$ exit
exit
[STEP 104] $
Upvotes: 2
Reputation: 132
Ah, I was so close! Had I taken a few more minutes in the Exploring Expect book, I would have figured it out. Anyways, here is my solution. Hopefully it helps someone else with the same problem.
It has everything to do with the spawn_id variable. Basically, by default, the send and expect commands interact only with the last spawned process. Each spawned process is assigned a unique spawn_id value. You can set the spawn_id of a procedure to the spawn_id of another spawned process.
Here is the code in my original question modified to support the spawn_id variable:
;# Proc definition for procedure1
proc procedure1 {user host pw} {
spawn /usr/bin/ssh $user@$host
global procedure1_spawnid ;# Creates a global variable called
;# procedure1_spawnid
set procedure1_spawnid $spawn_id ;# Assigns the spawn_id of the SSH process to the
;# global variable.
expect "Password:"
send "$pw\r"
expect "#" ;# This is my device's prompt
;# From here it does a bunch of stuff... sends commands to the SSH
;# session, captures output, builds arrays and lists, etc
return $mylist ;# returns a list of numbers to be used in procedure 2
}
;# Proc definition for procedure2
proc procedure2 {resultofproc1} {
global procedure1_spawnid
set spawn_id $procedure1_spawnid ;# Sets the spawn_id to that of the SSH connection
;# in procedure1.
send "show ip interface brief"
expect "#"
;# The show ip interface brief command is sent to the device through the
;# SSH connection opened in procedure1.
return
}
;# Procedure call for first procedure:
set resultofproc1 "[procedure1 $user $host $pw]"
;# Procedure call for second procedure:
procedure2 $resultofproc1
More information can be found in Exploring Expect, page 233.
If anyone has any suggestions or improvements, please feel free to lay them on me! I am still learning tcl/expect.
Upvotes: 0