paramiko print exec_command result immediately - python

I know exec_command returns 3 streams, but I'm unable to print the output as it should arrive to these streams.
client = SSHClient()
client.load_system_host_keys()
client.connect('ssh.example.com')
stdin, stdout, stderr = client.exec_command('ls -l; sleep 10; ls -l')
for line in stdout.readlines():
print line
for line in stderr.readlines():
print line
This seems to output new lines only at the end when the entire command has been finished. I'd like to receive the characters as they are generated by the remote commands. Is this possible?

To read the output on your terminal, the method i knew is to stream the readbinaries and writebinaries straight from the ssh session into your terminal
import paramiko
import os
import sys
host_address = ''
USER = ''
PASSWORD = ''
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(host_address, user=USER, password=PASSWORD)
#Time to invoke the shell from paramiko support
client_shell = client.INVOKE_SHELL()
standard_input = client_shell.makefile('wb')
standard_output = client_shell.makefile('rb')
standard_input.write('''
ls -l
sleep 10
ls -l
''')
print standard_output.read()
#Close all the streams and ssh connection in the end to flush the session
standard_input.close()
standard_output.close()
client.close()

Related

python; create file on remote server with variable file name

Using the paramiko library in python im trying to touch a file on remote location and able to do so using below code..
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=servername,username=usr,password=pass)
cmd = "touch /home/test/file.txt"
stdin, stdout, stderr = ssh_client.exec_command(cmd)
stdout = stdout.readlines()
stderr = stderr.readlines()
however, as next step the file im trying to create on remote having name depends on a variable.. so it should be like
cmd = "touch /home/test/var_file.txt"
where var could be anything a variable..
i have tried server options to create that file but not working..
can anyone guide on how to achieve it..
This should work:
import os
import paramiko
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=servername,username=usr,password=pass)
name_of_file = "Test.txt"
path_to_file = os.path.join('/home/test/', name_of_file)
cmd = f"touch {path_to_file}"
stdin, stdout, stderr = ssh_client.exec_command(cmd)
stdout = stdout.readlines()
stderr = stderr.readlines()

python paramiko ssh client then ssh to another server again [duplicate]

