Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
The only simple way to do SSH in Python today is to use subprocess + OpenSSH...
#!/usr/bin/python
# All SSH libraries for Python are junk (2011-10-13).
# Too low-level (libssh2), too buggy (paramiko), too complicated
# (both), too poor in features (no use of the agent, for instance)
# Here is the right solution today:
import subprocess
import sys
HOST="www.example.org"
# Ports are handled in ~/.ssh/config since we use OpenSSH
COMMAND="uname -a"
ssh = subprocess.Popen(["ssh", "%s" % HOST, COMMAND],
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
result = ssh.stdout.readlines()
if result == []:
error = ssh.stderr.readlines()
print >>sys.stderr, "ERROR: %s" % error
else:
print result
@aayoubi

This comment has been minimized.

Copy link

@aayoubi aayoubi commented Nov 20, 2013

What happens if the ssh connection hangs ? will it hang ?

@gbromios

This comment has been minimized.

Copy link

@gbromios gbromios commented Dec 6, 2013

I'm glad I found this right away haha

@itsmeremz

This comment has been minimized.

Copy link

@itsmeremz itsmeremz commented Feb 7, 2014

How would you input a list of hostnames instead of HOST="www.example.org" and use subprocess to do some task on all those hosts?

@jeffcjohnson

This comment has been minimized.

Copy link

@jeffcjohnson jeffcjohnson commented May 2, 2014

Thanks for this!

@taigrr

This comment has been minimized.

Copy link

@taigrr taigrr commented Mar 28, 2015

How would you specify a password so it automatically logs in?

@romain-dartigues

This comment has been minimized.

Copy link

@romain-dartigues romain-dartigues commented Apr 2, 2015

@taigrr use SSH keys; see ssh(1) and ssh-copy-id(1)

@CTimmerman

This comment has been minimized.

Copy link

@CTimmerman CTimmerman commented Jun 4, 2015

I use remote_output = sub.check_output(['ssh', 'company.com', 'ps', 'u', '-U', 'tomcat6'], stderr=sub.STDOUT)

@uvaizm

This comment has been minimized.

Copy link

@uvaizm uvaizm commented Jul 22, 2015

Thank you very much!

@ghost

This comment has been minimized.

Copy link

@ghost ghost commented Sep 3, 2015

@itsmeremz, have you found a solution? list instead of individual name?

@ghost

This comment has been minimized.

Copy link

@ghost ghost commented Sep 3, 2015

also, how would you specify id and password?
Thanks.

@frodopwns

This comment has been minimized.

Copy link

@frodopwns frodopwns commented Oct 13, 2015

@sendalot

You can't send a password to ssh without using something like sshpass. Normally people handle this by using public/private keypairs. ie. you create a public key and push it to the remote host's authorized_keys file.

That being said you can use the fabric library for this (put this in a fabfile.py):

import os
from fabric.api import *

HOST="www.example.org"
COMMAND="uname -a"

env.user = os.getenv('SSH_USER', 'vagrant')
env.password = os.getenv('SSH_PASSWORD', 'vagrant')

@hosts(HOST)
def do_something():
    run(COMMAND)

froom cli (in directory where fabfile.py exists)

fab do_something
@pkittenis

This comment has been minimized.

Copy link

@pkittenis pkittenis commented Dec 23, 2015

from pssh.pssh_client import ParallelSSHClient
hosts = ['myhost1', 'myhost2']
client = ParallelSSHClient(hosts)
output = client.run_command('ls -ltrh /tmp/', sudo=True)
print output

{'myhost1': {'exit_code': 0, 'stdout': <generator>, 'stderr': <generator>, 'channel': <channel>, 'cmd' : <greenlet>, 'exception' : None},
 'myhost2': {'exit_code': 0, 'stdout': <generator>, 'stderr': <generator>, 'channel': <channel>, 'cmd' : <greenlet>, 'exception' : None}}

From the parallel-ssh project's front page.

@ksingh7

This comment has been minimized.

Copy link

@ksingh7 ksingh7 commented Jan 23, 2016

Thank you , your code was really helpful.

cheers

@HouseMommy

This comment has been minimized.

Copy link

@HouseMommy HouseMommy commented Jun 3, 2016

Thank you, your code has helped me as a baseline

@mcculloh

This comment has been minimized.

Copy link

@mcculloh mcculloh commented Jun 4, 2016

Why the % operator here?

ssh = subprocess.Popen(["ssh", "%s" % HOST, COMMAND],

instead just passing the param is fine

ssh = subprocess.Popen(["ssh", HOST, COMMAND],
@JulienPalard

This comment has been minimized.

Copy link

@JulienPalard JulienPalard commented Jun 16, 2016

This WILL hang if ssh multiplexing is used, sadly :(

@llazzaro

This comment has been minimized.

Copy link

@llazzaro llazzaro commented Jun 25, 2016

try paramiko

@alfonso1003

This comment has been minimized.

Copy link

@alfonso1003 alfonso1003 commented Sep 7, 2016

What does this line do?
print >>sys.stderr, "ERROR: %s" % error

Specifically, I'm stumped by >>. You can't google those symbols unfortunately... I know you can use >> and << for bit shifting, but what's going on in this case? Thanks.

@geoffreybrown

This comment has been minimized.

Copy link

@geoffreybrown geoffreybrown commented Sep 9, 2016

print >> or "print chevron" is part of the python 2.x syntax for the print statement. The first argument is a file-like object (sys.stderr in this case) and the second a string expression to be written to the file-like object.

@alfonso1003

This comment has been minimized.

Copy link

@alfonso1003 alfonso1003 commented Sep 10, 2016

@geoffreybrown, thanks that makes sense. And I can now do a web search on "python print chevron" to get more details! The tricky part of this was that you can't easily do a web search on >>.

@sameertikoo

This comment has been minimized.

Copy link

@sameertikoo sameertikoo commented Oct 4, 2016

I want to run this command "unlock_user"
But its creating the command string as "unlock-user\n', '\r\n'"

@nicktimko

This comment has been minimized.

Copy link

@nicktimko nicktimko commented Oct 25, 2016

a from __future__ import print_function and print("ERROR: %s" % error, file=sys.stderr) would probably be preferable.

@sprive

This comment has been minimized.

Copy link

@sprive sprive commented Nov 11, 2016

Nonsense. Paramiko is not especially buggy. I have been using it for years. The documentation could use some help, and more examples, but it works great!

If you just need to run a single SSH call, sure -- just wrap SSH command in subprocess.

But if you need to execute a suite of SSH commands, using a variety of keys and users -- like if you are doing QA integration testing -- then I would bite the bullet and use Paramiko (or even easier, I hear, is Fabric)

@atsatosh

This comment has been minimized.

Copy link

@atsatosh atsatosh commented Jan 8, 2017

I don't want to come out from the remote machine. Can somebody please help?
Thanks,
Santosh

@ghost

This comment has been minimized.

Copy link

@ghost ghost commented Apr 3, 2017

Im just a bit confused about

if result == []:

What is the if condition here ? What does it mean ?

@dmpaul26

This comment has been minimized.

Copy link

@dmpaul26 dmpaul26 commented Apr 6, 2017

@jeunii it looks like it's checking if the stdout returned is empty (if the command didn't return any output)

@Kossover-gal

This comment has been minimized.

Copy link

@Kossover-gal Kossover-gal commented Apr 27, 2017

Hi,

I'm trying to use subprocess.call() from AWS lambda and I'm having problems.

My cmd_to_execute contain 'sudo'.

This is my piece of code:

call(['ssh', '-i', '/tmp/keyname.pem', 'user@hostIP','sudo python3.5 /home/ubuntu/code.py'], shell=True)

Someone has an idea to make it succeed?

Thanks!

@MrMino

This comment has been minimized.

Copy link

@MrMino MrMino commented May 19, 2017

Paramiko is not that buggy compared to the level of fuss required to get this to work.
Try doing something like nesting a connection using netcat, forwarding through direct-tcpip, etc. Even a simple key/password authentication becomes a nightmare. Also, this script assumes that there is an ssh binary present. Which is not true 90% of the times (windows users, embeded systems, stripped down versions of Unix).

@cacm1975

This comment has been minimized.

Copy link

@cacm1975 cacm1975 commented Jun 28, 2017

the script does work well for the most of command using SSH, however for some it fails since it recognizes as error when the banner is displayed as you can see below. Does anybody know how to fix that? Thanks.
blabla@blabla00623:blabla/users/carlos/scripts> ./test_ssh.py
ERROR: ['\n', ' +----------------------------------------------------------------------------+\n', ' | WARNING |\n', ' | The programs and data stored on this system are licensed to, or are |\n', ' | private property of, blabla and are lawfully available only to authorized |\n', ' | users for approved purposes. Unauthorized access to any program or data |\n', ' | on this system is not permitted, and any unauthorized access beyond this |\n', ' | point may lead to prosecution and/or disciplinary action. |\n', ' | This system may be monitored at any time for operational reasons. ]

@rwind84

This comment has been minimized.

Copy link

@rwind84 rwind84 commented Nov 18, 2017

Hello All, I am completely out of scripting and programming background, so apologize for throwing a dum question here.
I am on Python 2.7.10. I wanted to have a .py script for ssh'ing into multiple remote servers.
My requirement. I wanted to establish the below using

ssh -i (KEY) USERNAME@SERVERIP -p (PORTNUMBER)
after ssh'ing I want auth the passphrase for the Key.

Can someone help me, please?

@pkittenis

This comment has been minimized.

Copy link

@pkittenis pkittenis commented May 25, 2018

Hello from the future.

For the benefit of others that are seeing this post 7 years after it was written, there are now alternatives that are not junk.

parallel-ssh is a high-level library that builds on a new libssh2 wrapper, ssh2-python, making it easy to use.

The motivation for writing it was exactly as the OP had found all those years ago - all existing clients were junk. In addition, no clients were available that could feasibly scale to multiple hosts (tens, hundreds, thousands).

from pssh.clients import ParallelSSHClient

client = ParallelSSHClient(['www.example.org'])
output = client.run_command('uname -a')

for host, host_out in output.items():
    for line in host_out.stdout:
        print line

There is also a single host client (pssh.clients.SSHClient) for cases where concurrency is not needed.

See documentation for more examples. Connection retries, exceptions, SFTP, SCP, tunneling/proxying, ssh agents et al are all supported.

Disclaimer - author of both.

@d383746

This comment has been minimized.

Copy link

@d383746 d383746 commented Jul 29, 2018

the script does work well for the most of command using SSH, however for some it fails since it recognizes as error when the banner is displayed as you can see below. Does anybody know how to fix that? Thanks.

@cacm1975. Suppress the banner via ssh. Try to use '-q' option.

@girishlc

This comment has been minimized.

Copy link

@girishlc girishlc commented Sep 19, 2018

Not working for me!

Traceback (most recent call last):
File "C:/Users/girishlc/AppData/Local/Programs/Python/Python35-32/ssh7.py", line 11, in
stderr=subprocess.PIPE)
File "C:\Users\girishlc\AppData\Local\Programs\Python\Python35-32\lib\subprocess.py", line 947, in init
restore_signals, start_new_session)
File "C:\Users\girishlc\AppData\Local\Programs\Python\Python35-32\lib\subprocess.py", line 1224, in _execute_child
startupinfo)
FileNotFoundError: [WinError 2] The system cannot find the file specified

@josephcoombe

This comment has been minimized.

Copy link

@josephcoombe josephcoombe commented Sep 20, 2018

@girishlc
Try this version:

from __future__ import print_function
import os
import subprocess
import platform

PRIVATE_KEY_LOCATION = "C:/Users/johndoe/.ssh/id_rsa"
USER = "johndoe"
HOST = "192.168.1.1"
COMMAND="uname -a"
# Ports are handled in ~/.ssh/config since we use OpenSSH

system32 = os.path.join(os.environ['SystemRoot'], 'SysNative' if platform.architecture()[0] == '32bit' else 'System32')
ssh_path = os.path.join(system32, 'OpenSSH/ssh.exe')

ssh = subprocess.Popen([ssh_path, '-i', PRIVATE_KEY_LOCATION, "{}@{}".format(USER, HOST)],
                       stdin=subprocess.PIPE,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE)

std_data = ssh.communicate(COMMAND)

I ran into this problem as well.

See:
https://bugs.python.org/issue8557
https://stackoverflow.com/questions/41630224/python-does-not-find-system32

@jmp8600

This comment has been minimized.

Copy link

@jmp8600 jmp8600 commented Oct 2, 2018

i am using py script like below:

import subprocess
import sys

HOST="root@192.168.1.25"
COMMAND="ls / -ltrh"

ssh = subprocess.Popen(["ssh", "%s" % HOST, COMMAND],
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
result = ssh.stdout.readlines()
if result == []:
error = ssh.stderr.readlines()
print >>sys.stderr, "ERROR: %s" % error
else:
print(result)

the print command prints output but evenything is inside bracket and bunch on newline,tabs and other characters...How do i properly format this output? I tried result.decode('utf-8') but i get an error that decode is not available...any idea?

[b'total 20K\n', b'drwxr-xr-x. 2 root root 6 Apr 11 00:59 srv\n', b'drwxr-xr-x. 2 root root 6 Apr 11 00:59 mnt\n', b'drwxr-xr-x. 2 root
root 6 Apr 11 00:59 media\n', b'drwxr-xr-x. 2 root root 6 Apr 11 00:59 home\n', b'lrwxrwxrwx. 1 root root 7 Sep 26 18:18 bin -> usr/b
in\n', b'lrwxrwxrwx. 1 root root 9 Sep 26 18:18 lib64 -> usr/lib64\n', b'lrwxrwxrwx. 1 root root 7 Sep 26 18:18 lib -> usr/lib\n', b'lrwx
rwxrwx. 1 root root 8 Sep 26 18:18 sbin -> usr/sbin\n', b'drwxr-xr-x. 13 root root 155 Sep 26 18:18 usr\n', b'dr-xr-xr-x. 5 root root 4.0K
Sep 26 18:26 boot\n', b'drwxr-xr-x. 19 root root 267 Sep 26 22:26 var\n', b'drwxr-xr-x. 3 root root 16 Sep 27 20:26 opt\n', b'dr-xr-xr-x. 11
0 root root 0 Sep 29 04:08 proc\n', b'dr-xr-xr-x. 13 root root 0 Sep 29 04:08 sys\n', b'drwxr-xr-x. 19 root root 3.1K Sep 29 18:22 dev\n',
b'drwxr-xr-x. 24 root root 740 Sep 30 05:22 run\n', b'drwxr-xr-x. 79 root root 8.0K Sep 30 05:22 etc\n', b'drwxrwxrwt. 9 root root 4.0K Sep 30
07:19 tmp\n', b'dr-xr-x---. 3 root root 170 Sep 30 07:21 root\n']

@colton22

This comment has been minimized.

Copy link

@colton22 colton22 commented Nov 27, 2018

@taigrr use SSH keys; see ssh(1) and ssh-copy-id(1)

I agree with this... but what if we dont have keys? or the ability to use keys due to a corporate env and RSA revolving password?

@bkafi

This comment has been minimized.

Copy link

@bkafi bkafi commented Dec 17, 2018

@girishlc
Try this version:

from __future__ import print_function
import os
import subprocess
import platform

PRIVATE_KEY_LOCATION = "C:/Users/johndoe/.ssh/id_rsa"
USER = "johndoe"
HOST = "192.168.1.1"
COMMAND="uname -a"
# Ports are handled in ~/.ssh/config since we use OpenSSH

system32 = os.path.join(os.environ['SystemRoot'], 'SysNative' if platform.architecture()[0] == '32bit' else 'System32')
ssh_path = os.path.join(system32, 'OpenSSH/ssh.exe')

ssh = subprocess.Popen([ssh_path, '-i', PRIVATE_KEY_LOCATION, "{}@{}".format(USER, HOST)],
                       stdin=subprocess.PIPE,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE)

std_data = ssh.communicate(COMMAND)

I ran into this problem as well.

See:
https://bugs.python.org/issue8557
https://stackoverflow.com/questions/41630224/python-does-not-find-system32

how does this convert over to Android file system?
Your script works perfectly from my kodi on windows and ssh's into a raspberry pi. I'm trying to get this working from an android Xiamoi Mi box. Any suggestions? :)

@johnnybubonic

This comment has been minimized.

Copy link

@johnnybubonic johnnybubonic commented Jan 30, 2019

this gist aged well. ELL OH ELL

edit: good luck with all your forked processes. i'm happy with paramiko.

@tarzan270786

This comment has been minimized.

Copy link

@tarzan270786 tarzan270786 commented Aug 12, 2019

Hi All ,
I need a help . I am using below code as I want ssh to run few commands in bash mode . But I am getting errors .

import subprocess
import sys

HOST="root@192.168.1.2". # change the IP as per ur testing
COMMAND="ls / -ltrh"
print("Type ",type(COMMAND))
ssh = subprocess.Popen(["ssh", "%s" % HOST, "%s" %COMMAND],
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
result = ssh.stdout.readlines()
if result == []:
error = ssh.stderr.readlines()
print >>sys.stderr, "ERROR: %s" % error
else:
print(result)

(venv) [rrshanke@slc10gon ADW]$ python ssh_try.py
('Type ', <type 'str'>)
ERROR: ['usage: ssh [-1246AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n', ' [-D [bind_address:]port] [-E log_file] [-e escape_char]\n', ' [-F configfile] [-I pkcs11] [-i identity_file]\n', ' [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n', ' [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]\n', ' [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n', ' [user@]hostname [command]\n']
(venv) [rrshanke@slc10gon ADW]$

I am not sure what is wrong . Please help me

@bortzmeyer

This comment has been minimized.

Copy link
Owner Author

@bortzmeyer bortzmeyer commented Aug 12, 2019

ssh = subprocess.Popen(["ssh", "%s" % HOST, "%s" %COMMAND],
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)

You should read the documentation. https://docs.python.org/3/library/subprocess.html Executive summary: shell=True is both dangerous and a bad idea in most cases. From a Python program, shell=False is the right solution most of the time. (See in the doc, around "If shell is True, it is recommended to pass args as a string rather than as a sequence.")

@datlife

This comment has been minimized.

Copy link

@datlife datlife commented Nov 4, 2019

SSH without third-party library

Here is my take on SSH without using third-party library. I leveraged a concept of fork system call in UNIX [1].

# Create an ssh session with python
import os
import shlex

def create_ssh(host, user):
  """Create a ssh session"""
  ssh = "/usr/bin/ssh -t {user}@{host} ".format(user=user, host=host)
 
  # Now, fork a child from current process 
  # This is a basic concept from Operating System class.
  pid = os.fork()
  if pid == 0:  # a child process
     print("Executing: %s" %(ssh))
     cmd = shlex.split(ssh)
     os.execv(cmd[0], cmd)
  
  os.wait(pid, 0)
  print("ssh session is finished. :)")
 
if __name__ == "__main__":
  create_ssh(
      host="remote_host", 
      user="remote_user")

Ref:
[1] https://en.wikipedia.org/wiki/Fork_(system_call)

@Insektosaurus

This comment has been minimized.

Copy link

@Insektosaurus Insektosaurus commented Jan 11, 2020

Amazing

Still populating top three google results when searching for 'python3 connect ssh and run script' :)

@faddison

This comment has been minimized.

Copy link

@faddison faddison commented May 6, 2020

Still true today. Amazing how badly libraries like paramiko and fabric fail at such a simple task.

@rogercallster

This comment has been minimized.

Copy link

@rogercallster rogercallster commented Jun 7, 2020

Still a valid chain but still finding it hard to reliably send password from Osx or linux

@hoon0912

This comment has been minimized.

Copy link

@hoon0912 hoon0912 commented Jan 7, 2021

thank you

@HungPhann

This comment has been minimized.

Copy link

@HungPhann HungPhann commented Jan 18, 2021

Can tell how amazing it is when this solution still works today.
Thanks a lot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment