0

I'm fairly new to Python, but trying to create a small script that will SSH into a server on my work and navigate through various menus. Normally I would do that with Putty client. I did succeed in this using code below:

from socket import setdefaulttimeout
import time
import paramiko
from getpass import getpass
from prompt_toolkit import ANSI

# Connection parameters for SSH + create connection.
hostname = 'workhostname'
port = 22
user = input('User (server): ')
passwd = getpass('Password (server): ')
programlogin = input('User (hyperspace): ')
programpass = getpass('Password (hyperspace): ')
exportdir = "/home/" + user + "/PYTHONTEST1"
commandsequence = ["2", "1", programlogin, programpass, "", "6", "DEP", "", "1", "7", "6", "", "17030", "24650", "", "995", "1121042", "1121806", "", exportdir, "", ""]

client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname,port=port,username=user,password=passwd)

# Create shell via paramiko.
chan = client.invoke_shell(term='xterm')
# Time.sleep for more than 5 seconds to make sure that the program initial screen pause of 5 seconds is passed.
# "This message will disapper in 5 seconds"
time.sleep(7)

# Loop through command sequence.
for command in commandsequence:
    # Send command.
    chan.send(command + '\r')
    time.sleep(2.3)
    print(chan.recv(4096).decode('ISO-8859-1')) # For debugging purposes - to check whats actually going on in the console
    if command == programpass:
        print("Command: PASSWORD HIDDEN completed")
    else:
        print("Command: " + command + " completed")
    # Trying to create some loop to check if all bytes has been received from the channel. loops out, if not ready after 10 checks.
    """
    counter = 0
    while not chan.recv_ready():
        print("Not ready")
        time.sleep(1)
        if counter < 10:
            counter += 1
        else:
            break
        """
    # Get all bytes + decode the data (so it is readable through print)
    #s = chan.recv(4096).decode()
s = chan.recv(4096).decode('ISO-8859-1')
print(s)

client.close()

However this only works because I give the client enough time (time.sleep(2.3)) between each command. I have read somewhere that paramikos exec command is the only real reliable way to tell if the command was actually completed. However I don't think I will be able to use the exec command to navigate this "program" that I'm facing when doing the shell approach. I can use linux terminal commands like "hostname" to get that returned, but I have no idea how to start the program and navigate through it this way.

Will I somehow be able to tell, by reading the chan.recv() if I'm done receiving output from the server, instead of "blindly" trust some high timer? - Or what would the approach be?

Jelboen
  • 39
  • 5
  • 1
    You might want to check out [`pexpect`](https://pypi.org/project/pexpect/) as well. It was built for just this sort of thing. – MattDMo Aug 28 '22 at 00:34
  • 1
    If you're using the same connection for a bunch of commands, as it seems you are, and if you can't make assumptions about what the end of the output of a command will look like, then I don't see how you'll ever know for sure. Ask yourself, how do you tell the difference between a command that prints some output and then completes right away, and one that outputs something and then goes off for 20 minutes before finishing what it's doing. Think about your own experience at a console. How do you know when something is done? It's when you see a new prompt, right? What's the equivalent hee? – CryptoFool Aug 28 '22 at 00:42
  • 1
    Would it work to create a new connection per command rather than using a shell? That's what's usually done. Then you know when you've got all the output because the socket is closed on the other end. – CryptoFool Aug 28 '22 at 00:46
  • 1
    What CryptoFool said. If you can at all avoid it, _don't_ use a long-running shell session in the first place; just run separate commands over the same transport. Because it's all one transport you don't have all the authentication overhead of setting up a new connection for each one; because it's separate channels for each and separate information on how they exited, you get proper isolation of each process's stdout, stderr and exit status. – Charles Duffy Aug 28 '22 at 00:47
  • ...if you _must_ have a long-running session, MattDMo's comment is on-point: you want an `expect`-like tool. – Charles Duffy Aug 28 '22 at 00:49
  • @CharlesDuffy - I know you generally know what you're talking about. I'm curious how some other client is going to do a better job with this. It seems that for the general case, like I describe it, there's nothing the client can do. There IS no difference in the short term between a short running command and a long running command that writes some stuff to stdout and then goes away for a long time. Right? – CryptoFool Aug 28 '22 at 00:54
  • 1
    @CryptoFool, right, by "long-running" I meant to refer just to places where you have a shell that lasts for more than the time that it takes to `exec` a single external command. Because ssh only lets one pass the command to run over the wire as a string, not as an argv array, it's unavoidable that there's a shell involved, even in the shell-that-just-invokes-one-executable trivial case; that's what's being distinguished against when I say "long-running shell". – Charles Duffy Aug 28 '22 at 02:20

0 Answers0