Skip to content

Instantly share code, notes, and snippets.

@bortzmeyer
Created October 13, 2011 13:42
Show Gist options
  • Save bortzmeyer/1284249 to your computer and use it in GitHub Desktop.
Save bortzmeyer/1284249 to your computer and use it in GitHub Desktop.
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
@pkittenis
Copy link

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
Copy link

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
Copy link

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
Copy link

@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
Copy link

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
Copy link

@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
Copy link

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
Copy link

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
Copy link

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
Copy link
Author

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
Copy link

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
Copy link

Insektosaurus commented Jan 11, 2020

Amazing

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

@faddison
Copy link

faddison commented May 6, 2020

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

@rogercallster
Copy link

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

@hoon0912
Copy link

hoon0912 commented Jan 7, 2021

thank you

@HungPhann
Copy link

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