SFTP in Python: Really Simple SSH

29 May 2008

ssh.py provides three common SSH operations, get, put and execute. It is a high-level abstraction upon Paramiko.

I wrote it yesterday for my own needs, so it is still very much in the beta stage. Any improvements or comments gratefully accepted.

In short, it works as follows:

import ssh
s = ssh.Connection('example.com')
s.put('hello.txt')
s.get('goodbye.txt')
s.execute('du -h --max-depth=0')
s.close()

That is it, in the rest of this post, I walk through this line by line.

Installation

First, we need to install paramiko, if you don't have it already.

On Gentoo Linux:

emerge paramiko

On Ubuntu/Debian and so on:

apt-get install python-paramiko

If you want to use Python's easy_install then:

easy_install paramiko

Secondly, you need to grab the ssh.py module, grab it from my code-page, and save it as ssh.py.

Connecting to a remote server

To play with the script interactively, you need to start Python:

python

Now, import the ssh module:

import ssh

Next we need to initiate the connection. If your username is the same on both systems, and you have set up ssh-keys, then all you need to do is:

s = ssh.Connection('example.com')

Connection supports the following options:

host The Hostname of the remote machine.
username Your username at the remote machine.
private_key Your private key file.
password Your password at the remote machine.
port The SSH port of the remote machine.

The host is essential of course. Port defaults to 22. The username defaults to the username you are currently using on the local machine.

You need to use one of the authentication methods, a private key or a password. If you don't specify anything, then ssh.Connection will attempt to use a private_key at ~/.ssh/id_rsa or ~/.ssh/id_dsa.

So to specify a username and password, you can do it like this:

s = ssh.Connection(host = 'example.com', username = 'warrior', password = 'lennalenna')

Of course, Python also allows you to use the order to specify the arguments, so the last example can be written as:

s = ssh.Connection('example.com', 'warrior', password = 'lennalenna')

Operations

Once you have set up the connection, there are three methods you can use. Firstly, to send a file from the local machine, you can use put:

s.put('hello.txt')

The above example copies a file called hello.txt from the current local working directory to the remote server. We can also be more explicit if we want:

s.put('/home/warrior/hello.txt', '/home/zombie/textfiles/report.txt')

So the above example copies /home/warrior/hello.txt on the local server to /home/zombie/textfiles/report.txt on the remote server.

The second operation works in a similar way but in reverse:

s.get('hello.txt')

get takes the file from the remote server to the local server, again we can be more explicit if we want:

s.get('/var/log/strange.log', '/home/warrior/serverlog.txt')

The above example copies the strange.log from the server and saves it as serverlog.txt.

The last operation is execute, this executes a command on the remote server:

s.execute('ls -l')

This returns the output as a Python list.

Closing the connection

You can do as many operations you like while the connection is open, but when you are finished, you need to close the connection between the local and remote machines. You do this with the close method:

s.close()

There we go, that is all I needed to do with SSH. Please do let me know using the comments below if you have any problems using it.

If you import my module in your program and later find that you need more power or flexibility, you should be able to swap it out for the full paramiko with a minimum of fuss.

1 Max says...

Awesome. Paramiko is over complicated for my simple single SSH command needs. I have usually just resorted to using a system command and reading from that.

Posted at 7:53 p.m. on May 29, 2008


2 Peter says...

This is pretty nice, maybe if an mget or mput could be added it would really improve its use. not sure how to do that couldn't figure it out using paramiko its probably impossible as of now. But i just learned python a week ago lol. This program really simplifies paramiko, good job! props

Posted at 3:30 p.m. on June 27, 2008


3 joeg says...

Thanx for coding this up... One glitch I found:

#key_key = paramiko.RSAKey.from_private_key_file(private_key_file) key_key = paramiko.DSSKey.from_private_key_file(private_key_file) self._transport.connect(username = username, pkey = key_key)

i'm resorting to this for the time being... it really should be conditionalized to choose the right RSA/DSS based on the private_key_file name, but well, thought you should know

