from datetime import datetime
import pysftp
import fnmatch
import os
from stat import S_IMODE, S_ISDIR, S_ISREG
Hostname = "Hostname"
Username = "Username"
Password = "Password"
cnopts = pysftp.CnOpts()
cnopts.hostkeys = None
with pysftp.Connection(host=Hostname, username=Username, password=Password,
cnopts=cnopts) as sftp:
print("Connection successfully established ... ")
local_dir = r"D:\testing"
remote_dir = r'C:\Users\admin\Documents\personal'
for entry in sftp.listdir(remote_dir):
root, ext = os.path.splitext(entry)
for entry1 in sftp.listdir(local_dir):
root1, ext1 = os.path.splitext(entry1)
if root == root1:
if ext != ext1:
pass
elif root != root1:
remotepath = os.path.join(remote_dir, entry)
localpath = os.path.join(local_dir, entry)
sftp.get(remotepath, localpath, preserve_mtime=False)
I'm trying to export files from SFTP server to local. In order to do that I need to compare files by filename and file extension from server and local folder.
For instance abc.xlsx, abc.csv, adc.txt from server and
local folder has got abc.xlsx, then my script shouldn't copy any files with same name and same extension or different extension.
Here I need to compare the filenames and extension
if the same name then don't download
if same name and different extension don't download
You have to use os.listdir for the local path.
There's no need to re-read the local folder every time (unless you somehow aim to cater for multiple files with same base name in the remote directory).
Your code for identifying new files is not correct either.
You should not use os.path.join for SFTP paths.
This should do:
localfiles = os.listdir(local_dir)
for rentry in sftp.listdir(remote_dir):
rroot, rext = os.path.splitext(rentry)
found = False
for lentry in localfiles:
lroot, lext = os.path.splitext(lentry)
if rroot == lroot:
found = True
break
if not found:
remotepath = remote_dir + "/" rentry
localpath = os.path.join(local_dir, rentry)
sftp.get(remotepath, localpath, preserve_mtime=False)
Though these days, you should not use pysftp, as it is dead. Use Paramiko directly instead. See pysftp vs. Paramiko. The above code will work with Paramiko too with its SFTPClient.listdir.
Obligatory warning: Do not set cnopts.hostkeys = None, unless you do not care about security. For the correct solution see Verify host key with pysftp.
In my root folder I have few folders.
For example:
root
AAA
a.txt
a.xml
BBB
b.txt
b.xml
CCC
c.xml
DDD
......
I need to download all folder which contain .txt file inside it except DDD.
For example AAA, BBB has .txt file so I need to download it except CCC(as CCC didn't contain .txt)
I used the below code which download all file except DDD but am not able to check whether it contains .txt or not
Code I tried:
from paramiko import SSHClient
import paramiko
from stat import S_ISDIR
ssh = SSHClient()
ssh.load_system_host_keys()
ssh.connect('host', username='test', password='123')
remote_dir ='/root'
local_path = 'C:/download/'
def download_dir(remote_dir, local_dir):
import os
os.path.exists(local_dir) or os.makedirs(local_dir)
dir_items = sftp.listdir_attr(remote_dir)
for item in dir_items:
# assuming the local system is Windows and the remote system is Linux
# os.path.join won't help here, so construct remote_path manually
if(item.filename != "DDD"):
remote_path = remote_dir + '/' + item.filename
local_path = os.path.join(local_dir, item.filename)
if S_ISDIR(item.st_mode):
download_dir(remote_path, local_path)
else:
sftp.get(remote_path, local_path)
download_dir(remote_dir,local_path)
How can I check the files inside it and download the folder based on that?
You can use glob.glob(pathname, "*.txt") to get all txt files in a directory.
The documentation can be found here
Something like this:
import fnmatch
if S_ISDIR(item.st_mode):
if any(fnmatch.fnmatch(f, "*.txt") for f in sftp.listdir(remote_path)):
download_dir(remote_path, local_path)
else:
sftp.get(remote_path, local_path)
It's bit inefficient, as you will list all subfolders twice. But you can optimize it easily, if needed.
Based on List files on SFTP server matching wildcard in Python using Paramiko.
Any ideas on why I cannot copy from remote computer?
The first snippet works and I can copy to the 'servername'.
The second snippet gives me a 'No such file or directory.' error when I want to copy from 'servername' to my local computer.
UPDATE 2
This does not work either...
def copyfrom():
source_path = "\\computername\c$\test"
dest_path= "C:\localtest"
file_name = "testfile.txt"
shutil.copyfile(os.path.join(source_path, file_name), os.path.join(dest_path, file_name))
UPDATE I'm reading that you cannot copy from a remote computer using shutil. Anyone have any ideas on what my options are?
I have been copying to a list of computers using this...
import os
import shutil
import fileinput
import re
import sys # some of these use for other code in my program
source = os.listdir("C:/Users/jm/Desktop/PythonUpdate/")
destination = '//' + servername + r'/c$/test/'
for files in source:
if files.endswith("myname.config"):
try:
os.makedirs(destination, exist_ok=True)
shutil.copy(files,destination)
except:
copyerror()
os.system('cls' if os.name == 'nt' else 'clear')
array = []
with open("C:/Users/jm/Desktop/PythonUpdate/serverlist.txt", "r") as f:
for servername in f:
copyfiles(servername.strip())
Why is the opposite not working?
Here is what I am trying:
def copyfrom(servername):
# copy config from server
source = os.listdir('//' + servername + r'/c$/test') # directory where original configs are located
destination = 'C:/Users/jm/Desktop/PythonUpdate/' # destination server directory
for files in source:
if files.endswith("myname.config"):
try:
os.makedirs(destination, exist_ok=True)
shutil.copy(files,destination)
except:
copyerror()
readconfig(servername)
# begin here
os.system('cls' if os.name == 'nt' else 'clear')
array = []
with open("C:/Users/jm/Desktop/PythonUpdate/serverlist.txt", "r") as f:
for servername in f:
copyfrom(servername.strip())
You're not running the script from a directory called C:/Users/jm/Desktop/PythonUpdate/ by any chance?
os.listdir would be giving you the names of the entries in a directory at the specified path, not the full network name including the path.
https://docs.python.org/2/library/os.html
the modified example is using backslashes instead of forward slashes. if you want to do that, it's easier to use raw strings.
def copyfrom():
source_path = r"\\computername\c$\test"
dest_path= r"C:\localtest"
file_name = "testfile.txt"
shutil.copyfile(os.path.join(source_path, file_name), os.path.join(dest_path, file_name)
that said, there's really no reason to use backslashes in path names. unless you're running on a windows PC with no network share support, forward slash will be treated the same as backslash.
We can write a simple get like this:
import pysftp
hostname = "somehost"
user = "bob"
password = "123456"
filename = 'somefile.txt'
with pysftp.Connection(hostname, username=user, private_key='/home/private_key_file') as sftp:
sftp.get(filename)
However, I want to specify a pattern in the filename, something like: '*.txt'
Any idea on how to do this using pysftp ?
There's no function to download files matching a file mask in pysftp.
You have to:
list the directory, using Connection.listdir or Connection.walktree (if you need recursion)
iterate the list of files, filtering the files you want
call Connection.get individually for each.
For a trivial implementation, see:
List files on SFTP server matching wildcard in Python using Paramiko
It's about Paramiko, but the file matching part will be the same with pysftp:
import fnmatch
for filename in sftp.listdir('/remote/path'):
if fnmatch.fnmatch(filename, "*.txt"):
sftp.get("/remote/path/" + filename, "/local/path/" + filename)
Though your should really be using Paramiko anyway: pysftp vs. Paramiko
For a recursive example (you have to add the file matching), see:
Python pysftp get_r from Linux works fine on Linux but not on Windows.
See also how Connection.get_d or Connection.get_r are implemented.
Can confirm after going through the documentation that you can't list using a pattern. So i did something like this:
import pysftp
import re
server = pysftp.Connection(host=FTP_HOST,
username=FTP_USERNAME,
password=FTP_PASSWORD)
server.cwd(YOUR_FILES_PATH)
filelist = server.listdir()
for filename in filelist:
filedate = re.search(".*\.txt$", filename)
if filedate:
print "FOUND FILE " + filename
import pysftp
import sys
[...]
dn = datetime.now().strftime("%Y%m%d%H");
with pysftp.Connection(myHost, myUsername, password=myPassword) as sftp:
myFileList = sftp.listdir("files/")
for filename in myFileList:
if (filename.rfind("ArrivalList_" + dn) != -1):
sftp.get("files/" + filename, "/tmp/" + filename)
I am new to Python scripting. I need to copy few folders from my local machine (windows) to Linux server. As of now, I am copying the folders by opening WinSCP console. I need to automate this process. I have written a below code in Python using Paramiko module library.
import paramiko
import os
transport = paramiko.Transport(('10.10.10.10', 22))
transport.connect(username='weblogic', password='weblogic')
sftp = paramiko.SFTPClient.from_transport(transport)
filepath = '/apps/logs'
localpath = 'C:\\Users\\Public\\test'
sftp.put(localpath,filepath)
Above is not working properly and giving below error. Can you please help me to copy the folder present in the windows path C:\Users\Public\test to Linux server path /apps/logs?
Traceback (most recent call last):
File "C:\Users\Desktop\python\execute_script.py", line 28, in <module>
sftp.put(localpath,filepath)
File "C:\Python27\lib\paramiko\sftp_client.py", line 548, in put
fl = file(localpath, 'rb')
IOError: [Errno 13] Permission denied: 'C:\\Users\\Public\\test'
Please check the below code from the link https://gist.github.com/johnfink8/2190472. I have used put_all method in the snippet.
import paramiko
import socket
import os
from stat import S_ISDIR
class SSHSession(object):
# Usage:
# Detects DSA or RSA from key_file, either as a string filename or a
# file object. Password auth is possible, but I will judge you for
# using it. So:
# ssh=SSHSession('targetserver.com','root',key_file=open('mykey.pem','r'))
# ssh=SSHSession('targetserver.com','root',key_file='/home/me/mykey.pem')
# ssh=SSHSession('targetserver.com','root','mypassword')
# ssh.put('filename','/remote/file/destination/path')
# ssh.put_all('/path/to/local/source/dir','/path/to/remote/destination')
# ssh.get_all('/path/to/remote/source/dir','/path/to/local/destination')
# ssh.command('echo "Command to execute"')
def __init__(self,hostname,username='root',key_file=None,password=None):
#
# Accepts a file-like object (anything with a readlines() function)
# in either dss_key or rsa_key with a private key. Since I don't
# ever intend to leave a server open to a password auth.
#
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((hostname,22))
self.t = paramiko.Transport(self.sock)
self.t.start_client()
keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
key = self.t.get_remote_server_key()
# supposed to check for key in keys, but I don't much care right now to find the right notation
if key_file is not None:
if isinstance(key,str):
key_file=open(key,'r')
key_head=key_file.readline()
key_file.seek(0)
if 'DSA' in key_head:
keytype=paramiko.DSSKey
elif 'RSA' in key_head:
keytype=paramiko.RSAKey
else:
raise Exception("Can't identify key type")
pkey=keytype.from_private_key(key_file)
self.t.auth_publickey(username, pkey)
else:
if password is not None:
self.t.auth_password(username,password,fallback=False)
else: raise Exception('Must supply either key_file or password')
self.sftp=paramiko.SFTPClient.from_transport(self.t)
def command(self,cmd):
# Breaks the command by lines, sends and receives
# each line and its output separately
#
# Returns the server response text as a string
chan = self.t.open_session()
chan.get_pty()
chan.invoke_shell()
chan.settimeout(20.0)
ret=''
try:
ret+=chan.recv(1024)
except:
chan.send('\n')
ret+=chan.recv(1024)
for line in cmd.split('\n'):
chan.send(line.strip() + '\n')
ret+=chan.recv(1024)
return ret
def put(self,localfile,remotefile):
# Copy localfile to remotefile, overwriting or creating as needed.
self.sftp.put(localfile,remotefile)
def put_all(self,localpath,remotepath):
# recursively upload a full directory
os.chdir(os.path.split(localpath)[0])
parent=os.path.split(localpath)[1]
for walker in os.walk(parent):
try:
self.sftp.mkdir(os.path.join(remotepath,walker[0]))
except:
pass
for file in walker[2]:
self.put(os.path.join(walker[0],file),os.path.join(remotepath,walker[0],file))
def get(self,remotefile,localfile):
# Copy remotefile to localfile, overwriting or creating as needed.
self.sftp.get(remotefile,localfile)
def sftp_walk(self,remotepath):
# Kindof a stripped down version of os.walk, implemented for
# sftp. Tried running it flat without the yields, but it really
# chokes on big directories.
path=remotepath
files=[]
folders=[]
for f in self.sftp.listdir_attr(remotepath):
if S_ISDIR(f.st_mode):
folders.append(f.filename)
else:
files.append(f.filename)
print (path,folders,files)
yield path,folders,files
for folder in folders:
new_path=os.path.join(remotepath,folder)
for x in self.sftp_walk(new_path):
yield x
def get_all(self,remotepath,localpath):
# recursively download a full directory
# Harder than it sounded at first, since paramiko won't walk
#
# For the record, something like this would gennerally be faster:
# ssh user#host 'tar -cz /source/folder' | tar -xz
self.sftp.chdir(os.path.split(remotepath)[0])
parent=os.path.split(remotepath)[1]
try:
os.mkdir(localpath)
except:
pass
for walker in self.sftp_walk(parent):
try:
os.mkdir(os.path.join(localpath,walker[0]))
except:
pass
for file in walker[2]:
self.get(os.path.join(walker[0],file),os.path.join(localpath,walker[0],file))
def write_command(self,text,remotefile):
# Writes text to remotefile, and makes remotefile executable.
# This is perhaps a bit niche, but I was thinking I needed it.
# For the record, I was incorrect.
self.sftp.open(remotefile,'w').write(text)
self.sftp.chmod(remotefile,755)
In addition to the answer #user1041177, but here a way to do it when you are on windows to linux host (not really sure which kind of host actually).
I don't know why, but if I keep backslash onto remote path, I get a FileNotFoundException. The only way to work was to replace all '\' by '/'
Maybe someone could tell me the proper way to avoid this situation at all ?
Here a part of the exact same code above to give you breadcrumbs if you encounter the same issue :
def sftp_walk(socket, remotepath):
remotepath = remotepath.replace('\\', '/')
path = remotepath
files = []
folders = []
for f in socket.listdir_attr(remotepath.replace('\\', '/')):
if S_ISDIR(f.st_mode):
folders.append(f.filename)
else:
files.append(f.filename)
print(path, folders, files)
yield path, folders, files
for folder in folders:
new_path = os.path.join(remotepath.replace('\\', '/'), folder)
for x in sftp_walk(socket, new_path):
yield x
def get_all(socket, remotepath, localpath):
remotepath = remotepath.replace('\\', '/')
socket.chdir(os.path.split(remotepath)[0])
parent = os.path.split(remotepath)[1]
try:
os.mkdir(localpath)
except:
pass
for walker in sftp_walk(socket, parent):
try:
os.mkdir(os.path.join(localpath, walker[0]).replace('\\', '/'))
except:
pass
for file in walker[2]:
socket.get(os.path.join(walker[0], file).replace('\\', '/'), os.path.join(localpath, walker[0], file).replace('\\', '/'))
BTW, I am not using those function inside an object, that's why their is 'socket' instead of 'self' because I call those function by passing the SFTP socket to them.
Finally have to say thank you to #user1041177, working like a charm.
I was trying to copy from a windows box to a linux box and got the same error as #Apex above. I was using the put_all method and I had to do some "replace" on parts of the code.
def put_all(self,localpath,remotepath):
remotepath = remotepath.replace('\\', '/')
# recursively upload a full directory
os.chdir(os.path.split(localpath)[0])
parent=os.path.split(localpath)[1]
for walker in os.walk(parent):
try:
self.sftp.mkdir(os.path.join(remotepath,walker[0]).replace('\\', '/'))
except:
pass
for file in walker[2]:
self.put(os.path.join(walker[0],file).replace('\\', '/'),os.path.join(remotepath,walker[0],file).replace('\\', '/'))
I found a few shortcomings with the above methods - first, the putter/getter doesn't function in the way you'd expect - if you want to put /foo/bar into /some/folder, you can't as it won't let you put files from a source folder to a different destination folder - the only thing you can do is put /foo/bar into /some/bar. In addition, you have to specify the source as /foo/bar and the destination as /some to end up with /some/bar - I find this confusing as it's not how most operating/ftp systems handle putting/getting/copying/etc. So, I improved on the answers listed above:
If you're going from Windows to Linux:
def put_dir(source, dest):
source = os.path.expandvars(source).rstrip('\\').rstrip('/')
dest = os.path.expandvars(dest).rstrip('\\').rstrip('/')
for root, dirs, files in os.walk(source):
for dir in dirs:
try:
sftp.mkdir(posixpath.join(dest, ''.join(root.rsplit(source))[1:].replace('\\', '/'), dir))
except:
pass
for file in files:
sftp.put(os.path.join(root, file), posixpath.join(dest, ''.join(root.rsplit(source))[1:].replace('\\', '/'), file))
source = '%USERPROFILE%\\Downloads\\'
dest = '/foo/bar'
put_dir(source, dest)
If you're just doing Windows then swap out posixpath.join with os.path.join and remove .replace('\\', '/'):
def put_dir(source, dest):
source = os.path.expandvars(source).rstrip('\\').rstrip('/')
dest = os.path.expandvars(dest).rstrip('\\').rstrip('/')
for root, dirs, files in os.walk(source):
for dir in dirs:
try:
sftp.mkdir(os.path.join(dest, ''.join(root.rsplit(source))[1:], dir))
except:
pass
for file in files:
sftp.put(os.path.join(root, file), os.path.join(dest, ''.join(root.rsplit(source))[1:], file))
source = '%USERPROFILE%\\Downloads\\'
dest = 'foo\\bar'
put_dir(source, dest)
The reason for the try statement is that sftp.mkdir errors out if the folder already exists.
Paramiko does not support recursive operations.
You can use pysftp. It's a wrapper around Paramiko that has more Python-ish look and feel and supports recursive operations. See
pysftp.Connection.put_r()
pysftp.Connection.get_r()
Or you can just base your code on pysftp source code. Or see my answer to Python pysftp get_r from Linux works fine on Linux but not on Windows.