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

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

@nicktimko
Copy link

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

@sprive
Copy link

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

atsatosh commented Jan 8, 2017

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

Copy link

ghost commented Apr 3, 2017

Im just a bit confused about

if result == []:

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

@dmpaul26
Copy link

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

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

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

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

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