Posted at 7:48 p.m. on July 30, 2008


4 Howie_in_AZ says...

I too ran into some issues with your code, but I've modified it account for RSA and DSA/DSS keys. I'd post it but whatever commenting software is in use here is irking me. Basically do a check in your 'Try to use default key' block and set the rsa_key var to paramiko.RSAKey.from_private_key_file if its the former, or paramoki.DSSKey.from_private_key_file if its the latter.

Posted at 11:49 p.m. on September 21, 2008


5 Felix Hummel says...

Your code made my life a lot easier. Thanks!

Posted at 3:59 p.m. on October 5, 2008


6 grateful says...

Thanks, you saved me a buttload of pain!

Posted at 12:36 a.m. on October 7, 2008


7 bsergean says...

It's working for me but it's slow, as it's not working with the ssh trick to re-use opened socket. Probably a paramiko problem, or the right flag not given to paramiko ?

(this is the trick)
Host *
ControlMaster auto ControlPath /tmp/%r@%h:%p

Posted at 4:39 a.m. on October 8, 2008


8 Adrian says...

I got this stacktrace:
>>> username = 'user'
>>> s = ssh.Connection('example.com',username)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ssh.py", line 46, in __init__
  rsa_key = paramiko.RSAKey.from_private_key_file(private_key_file)
File "/var/lib/python-support/python2.5/paramiko/pkey.py", line 197, in   from_private_key_file
  key = cls(filename=filename, password=password)
File "/var/lib/python-support/python2.5/paramiko/rsakey.py", line 51, in __init__
  self._from_private_key_file(filename, password)
File "/var/lib/python-support/python2.5/paramiko/rsakey.py", line 164, in _from_private_key_file
  data = self._read_private_key_file('RSA', filename, password)
File "/var/lib/python-support/python2.5/paramiko/pkey.py", line 279, in _read_private_key_file
  data = self._read_private_key(tag, f, password)
File "/var/lib/python-support/python2.5/paramiko/pkey.py", line 322, in _read_private_key
  raise PasswordRequiredException('Private key file is encrypted')

System Message: WARNING/2 (<string>, line 18)

Definition list ends without a blank line; unexpected unindent.

paramiko.PasswordRequiredException: Private key file is encrypted

and this

>>> s = ssh.Connection('example.com',username,'~/.ssh/id_rsa','password')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ssh.py", line 33, in __init__
    self._transport.connect(username = username, password = password)
  File "/var/lib/python-support/python2.5/paramiko/transport.py", line 855, in connect
    self.auth_password(username, password)
  File "/var/lib/python-support/python2.5/paramiko/transport.py", line 1016, in auth_password
    return self.auth_handler.wait_for_response(my_event)
  File "/var/lib/python-support/python2.5/paramiko/auth_handler.py", line 174, in wait_for_response
    raise e
paramiko.BadAuthenticationType: Bad authentication type (allowed_types=['publickey'])

Posted at 2:57 p.m. on November 4, 2008


9 Very helpful says...

but i need more help. I'm have to execute the sudo command after I log in. What do I need to do to enter the password after the sudo command is executed?

Thanks

Posted at 3:03 p.m. on November 12, 2008


10 Felipe Coury says...

What do I have to say? Only this: "THANK YOU"!

Awesome!

Posted at 12:43 a.m. on November 23, 2008


11 QuickSilver says...

Nice! Is there anyway to implement a ServerAliveInterval for long processes? This is because my our firewall keeps closing the connection based on inactive connections.

Thanks,

Posted at 10:19 p.m. on January 5, 2009


What do you have to say?

Show Editing Help

Europython

About

Hello, my name is Zeth, I'll be your host here.

Command Line Warriors is about taking control of your own technology, it looks at our experiences of computing; especially using GNU/Linux, the Python programming language, the command-line and issues such as techno-ethics, best practices and whatever is cool now. If you take control of your technology then you are a Warrior too!

