I'm working with Python on my PC, sending serial commands to an arduino which controls a certain number of stepper motors.
However, in this function:
# takes array of commands to send to motors (in order) and sends commmand arcodinlgy
# each element of commands is an absolute angle (rad) to give to one motor
def send_command(commands):
if not len(commands) > 0:
return
# make command string to send serial
# (with command separator and line termination)
command_string = "";
for i in range(len(commands) - 1):
command_string += f"{commands[i]:.4f}{COMMAND_SEPARATOR}"
command_string += f"{commands[-1]:.4f}{LINE_TERMINATOR}"
# make command string into bytes UTF-8
# print(command_string)
command_string = bytes(command_string, "utf-8")
# send command string serial
print(f"Sending command: " + str(command_string))
port.write(command_string)
# wait for arduino's return_code on serial
while True:
if port.inWaiting() > 0:
return_code = int(port.readline())
return return_code
# driving code
while True:
commands = [0, 0]
commands[0] = float(input("command 1: "))
commands[1] = float(input("command 2: "))
return_code = send_command(commands)
print(f"return code: {return_code}")
This code works correctly, but if I don't want user input for the commands :
while True:
commands = [float(random.random()*pi), float(random.random()*pi)]
# or even the following doesn't work for exemple:
# commands = [3.1415, 0.5555]
return_code = send_command(commands)
print(f"return code: {return_code}")
sleep(1)
This code doesn't.
I don't get why because in the first case, the line print(f"Sending command: " + str(command_string)) prints exactly the same as in the second case :
Sending command: b'3.1415:0.5555\n'
(when input those angles)
But in the second case, no return code is received, and the function doesn't work.
I tried totally hardcoding the values given.
I implemented that the commands are always formated the same way, and always converted with float() (before the print("Serial command... line.
So I expected the same behavior for the two codes, and I don't see what changes between the two (since the print("Serial command ... line gives the same and is the last line before the command is sent over serial
Thanks !
Try to add logging to receiving part.
Replace this:
while True:
if port.inWaiting() > 0:
return_code = int(port.readline())
return return_code
With this:
import time
while True:
if port.inWaiting() > 0:
print(f"Received {port.inWaiting()} bytes.")
return_code = int(port.readline())
return return_code
print("Nothing received yet.")
time.sleep(0.1)
Maybe your Arduino does not response for request, and you just waiting for the response in infinite loop.
You can also add timeout for response, so after some time if there was no response method send_command(commands) will just return None.
Found the problem :
After every new serial connection, the Arduino resets.
When it resets, it need a little time to start the loop() function !
So when I was entering my commands manually, I gave it a little time without noticing, but when the commands were generated inside my Python code, the first command was sent so quickly that it wasn't interpreted by the Arduino, and the function didn't get a return code from the Arduino !
2 ways to solve the problem:
1. Adding a timeout
TIMEOUT = 2
TIMEOUT_ACTIVE = True
def send_command(commands):
### same code as above ###
port.write(command_string)
# wait for arduino's return_code on serial WITH TIMEOUT
init_time = time()
while (not TIMEOUT_ACTIVE) or (time() - init_time) <= TIMEOUT:
if port.inWaiting() > 0:
return_code = int(port.readline())
return return_code
2. Waiting before sending the first command
waiting 2 seconds (tested 1 seconds, was not enough) before begining to send commands
sleep(2)
while True:
commands = [float(random.random()*pi), float(random.random()*pi)]
return_code = send_command(commands)
print(f"return code: {return_code}")
# don't need the sleep(0.1) anymore
Related
I'm reading a book called ( Black Hat Python: Python Programming for Hackers and Pentesters
Book by Justin Seitz)
and I'm doing a project from the book called Replacing Netcat , basically an attempt to replace netcat with a python script ,
I wrote the code identical to the book but its not running properly,,,
** I mean I had to make some obvious changes , cause the book is using python 2.7 , and I'm using Python 3.9.12**
but the code isn't working as it should , according to the book,,,,
this is how the book is telling me to run the code ::- how the book tells me to run the script
me trying to run the code 1st step
me trying to run the code 2nd step
as u can see it gives no output ,, if I try to run it according to the book
and just print's the strings and stops when I write "python3 file_name.py" 2
and stops , it doesn't even execute a single function after that, what am I doing wrong ?
import sys
import socket
import getopt
import threading
import subprocess
# global variables that we fooking need .
listen = False
command = False
upload = False
execute = ""
target = ""
upload_dest = ""
port = 0
def usage():
print ("BHP Net Tool")
print()
print ("Usage: bhpnet.py -t target_host -p port")
print ("""-l --listen - listen on [host]:[port] for ¬
incoming connections""")
print ("""-e --execute=file_to_run - execute the given file upon ¬
receiving a connection""")
print ("-c --command - initialize a command shell")
print ("""-u --upload=destination - upon receiving connection upload a ¬
file and write to [destination]""")
print()
print()
print ("Examples: ")
print ("bhpnet.py -t 192.168.0.1 -p 5555 -l -c")
print ("bhpnet.py -t 192.168.0.1 -p 5555 -l -u=c:\\target.exe")
print ("bhpnet.py -t 192.168.0.1 -p 5555 -l -e=\"cat /etc/passwd\"")
print ("echo 'ABCDEFGHI' | ./bhpnet.py -t 192.168.11.12 -p 135")
sys.exit(0)
def main():
global listen
global port
global execute
global command
global upload_dest
global target
if not len(sys.argv[1:]):
usage()
# read commandline options
try:
opts,args = getopt.getopt(sys.argv[1:],"hle:t:p:cu:",
["help", "listen", "execute", "target", "port", "command", "upload"])
except getopt.GetoptError as err:
print(str(err))
usage()
for o,a in opts:
if o in ("-h", "--help"):
usage()
elif o in ("-l", "--listen"):
listen = True
elif o in ("-e", "--execute"):
execute = a
elif o in ("-c", "commandshell"):
command = True
elif o in ("-u", "--upload"):
upload_dest = a
elif o in ("-t", "--target"):
target = a
elif o in ("-p", "--port"):
port = int(a)
else:
assert False, "Unhandled Option"
#are we going to listen or just send data from stdin ?
if not listen and len(target) and port > 0 :
#read in buffer from the commandline
#this will block , so send CTRL-D if not sending input
#to stdin
buffer = sys.stdin.read()
# send data off
client_sender(buffer)
# we are going to listen and potentially
# upload things , execute commands , and drop a shell back
# depending on our command line options above
if listen:
server_loop()
main()
def client_sender(buffer):
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# connect to out target host
client.connect((target,port))
if len(buffer):
client.send(buffer)
while True:
# now wait for back
recv_len = 1
response = ""
while recv_len:
data = client.recv(4096)
recv_len = len(data)
response += data
if recv_len < 4096:
break
print(response)
# wait for more input
buffer = input("")
buffer += "\n"
# send it off
client.send(buffer)
except:
print("[*] Exception ! Exiting.")
# tear down the connection
client.close()
def server_loop():
global target
#if no target is defined, we listen on al interfaces
if not len(target):
target = "0.0.0.0"
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((target,port))
server.listen(5)
while True:
client_socket, addr = server.accept()
# spin off a thread to handel our new client
client_thread = threading.Thread(target=client_handler, args = (client_socket,))
client_thread.start()
def run_command(command):
# trim the newline
command = command.rstrip()
# run the command and get the output back
try:
output = subprocess.check_output(command,stderr = subprocess.STDOUT, shell=True)
except:
output = "Failed to execute command.\r\n"
#send the output back to the client
return output
def client_handler(client_socket):
global upload
global exceute
global command
# check for upload
if len(upload_dest):
# read in all of the bytes and write to our destination
file_buffer = ""
#keep reading data until none is available
while True:
data = client_socket.recv(1024)
if not data:
break
else:
file_buffer += data
# now we take these bytes and try to write them out
try:
file_descriptor = open("upload_dest", "wb")
file_descriptor.write(file_buffer)
file_descriptor.close()
# acknowledge that we wrote the file out
client_socket.send(f"Succesfully saved file to {upload_dest}")
except:
client_socket.send(f"Failed to save file to \r\n{upload_dest}")
# check for command execution
if len(execute):
# run the command
output = run_command(execute)
client_socket.send(output)
# now we go into another loop if a command shell was requested
if command:
while True:
# show a simple prompt
client_socket.send("<BHP:#>")
# now we recive until we see a linefeed ( enter key )
cmd_buffer = ""
while "\n" not in cmd_buffer:
cmd_buffer += client_socket.recv(1024)
# send back the command output
response = run_command(cmd_buffer)
# send back the response
client_socket.send(response)
How is your debugging skill?
I was experiencing the same issue afew days ago & i fixed it by debugging entry points into functions to keep track of the eip while the program is running by printing arguments before any operation as they are being passed between functions .. this helps to check whether the code is producing expected values as it runs ..
in short as this case , the book was written in regards to python2 so most of the errors are going to force you to work on your debugging skills..
i would also advice you to take a short crash course in C programming before taking on the BHP book since alot of system calls like socket getopts are really C apis , check on man 2 and man 3 of whatever you're importing in python to have a broader look on things as you debug most errors ..
managed to fix it up until here ..
download source code link
https://filebin.net/i40e2oisabxqmbys
I'm working on a script to send a list of commands to a device and return the output.
When the device first boots up, it has a few prompts. I am able to get through the prompts.
However, after completing the prompts, when I try to send a command the command isn't sent.
Commands
The commands.txt is set up like this:
200,
2,no
2,
The first line (200) is to let the device boot up.
The 2nd and 3rd lines answer 2 different prompts.
Issues
The issues come after these 3 inputs. The code runs and completes. Python prints out each of the commands. So the list is processed by Python. However, I don't think the device is receiving the commands.
In the log, the \n and no are written out, but none of the commands after it are. The commands do show when I use ser.inWaiting()
When I access the device through putty and run the commands through the console, everything works as expected.
Why aren't the commands going through?
Small update:
I read somewhere that python may be sending the commands to quickly, so I tried sending the commands 1 char at a time with a .01 delay.
It still didn't work:
for i in lines[1]:
cmd = i
encoded_cmd = cmd.encode("utf-8")
ser.write(encoded_cmd)
sleep(0.1)
print(cmd)
Code
import serial
import time
from time import sleep
from datetime import datetime
# create list of commands
with open('commands.txt') as commands:
list_of_commands = [tuple(map(str, i.split(','))) for i in commands]
# open and name log file
date = datetime.now().strftime("%Y-%m-%d")
log = open(f'{date}.txt', 'w+')
# serial configuration
info = open('info.txt', 'r')
lines = info.readlines()
port = lines[0].strip('\n')
baud = int(lines[1].strip('\n'))
try:
# open port
ser = serial.Serial(port=port, baudrate=baud, timeout=5, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, write_timeout=0)
except ConnectionError:
log.write(''.join('There was a connection error'))
else:
# run commands
x = 0
for lines in list_of_commands:
ser.close()
ser.open()
sleep(2)
cmd = lines[1]
encoded_cmd = cmd.encode("utf-8")
sleep_time = int(lines[0])
ser.write(encoded_cmd)
time.perf_counter()
# log output
while 1:
test = ser.readline()
text = test.decode('utf-8')
print(text)
log.write(''.join(text))
print(time.perf_counter())
print(time.perf_counter() - x)
if time.perf_counter() - x > sleep_time:
x = time.perf_counter()
ser.flushInput()
ser.flushOutput()
break
print(cmd)
# close port
ser.close()
# close files
log.close()
From the question it's obvious that multiple issues are intermingled. The same observation comes when reading the code. So I tried to list some of those I struggled with.
Issues
Try-except-else
What is the intention behind try .. except .. else ?
Not sure, its used correctly on purpose here. See try-except-else explained:
The else clause is executed if and only if no exception is raised. This is different from the finally clause that’s always executed.
The serial connection
Why opening and closing inside the loop:
ser.close()
ser.open()
Why the misleading comment:
# close server
ser.close()
Usage of sleep_time
What is the purpose of using the first column sleep_time of your CSV commands.txt inside a conditional break inside you read-loop?
sleep_time = int(lines[0])
Instead the sleep is fix 2 seconds before sending the command:
sleep(2)
How to debug
I would recommend adding some print (or log) statements to
verify the list_of_commands has been read correctly
verify which commands (cmd or even encoded_cmd) have been sent to the serial output
I'm trying to interface with a program (GoGui - https://sourceforge.net/projects/gogui/) using the Go Text Protocol (GTP - http://www.lysator.liu.se/~gunnar/gtp/) which has its documentation here. (Link can also be found on the previous website.)
So I've written a bit of code to at least get GoGui to acknowledge the existence of my program:
import sys
import engine
import input_processing
for line in sys.stdin:
if line == 'name\n':
sys.stdout.write(' Muizz-Bot\n\n')
if line == 'protocol_version\n':
sys.stdout.write('2\n\n')
if line == 'version\n':
sys.stdout.write('')
Now this doesn't seem unreasonable per se, but results in GoGui giving me the following error:
Which is of course a problem. So I figured that I made a mistake somewhere in my programming, but when I simply run the program through visual studio, everything works as expected:
This makes me think that the problem lies in interfacing the two applications, and maybe I should be looking at other functions than stdin and stdout. Does anyone know what may be going wrong here?
EDIT FOR COMMENTS: The code I'm currently working on for command parsing (in its entirety) looks like this:
import sys
commands = ['protocol_version', 'name', 'version', 'list_commands', 'known_command', 'quit', 'boardsize',
'clear_board', 'komi', 'play', 'genmove']
pre_game_out = ['2','Muizz-Bot','']
# Define all output functions
def list_commands():
out = '\n'.join(commands)
return(out)
def known():
return(True)
def quit():
return(None)
def boardsize():
return(None)
def clear_board():
return(None)
def komi():
return(None)
def play():
return(None)
def genmove():
return("A1")
# Create dictionary to point to all functions.
output = {'list_commands':list_commands, 'known_command':known, 'quit':quit, 'boardsize':boardsize,
'clear_board':clear_board, 'komi':komi, 'play':play, 'genmove':genmove}
# Define the function that will pass the commands and write outputs.
def parse(line):
if line.strip() in commands:
i = commands.index(line.strip())
if i<3:
sys.stdout.write('= '+ pre_game_out[i]+'\n\n')
sys.stdout.flush()
else:
sys.stdout.write('= ' + output[line.strip()]() + '\n\n')
sys.stdout.flush()
For pre processing:
def input(inp):
# Remove control characters
inp = inp.replace('\r', '')
inp = inp.replace(' ', '')
inp = inp.split('#', 1)[0]
inp = inp.replace('\t', ' ')
# Check if empty
if inp.isspace() or inp==None or inp=='':
return
else:
return(inp)
You're not flushing your response so nothing gets sent back to the caller (as the command is not big enough to trigger auto buffer flush). Also, strolling through the protocol document it clearly says that your response should be in the form of = response\n\n so even if you were flushing it probably still wouldn't work.
Try with something like:
import sys
for line in sys.stdin:
if line.strip() == 'name':
sys.stdout.write('= Muizz-Bot\n\n')
sys.stdout.flush()
elif line.strip() == 'protocol_version':
sys.stdout.write('= 2\n\n')
sys.stdout.flush()
elif line.strip() == 'version':
sys.stdout.write('=\n\n')
sys.stdout.flush()
You might want to create a simple function for parsing commands / responding back instead of repeating the code, tho. Also, this probably won't (fully) work either as the protocol document states that you need to implement quite a number of commands (6.1 Required Commands) but it should get you started.
UPDATE - Here's one way to make it more manageable and in line with the specs - you can create a function for each command so you can easily add/remove them as you please, for example:
def cmd_name(*args):
return "Muizz-Bot"
def cmd_protocol_version(*args):
return 2
def cmd_version(*args):
return ""
def cmd_list_commands(*args):
return " ".join(x[4:] for x in globals() if x[:4] == "cmd_")
def cmd_known_command(*args):
commands = {x[4:] for x in globals() if x[:4] == "cmd_"}
return "true" if args and args[0] in commands else "false"
# etc.
Here all the command functions are prefixed with "cmd_" (and cmd_list_commands() and cmd_known_command() use that fact to check for the command functions in the global namespace) but you can also move them to a different module and then 'scan' the module instead. With such structure it's very easy to add a new command, for example to add the required quit command all you need is to define it:
def cmd_quit(*args):
raise EOFError() # we'll use EOFError to denote an exit state bellow
Also, we'll deal bellow with the situation when a command needs to return an error - all you need to do from your functions is to raise ValueError("error response") and it will be sent back as an error.
Once you have your set of commands added as functions all you need is to parse the input command, call the right function with the right arguments and print back the response:
def call_command(command):
command = "".join(x for x in command if 31 < ord(x) < 127 or x == "\t") # 3.1.1
command = command.strip() # 3.1.4
if not command: # ... return if there's nothing to do
return
command = command.split() # split to get the [id], cmd, [arg1, arg2, ...] structure
try: # try to convert to int the first slice to check for command ID
command_id = int(command[0])
command_args = command[2:] if len(command) > 2 else [] # args or an empty list
command = command[1] # command name
except ValueError: # failed, no command ID present
command_id = "" # set it to blank
command_args = command[1:] if len(command) > 1 else [] # args or an empty list
command = command[0] # command name
# now, lets try to call our command as cmd_<command name> function and get its response
try:
response = globals()["cmd_" + command](*command_args)
if response != "": # response not empty, prepend it with space as per 3.4
response = " {}".format(response)
sys.stdout.write("={}{}\n\n".format(command_id, response))
except KeyError: # unknown command, return standard error as per 3.6
sys.stdout.write("?{} unknown command\n\n".format(command_id))
except ValueError as e: # the called function raised a ValueError
sys.stdout.write("?{} {}\n\n".format(command_id, e))
except EOFError: # a special case when we need to quit
sys.stdout.write("={}\n\n".format(command_id))
sys.stdout.flush()
sys.exit(0)
sys.stdout.flush() # flush the STDOUT
Finally, all you need is to listen to your STDIN and forward the command lines to this function to do the heavy lifting. In that regard, I'd actually explicitly read line-by-line from your STDIN rather than trying to iterate over it as it's a safer approach so:
if __name__ == "__main__": # make sure we're executing instead of importing this script
while True: # main loop
try:
line = sys.stdin.readline() # read a line from STDIN
if not line: # reached the end of STDIN
break # exit the main loop
call_command(line) # call our command
except Exception: # too broad, but we don't care at this point as we're exiting
break # exit the main loop
Of course, as I mentioned earlier, it might be a better idea to pack your commands in a separate module, but this should at least give you an idea how to do 'separation of concerns' so you worry about responding to your commands rather than on how they get called and how they respond back to the caller.
I'm trying to develop a Python based wrapper around the Xilinx ISE TCL shell xtclsh.exe. If it works, I'll add support for other shells like PlanAhead or Vivado ...
So what's the big picture? I have a list of VHDL source files, which form an IP core. I would like to open an existing ISE project, search for missing VHDL files and add them if necessary. Because IP cores have overlapping file dependencies, it's possible that a project already contains some files, so I'm only looking for missing files.
The example user Python 3.x and subprocess with pipes. The xtclsh.exe is launched and commands are send line by line to the shell. The output is monitored for results. To ease the example, I redirected STDERR to STDOUT. A dummy output POC_BOUNDARY is inserted into the command stream, to indicate completed commands.
The attached example code can be tested by setting up an example ISE project, which has some VHDL source files.
My problem is that INFO, WARNING and ERROR messages are displayed, but the results from the TCL commands can not be read by the script.
Manually executing search *.vhdl -type file in xtclsh.exe results in:
% search *.vhdl -type file
D:/git/PoC/src/common/config.vhdl
D:/git/PoC/src/common/utils.vhdl
D:/git/PoC/src/common/vectors.vhdl
Executing the script results in:
....
press ENTER for the next step
sending 'search *.vhdl -type file'
stdoutLine='POC_BOUNDARY
'
output consumed until boundary string
....
Questions:
Where does xtclsh write to?
How can I read the results from TCL commands?
Btw: The prompt sign % is also not visible to my script.
Python code to reproduce the behavior:
import subprocess
class XilinxTCLShellProcess(object):
# executable = "sortnet_BitonicSort_tb.exe"
executable = r"C:\Xilinx\14.7\ISE_DS\ISE\bin\nt64\xtclsh.exe"
boundarString = "POC_BOUNDARY"
boundarCommand = bytearray("puts {0}\n".format(boundarString), "ascii")
def create(self, arguments):
sysargs = []
sysargs.append(self.executable)
self.proc = subprocess.Popen(sysargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
self.sendBoundardCommand()
while(True):
stdoutLine = self.proc.stdout.readline().decode()
if (self.boundarString in stdoutLine):
break
print("found boundary string")
def terminate(self):
self.proc.terminate()
def sendBoundardCommand(self):
self.proc.stdin.write(self.boundarCommand)
self.proc.stdin.flush()
def sendCommand(self, line):
command = bytearray("{0}\n".format(line), "ascii")
self.proc.stdin.write(command)
self.sendBoundardCommand()
def sendLine(self, line):
self.sendCommand(line)
while(True):
stdoutLine = self.proc.stdout.readline().decode()
print("stdoutLine='{0}'".format(stdoutLine))
if (stdoutLine == ""):
print("reached EOF in stdout")
break
elif ("vhdl" in stdoutLine):
print("found a file name")
elif (self.boundarString in stdoutLine):
print("output consumed until boundary string")
break
def main():
print("creating 'XilinxTCLShellProcess' instance")
xtcl = XilinxTCLShellProcess()
print("launching process")
arguments = []
xtcl.create(arguments)
i = 1
while True:
print("press ENTER for the next step")
from msvcrt import getch
from time import sleep
sleep(0.1) # 0.1 seconds
key = ord(getch())
if key == 27: # ESC
print("aborting")
print("sending 'exit'")
xtcl.sendLine("exit")
break
elif key == 13: # ENTER
if (i == 1):
#print("sending 'project new test.xise'")
#xtcl.sendLine("project new test.xise")
print("sending 'project open PoCTest.xise'")
xtcl.sendLine("project open PoCTest.xise")
i += 1
elif (i == 2):
print("sending 'lib_vhdl get PoC files'")
xtcl.sendLine("lib_vhdl get PoC files")
i += 1
elif (i == 3):
print("sending 'search *.vhdl -type file'")
xtcl.sendLine("search *.vhdl -type file")
i += 1
elif (i == 4):
print("sending 'xfile add ../../src/common/strings.vhdl -lib_vhdl PoC -view ALL'")
xtcl.sendLine("xfile add ../../src/common/strings.vhdl -lib_vhdl PoC -view ALL")
i += 16
elif (i == 20):
print("sending 'project close'")
xtcl.sendLine("project close")
i += 1
elif (i == 21):
print("sending 'exit'")
xtcl.sendCommand("exit")
break
print("exit main()")
xtcl.terminate()
print("the end!")
# entry point
if __name__ == "__main__":
main()
I have tried several approaches on Linux, but it seemes that xtclsh detects whether standard input is connected to a pipe or a (pseudo) terminal. If it is connected to a pipe, xtclsh suppresses any output which would be normally written to standard output (prompt output, command results). I think, the same applies to Windows.
Messages (whether informative, warning or error) which are printed on standard error still go there even if the input is connected to a pipe.
To get the messages printed on standard output you can use the puts tcl command which always prints on standard output. That is, puts [command] takes the standard output of command and prints it always to standard output.
Example: Let's assume we have a test.xise project with two files: the top-level entity in test.vhd and the testbench in test_tb.vhd. And, we want to list all files in the project using this tcl script (commands.tcl):
puts [project open test]
puts "-----------------------------------------------------------------------"
puts [search *.vhd]
exit
Then the call xtclsh < commands.tcl 2> error.log prints this on standard output:
test
-----------------------------------------------------------------------
/home/zabel/tmp/test/test.vhd
/home/zabel/tmp/test/test_tb.vhd
And this is printed on standard error (into file error.log):
INFO:HDLCompiler:1061 - Parsing VHDL file "/home/zabel/tmp/test/test.vhd" into
library work
INFO:ProjectMgmt - Parsing design hierarchy completed successfully.
I have a Python routine which invokes some kind of CLI (e.g telnet) and then executes commands in it. The problem is that sometimes the CLI refuses connection and commands are executed in the host shell resulting in various errors. My idea is to check whether the shell prompt alters or not after invoking the CLI.
The question is: how can I get the shell prompt string in Python?
Echoing PS1 is not a solution, because some CLIs cannot run it and it returns a notation-like string instead of the actual prompt:
SC-2-1:~ # echo $PS1
\[\]\h:\w # \[\]
EDIT
My routine:
def run_cli_command(self, ssh, cli, commands, timeout = 10):
''' Sends one or more commands to some cli and returns answer. '''
try:
channel = ssh.invoke_shell()
channel.settimeout(timeout)
channel.send('%s\n' % (cli))
if 'telnet' in cli:
time.sleep(1)
time.sleep(1)
# I need to check the prompt here
w = 0
while (channel.recv_ready() == False) and (w < timeout):
w += 1
time.sleep(1)
channel.recv(9999)
if type(commands) is not list:
commands = [commands]
ret = ''
for command in commands:
channel.send("%s\r\n" % (command))
w = 0
while (channel.recv_ready() == False) and (w < timeout):
w += 1
time.sleep(1)
ret += channel.recv(9999) ### The size of read buffer can be a bottleneck...
except Exception, e:
#print str(e) ### for debugging
return None
channel.close()
return ret
Some explanation needs here: the ssh parameter is a paramiko.SSHClient() instance. I use this code to login to a server and from there I call another CLI which can be SSH, telnet, etc.
I’d suggest sending commands that alter PS1 to a known string. I’ve done so when I used Oracle sqlplus from a Korn shell script, as coprocess, to know when to end reading data / output from the last statement I issued. So basically, you’d send:
PS1='end1>'; command1
Then you’d read lines until you see "end1>" (for extra easiness, add a newline at the end of PS1).