Как вывести список всех папок и файлов в каталоге после подключения через SFTP в Python

Я использую Python и пытаюсь подключиться к SFTP, хочу получить оттуда XML-файл, и мне нужно разместить его в моей локальной системе. Ниже приведен код:

import paramiko

sftpURL   =  'sftp.somewebsite.com'
sftpUser  =  'user_name'
sftpPass  =  'password'

ssh = paramiko.SSHClient()
# automatically add keys without requiring human intervention
ssh.set_missing_host_key_policy( paramiko.AutoAddPolicy() )

ssh.connect(sftpURL, username=sftpUser, password=sftpPass)

ftp = ssh.open_sftp()
files = ftp.listdir()
print files

Здесь связь удалась. И теперь я хочу увидеть все папки и все файлы, и мне нужно войти в нужную папку, чтобы получить оттуда XML-файл.

Наконец, я намерен просмотреть все папки и файлы после подключения к SFTP-серверу.

В приведенном выше коде я использовал ftp.listdir(), через который я получил вывод, как показано ниже

['.bash_logout', '.bash_profile', '.bashrc', '.mozilla', 'testfile_248.xml']

Я хочу знать, присутствуют ли только эти файлы?

И команда, которую я использовал выше, подходит и для просмотра папок?

Какая команда предназначена для просмотра всех папок и файлов?


person Shiva Krishna Bavandla    schedule 06.09.2012    source источник


Ответы (3)


Одно быстрое решение - изучить вывод lstat каждого объекта в ftp.listdir().

Вот как вы можете перечислить все каталоги.

>>> for i in ftp.listdir():
...     lstatout=str(ftp.lstat(i)).split()[0]
...     if 'd' in lstatout: print i, 'is a directory'
... 

Файлы обратного поиска:

>>> for i in ftp.listdir():
...     lstatout=str(ftp.lstat(i)).split()[0]
...     if 'd' not in lstatout: print i, 'is a file'
... 
person oz123    schedule 06.09.2012
comment
Полагаться на строковое поведение SFTPAttributes - это ужасный, ужасный взлом. Почему ты не делаешь это правильно и не используешь stat.S_ISDIR(lstatout.st_mode)? - person Fake Name; 26.01.2016
comment
По сути, это эквивалентно синтаксическому анализу вывода отладки с консоли. Это может сработать, но оно чрезвычайно хрупкое, и его следует делать только в случае крайней необходимости, и других вариантов нет. В этом случае вся суть класса SFTPAttributes состоит в том, чтобы удобно инкапсулировать метаданные о пути на удаленном хосте, поэтому не просто использовать его так, как он должен работать, откровенно глупо. - person Fake Name; 11.02.2018
comment
Этот код неэффективен. Правильное решение см. В моем ответе. - person Martin Prikryl; 28.08.2019

SFTPClient.listdir возвращает все, файлы и папки.

Были ли папки, чтобы отличить их от файлов, используйте SFTPClient.listdir_attr. Он возвращает набор SFTPAttributes.

from stat import S_ISDIR, S_ISREG
for entry in sftp.listdir_attr(remotedir):
    mode = entry.st_mode
    if S_ISDIR(mode):
        print(entry.filename + " is folder")
    elif S_ISREG(mode):
        print(entry.filename + " is file")

Принятый ответ @ Oz123 неэффективен. SFTPClient.listdir внутренне вызывает SFTPClient.listdir_attr и отбрасывает большую часть информации, возвращая только имена файлов и папок. Затем ответ бесполезно и кропотливо повторно извлекает все эти данные, вызывая SFTPClient.lstat для каждого файла.

См. Также Как получить размеры всех файлов SFTP в каталоге через Paramiko.


Обязательное предупреждение: не используйте AutoAddPolicy - вы теряете защиту от атак MITM. таким образом. Правильное решение см. На странице Неизвестный сервер Paramiko.

person Martin Prikryl    schedule 28.08.2019

Вот решение, которое я придумал. На основе https://stackoverflow.com/a/59109706. Мое решение дает хороший результат.

Обновление. Я немного изменил его, чтобы учесть предложения Мартина. Теперь мой код значительно быстрее по сравнению с моей первоначальной версией, использующей isdir и listdir.

# prefix components:
space =  '    '
branch = '│   '
# pointers:
tee =    '├── '
last =   '└── '

def stringpath(path):
    # just a helper to get string of PosixPath
    return str(path)

from pathlib import Path
from stat import S_ISDIR
def tree_sftp(sftp, path='.', parent='/', prefix=''):
    """
    Loop through files to print it out
    for file in tree_sftp(sftp):
        print(file)
    """
    fullpath = Path(parent, path)
    strpath = stringpath(fullpath)

    dirs = sftp.listdir_attr(strpath)
    pointers = [tee] * (len(dirs) - 1) + [last]
    pdirs = [Path(fullpath, d.filename) for d in dirs]
    sdirs = [stringpath(path) for path in pdirs]

    for pointer, sd, d in zip(pointers, sdirs, dirs):
        yield prefix + pointer + d.filename
        if S_ISDIR(d.st_mode):
            extension = branch if pointer == tee else space
            yield from tree_sftp(sftp, sd, prefix=prefix + extension)

Вы можете попробовать это, используя pysftp

import pysftp
with pysftp.Connection(HOSTNAME, USERNAME, PASSWORD) as sftp:
    for file in tree_sftp(sftp):
        print(file)

Дайте мне знать, если это сработает для вас.

person scientific_explorer    schedule 24.03.2021
comment
В-третьих, вы не должны использовать класс Python Path на путях SFTP. Path использует соглашения о локальной файловой системе, которые могут не соответствовать соглашениям SFTP. В частности, в Windows ваш код может дать сбой. - person Martin Prikryl; 24.03.2021
comment
Python Path используется только для упрощения конкатенации пути. Я конвертирую это в строку, чтобы использовать с pysftp. Я не включил stringpath функцию, просто заметил. Позвольте мне добавить это. Это просто помощник. - person scientific_explorer; 24.03.2021
comment
И все, SFTP всегда использует косую черту. В то время как Path будет использовать разделитель путей локальной системы. Если это не косая черта (как в Windows), ваш код завершится ошибкой. Вы представляете ту же проблему, что и get_r и put_r psftp. См. Python pysftp put_r не работает в Windows. Возможно, вы захотите явно использовать PosixPath. - person Martin Prikryl; 24.03.2021