This site is your site too which means that you can contribute and get involved. You can leave comments using the facility provided. For me, the comments and discussions are by far the best part of the site. So please do have your say!

Latest Discussions

picsus

January 5, 2009
Monique, a Leaf fan, originate this plumb persistent to believe. Now, let me regarding out that this was in no way an try to articulate one cooperate is more wisely ...
This week in the world of the Command Line; The Friday Round up!

QuickSilver

January 5, 2009
Nice! Is there anyway to implement a ServerAliveInterval for long processes? This is because my our firewall keeps closing the connection based on inactive connections. Thanks,
SFTP in Python: Really Simple SSH

Tun

January 5, 2009
Hi, Do You know, haw can i get start date for tasks evolution? If exists the similar way to your example: i.get_due() ? I would like to have sth like ...
Three Useful Python Bindings - ClamAV, Apt and Evolution

MurreiM

January 5, 2009
This is great! http://www.youtube.com/MurreiM Buy Alli Orlistat online cheap
Filing cabinets 101 - An introduction to disk partitions

sarah

January 5, 2009
I recently came across your blog and have been reading along. I thought I would leave my first comment. I don't know what to say except that I have enjoyed ...
This Week: Freedom not Time-Bombs

jnfrlast

January 4, 2009
Hi! http://www.youtube.com/jnfrlast buy cheap viagra online
Filing cabinets 101 - An introduction to disk partitions

Samuel Huckins

January 4, 2009
Great tips! I have had occasion to do a lot of MySQL instance migrations lately, so here is an improvement for Trick 1: mysqldump <DATABASE_NAME> [mysqldump_options] | gzip -c | ...
Five useful command one liners

George Glass

December 31, 2008
I don't really see the point in trying to make linux user-friendly or take over the desktop. We rule the servers the most important element of the entire game. Let ...
Give Linux a chance

bug

December 31, 2008
@Zeth: The hidden field does block some. Not perfect, but it does release some weight from the filtering system, as those are 100% false comments. Acctually, if you would have ...
On Comment Spam

Zeth

December 31, 2008
Hi Eion, Yes that is an interesting approach also. It is the only approach given by default in the stock Django comments module, though it does not stop all comment ...
On Comment Spam

Bug

December 30, 2008
Well... Sadly, and I guess you hate me for it, I use captcha. But at least it's not an image, so even if you visit using w3m [yey!] you can ...
On Comment Spam

Eion

December 30, 2008
Other than server-side processing of comments, I like to add additional <input>'s and hide them in external css. Most of the time the fields are populated by spam-bots, and if ...
On Comment Spam

Nostoc

December 27, 2008
... Mate possible because of the dull Kg8
Ruy Lopez, Berlin defence, open variation

Nostoc

December 27, 2008
My bad, I meant the picture beneath 15, after close inspection my suggestion would be on 18. Instead of 18 : Qe2, I would have taken that knight with my ...
Ruy Lopez, Berlin defence, open variation

Zeth

December 27, 2008
Nostoc, white takes the rook on 15, the rook is a better kill than a knight.
Ruy Lopez, Berlin defence, open variation

Nostoc

December 26, 2008
I'm not that good at chess, but I have a question. At 15, why doesn't white simply take black's knight in C6 with the bishop? It's an easy kill, since ...
Ruy Lopez, Berlin defence, open variation

Zeth

December 26, 2008
CorkyAgain, good question, I don't have a FreeBSD box available at the moment so I can't comment. On Linux at least watch does as I have described.
Five useful command one liners

CorkyAgain

December 25, 2008
Is the watch command you're describing a Linuxism? On my FreeBSD box, "man watch" seems to be describing something completely different.
Five useful command one liners

Binny V A

December 25, 2008
I have actually setup a site to store just short commands... http://txt.binnyva.com/
Five useful command one liners

Bassam essa

December 25, 2008
i try this line command elinks -source "http://www.e51g.com/" > resulthtml.txt its work done :) thx
Command the Web - an ELinks tutorial