I'm rewriting a Bash script I wrote into Python. The crux of that script was
ssh -t first.com "ssh second.com very_remote_command"
I'm having a problem with the nested authentication with paramiko. I wasn't able to find any examples dealing with my precise situation, but I was able to find examples with sudo on a remote host.
The first method writes to stdin
ssh.connect('127.0.0.1', username='jesse', password='lol')
stdin, stdout, stderr = ssh.exec_command("sudo dmesg")
stdin.write('lol\n')
stdin.flush()
The second creates a channel and uses the socket-like send and recv.
I was able to get stdin.write to work with sudo, but it doesn't work with ssh on the remote host.
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('first.com', username='luser', password='secret')
stdin, stdout, stderr = ssh.exec_command('ssh luser#second.com')
stdin.write('secret')
stdin.flush()
print '---- out ----'
print stdout.readlines()
print '---- error ----'
print stderr.readlines()
ssh.close()
...prints...
---- out ----
[]
---- error ----
['Pseudo-terminal will not be allocated because stdin is not a terminal.\r\n', 'Permission denied, please try again.\r\n', 'Permission denied, please try again.\r\n', 'Permission denied (publickey,password,keyboard-interactive).\r\n']
The pseudo-terminal error reminded me of the -t flag in my original command, so I switched to the second method, using a Channel. Instead of ssh.exec_command and later, I have:
t = ssh.get_transport()
chan = t.open_session()
chan.get_pty()
print '---- send ssh cmd ----'
print chan.send('ssh luser#second.com')
print '---- recv ----'
print chan.recv(9999)
chan = t.open_session()
print '---- send password ----'
print chan.send('secret')
print '---- recv ----'
print chan.recv(9999)
...but it prints '---- send ssh cmd ----' and just hangs until I kill the process.
I'm new to Python and none too knowledgeable about networks. In the first case, why does sending the password work with sudo but not with ssh? Are the prompts different? Is paramiko even the right library for this?
I managed to find a solution, but it requires a little manual work. If anyone have a better solution, please tell me.
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('first.com', username='luser', password='secret')
chan = ssh.invoke_shell()
# Ssh and wait for the password prompt.
chan.send('ssh second.com\n')
buff = ''
while not buff.endswith('\'s password: '):
resp = chan.recv(9999)
buff += resp
# Send the password and wait for a prompt.
chan.send('secret\n')
buff = ''
while not buff.endswith('some-prompt$ '):
resp = chan.recv(9999)
buff += resp
# Execute whatever command and wait for a prompt again.
chan.send('ls\n')
buff = ''
while not buff.endswith('some-prompt$ '):
resp = chan.recv(9999)
buff += resp
# Now buff has the data I need.
print 'buff', buff
ssh.close()
The thing to note is that instead of this
t = ssh.get_transport()
chan = t.open_session()
chan.get_pty()
...you want this
chan = ssh.invoke_shell()
It reminds me of when I tried to write a TradeWars script when I was a kid and gave up coding for ten years. :)
Here is a small example using paramiko only (and port forwarding):
import paramiko as ssh
class SSHTool():
def __init__(self, host, user, auth,
via=None, via_user=None, via_auth=None):
if via:
t0 = ssh.Transport(via)
t0.start_client()
t0.auth_password(via_user, via_auth)
# setup forwarding from 127.0.0.1:<free_random_port> to |host|
channel = t0.open_channel('direct-tcpip', host, ('127.0.0.1', 0))
self.transport = ssh.Transport(channel)
else:
self.transport = ssh.Transport(host)
self.transport.start_client()
self.transport.auth_password(user, auth)
def run(self, cmd):
ch = self.transport.open_session()
ch.set_combine_stderr(True)
ch.exec_command(cmd)
retcode = ch.recv_exit_status()
buf = ''
while ch.recv_ready():
buf += ch.recv(1024)
return (buf, retcode)
# The example below is equivalent to
# $ ssh 10.10.10.10 ssh 192.168.1.1 uname -a
# The code above works as if these 2 commands were executed:
# $ ssh -L <free_random_port>:192.168.1.1:22 10.10.10.10
# $ ssh 127.0.0.1:<free_random_port> uname -a
host = ('192.168.1.1', 22)
via_host = ('10.10.10.10', 22)
ssht = SSHTool(host, 'user1', 'pass1',
via=via_host, via_user='user2', via_auth='pass2')
print ssht.run('uname -a')
You can create ssh connection using channel from another ssh connection. See here for more detail.
For a ready made solution check out pxssh from the pxpect project. Look at the sshls.py and ssh_tunnel.py examples.
http://www.noah.org/wiki/Pexpect
Sinas's answer works well but didn't provide all the output from very long commands for me. However, using chan.makefile() allows me to retrieve all the output.
The below works on a system that requires tty and also prompts for sudo password
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.WarningPolicy())
ssh.connect("10.10.10.1", 22, "user", "password")
chan=ssh.get_transport().open_session()
chan.get_pty()
f = chan.makefile()
chan.exec_command("sudo dmesg")
chan.send("password\n")
print f.read()
ssh.close()

how to execute multiple lines using paramiko and read only their output

How do I execute multiple commands using Paramiko and read the output back into my python script?
This question is theoretically answered here How do you execute multiple commands in a single session in Paramiko? (Python), but in my view that answer is incorrect.
The problem is that when you read the stdout, it reads the entire content of the terminal including the program that you "typed" into the terminal.
Just try it (this is basically a copy paste from the above thread):
import paramiko
machine = "you machine ip"
username = "you username"
password = "password"
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(machine, username = username, password = password)
channel = client.invoke_shell()
stdin = channel.makefile('wb')
stdout = channel.makefile('rb')
stdin.write('''
cd tmp
ls
exit
''')
print stdout.read()
stdout.close()
stdin.close()
client.close()
So my question is, how do I execute multiple commands and read only the output of those commands, rather than the input I "typed" and the output?
Thanks in advance for your kind help and time.
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
target_host = 'x.x.x.x'
target_port = 22
target_port = 22
pwd = ':)'
un = 'root'
ssh.connect( hostname = target_host , username = un, password =pwd)
#Now exeute multiple commands seperated by semicolon
stdin, stdout, stderr = ssh.exec_command('cd mydir;ls')
print stdout.readlines()
You're seeing the commands you typed because the shell echoes them back. You can turn this off by running
stty -echo
before your other commands.
Another approach is to not invoke an interactive shell, but just run the commands directly, unless there's some other reason you especially need an interactive shell. For instance you could say
client.exec_command('/bin/sh -c "cd /tmp && ls")
If you want a shell but without a pty, you can try
client.exec_command('/bin/sh')
and I think that will suppress the echo too.
This should work:
# Explicitly provide key, ip address and username
from paramiko import SSHClient, AutoAddPolicy
result = []
def ssh_conn():
client = SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(AutoAddPolicy())
client.connect('<host_IP_address>', username='<your_host_username>', key_filename='<private_key_location>')
stdin, stdout, stderr = client.exec_command('ls -la')
for each_line in stdout:
result.append(each_line.strip('\n'))
client.close()
ssh_conn()
for each_line in result:
print(each_line.strip())

Paramiko: read from standard output of remotely executed command

so I was working with paramiko for some basic SSH testing and I'm not getting any output into stdout. Heres my code.
import paramiko
client=paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
com="ls ~/desktop"
client.connect('MyIPAddress',MyPortNumber, username='username', password='password')
output=""
stdin, stdout, stderr = client.exec_command(com)
print "ssh succuessful. Closing connection"
client.close()
print "Connection closed"
stdout=stdout.readlines()
print stdout
print com
for line in stdout:
output=output+line
if output!="":
print output
else:
print "There was no output for this command"
So whenever I run this, the command is executed (as seen by if I do something like a cp, the file is copied), but I always get "There was no output for this command". When stdout=stdout.readlines() is printed, [] is the output. In addition, if I add a print statement into the for loop, it never gets run. Could someone help me out here? Thanks!
You have closed the connection before reading lines:
import paramiko
client=paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
com="ls ~/desktop"
client.connect('MyIPAddress',MyPortNumber, username='username', password='password')
output=""
stdin, stdout, stderr = client.exec_command(com)
print "ssh succuessful. Closing connection"
stdout=stdout.readlines()
client.close()
print "Connection closed"
print stdout
print com
for line in stdout:
output=output+line
if output!="":
print output
else:
print "There was no output for this command"
The code in the accepted answer may hang, if the command produces also an error output. See Paramiko ssh die/hang with big output.
An easy solution, if you do not mind merging stdout and stderr, is to combine them into one stream using Channel.set_combine_stderr:
stdin, stdout, stderr = client.exec_command(command)
stdout.channel.set_combine_stderr(True)
output = stdout.readlines()
If you need to read the outputs separately, see Run multiple commands in different SSH servers in parallel using Python Paramiko.
*Interactive example :
====Part 1, this show the sh output in server ,at the end of is ">"
need some input to continual or exit ======
selilsosx045:uecontrol-CXC_173_6456-R32A01 lteue$ ./uecontrol.sh -host localhost
UE Control:Start UE control using:
UE Control:java -Dlogdir= -Duecontrol.configdir=./etc -jar ./server/server-R32A01.jar -host localhost
Loading properties from file /Users/lteue/Downloads/uecontrol-CXC_173_6456-R32A01/etc/uecontrol.properties
Starting remote CLI towards host localhost
Enter the command Q to exit the CLI, or the command HELP
to get information on available commands.
The CLI is ready for input.
uec>
===========Pyhton code with peramiko ============*
Try below method : while not stdout.channel.exit_status_ready():
def shCommand(server_list):
server_IP = server_list[0]
username = server_list[1]
password = server_list[2]
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(server_IP,22,username, password)strong text
commandList = ['list \n']
alldata = getUeInfo(ssh,commandList)
ssh.close()
def getUeInfo(ssh,commandList):
data_buffer = ""
num_of_input = 0
stdin, stdout, stderr = ssh.exec_command('cmd')
while not stdout.channel.exit_status_ready():
solo_line = ""
if stdout.channel.recv_ready():
solo_line = stdout.channel.recv(1024) # Retrieve the first 1024 bytes
data_buffer += solo_line
if(cmp(solo_line,'uec> ') ==0 ): #len of solo should be 5 ,
if num_of_input == 0 :
data_buffer = ""
for cmd in commandList :
#print cmd
stdin.channel.send(cmd)
num_of_input += 1
if num_of_input == 1 :
stdin.channel.send('q \n')
num_of_input += 1
return data_buffer
As #jabaldonedo said you closed your SSHClient connection before reading the stdout. SSHClient can be used as a context manager. Using the SSHClient as a context manager helps prevent you from trying to access stdout and stderr after the ssh connection is closed. The resulting code in Python3 syntax looks like:
from paramiko import AutoAddPolicy, SSHClient
with SSHClient() as client:
client.set_missing_host_key_policy(AutoAddPolicy)
client.connect(
'MyIPAddress',
MyPortNumber,
username='username',
password='password'
)
com = "ls ~/desktop"
stdin, stdout, stderr = client.exec_command(com)
output = ''
for line in stdout.readlines()
output += line
if output:
print(output)
else:
print("There was no output for this command")
# Program to print the output in console/interpreter/shell in runtime without using stdout.
import paramiko
import xlrd
import time
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
loc = ('/Users/harshgow/Documents/PYTHON_WORK/labcred.xlsx')
wo = xlrd.open_workbook(loc)
sheet = wo.sheet_by_index(0)
Host = sheet.cell_value(0, 1)
Port = int(sheet.cell_value(3, 1))
User = sheet.cell_value(1, 1)
Pass = sheet.cell_value(2, 1)
def details(Host, Port, User, Pass):
time.sleep(2)
ssh.connect(Host, Port, User, Pass)
print('connected to ip ', Host)
stdin = ssh.exec_command("")
remote_conn = ssh.invoke_shell()
print("Interactive SSH session established")
output = remote_conn.recv(1000)
remote_conn.send("\n")
remote_conn.send("xstatus Cameras\n")
time.sleep(5)
output = remote_conn.recv(10000)
print(output)
details(Host, Port, User, Pass)

How do you execute multiple commands in a single session in Paramiko? (Python)

def exec_command(self, command, bufsize=-1):
#print "Executing Command: "+command
chan = self._transport.open_session()
chan.exec_command(command)
stdin = chan.makefile('wb', bufsize)
stdout = chan.makefile('rb', bufsize)
stderr = chan.makefile_stderr('rb', bufsize)
return stdin, stdout, stderr
When executing a command in paramiko, it always resets the session when you run exec_command.
I want to able to execute sudo or su and still have those privileges when I run another exec_command.
Another example would be trying to exec_command("cd /") and then run exec_command again and have it be in the root directory. I know you can do something like exec_command("cd /; ls -l"), but I need to do it in separate function calls.
Non-Interactive use cases
This is a non-interactive example... it sends cd tmp, ls and then exit.
import sys
sys.stderr = open('/dev/null') # Silence silly warnings from paramiko
import paramiko as pm
sys.stderr = sys.__stderr__
import os
class AllowAllKeys(pm.MissingHostKeyPolicy):
def missing_host_key(self, client, hostname, key):
return
HOST = '127.0.0.1'
USER = ''
PASSWORD = ''
client = pm.SSHClient()
client.load_system_host_keys()
client.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
client.set_missing_host_key_policy(AllowAllKeys())
client.connect(HOST, username=USER, password=PASSWORD)
channel = client.invoke_shell()
stdin = channel.makefile('wb')
stdout = channel.makefile('rb')
stdin.write('''
cd tmp
ls
exit
''')
print stdout.read()
stdout.close()
stdin.close()
client.close()
Interactive use cases
If you have an interactive ssh use case, paramiko can handle it... I personally would drive interactive ssh sessions with scrapli.
Listing all the ways I can think of to use paramiko interactively:
See nagabhushan's answer which uses paramiko interactively
By default, ansible paramiko usage is configurable
By default, exscript ssh uses paramiko
By default, netmiko ssh uses paramiko
By default, scrapli ssh uses paramiko
I might have missed some libraries that use paramiko, but it should be clear that paramiko is used quite extensively by python libraries that control ssh sessions.
Try creating a command string separated by \n character. It worked for me.
For. e.g. ssh.exec_command("command_1 \n command_2 \n command_3")
Strictly speaking, you can't. According to the ssh spec:
A session is a remote execution of a program. The program may be a
shell, an application, a system command, or some built-in subsystem.
This means that, once the command has executed, the session is finished. You cannot execute multiple commands in one session. What you CAN do, however, is starting a remote shell (== one command), and interact with that shell through stdin etc... (think of executing a python script vs. running the interactive interpreter)
You can do that by invoking shell on the client and sending commands. Please refer here
The page has code for python 3.5. I have modified the code a bit to work for pythin 2.7. Adding code here for reference
import threading, paramiko
strdata=''
fulldata=''
class ssh:
shell = None
client = None
transport = None
def __init__(self, address, username, password):
print("Connecting to server on ip", str(address) + ".")
self.client = paramiko.client.SSHClient()
self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
self.client.connect(address, username=username, password=password, look_for_keys=False)
self.transport = paramiko.Transport((address, 22))
self.transport.connect(username=username, password=password)
thread = threading.Thread(target=self.process)
thread.daemon = True
thread.start()
def close_connection(self):
if(self.client != None):
self.client.close()
self.transport.close()
def open_shell(self):
self.shell = self.client.invoke_shell()
def send_shell(self, command):
if(self.shell):
self.shell.send(command + "\n")
else:
print("Shell not opened.")
def process(self):
global strdata, fulldata
while True:
# Print data when available
if self.shell is not None and self.shell.recv_ready():
alldata = self.shell.recv(1024)
while self.shell.recv_ready():
alldata += self.shell.recv(1024)
strdata = strdata + str(alldata)
fulldata = fulldata + str(alldata)
strdata = self.print_lines(strdata) # print all received data except last line
def print_lines(self, data):
last_line = data
if '\n' in data:
lines = data.splitlines()
for i in range(0, len(lines)-1):
print(lines[i])
last_line = lines[len(lines) - 1]
if data.endswith('\n'):
print(last_line)
last_line = ''
return last_line
sshUsername = "SSH USERNAME"
sshPassword = "SSH PASSWORD"
sshServer = "SSH SERVER ADDRESS"
connection = ssh(sshServer, sshUsername, sshPassword)
connection.open_shell()
connection.send_shell('cmd1')
connection.send_shell('cmd2')
connection.send_shell('cmd3')
time.sleep(10)
print(strdata) # print the last line of received data
print('==========================')
print(fulldata) # This contains the complete data received.
print('==========================')
connection.close_connection()
cmd = 'ls /home/dir'
self.ssh_stdin, self.ssh_stdout, self.ssh_stderr = self.ssh.exec_command(cmd)
print self.ssh_stdout.read()
cmd2 = 'cat /home/dir/test.log'
self.ssh_stdin2, self.ssh_stdout2, self.ssh_stderr2 = self.ssh.exec_command(cmd2)
print self.ssh_stdout2.read()
You can run multiple command using the below technique. Use semicolon to separate the Linux commands
Eg:
chan.exec_command("date;ls;free -m")
If you wish each command to have an effect on the next command you should use:
stdin, stdout, stderr = client.exec_command("command1;command2;command3")
but in some cases, I found that when ";" doesn't work, using "&&" does work.
stdin, stdout, stderr = client.exec_command("command1 && command2 && command3")
You can execute an entire BASH script file for better use, here is the code for that:
import paramiko
hostname = "192.168.1.101"
username = "test"
password = "abc123"
# initialize the SSH client
client = paramiko.SSHClient()
# add to known hosts
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(hostname=hostname, username=username, password=password)
except:
print("[!] Cannot connect to the SSH Server")
exit()
# read the BASH script content from the file
bash_script = open("script.sh").read()
# execute the BASH script
stdin, stdout, stderr = client.exec_command(bash_script)
# read the standard output and print it
print(stdout.read().decode())
# print errors if there are any
err = stderr.read().decode()
if err:
print(err)
# close the connection
client.close()
This will execute the local script.sh file on the remote 192.168.1.101 Linux machine.
script.sh (just an example):
cd Desktop
mkdir test_folder
cd test_folder
echo "$PATH" > path.txt
This tutorial explains this in detail: How to Execute BASH Commands in a Remote Machine in Python.

Categories

Resources