Python Paramiko 目录遍历 SFTP

问题描述 投票:0回答:3

如何通过 SSH 在另一台计算机上执行

os.walk()
?问题是
os.walk()
在本地计算机上执行,我想 ssh 到另一台主机,遍历目录并为其中的每个文件生成 MD5 哈希值。

到目前为止我写的看起来像这样(下面的代码),但它不起作用。任何帮助将不胜感激。

try:
    hash_array = []
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect('sunbeam', port=22, username='xxxx', password='filmlight')

    spinner.start()
    for root, dirs, files in os.walk(_path):
        for file in files:
            file_path = os.path.join(os.path.abspath(root), file)
            
            #  generate hash code for file
            hash_array.append(genMD5hash(file_path))
            
            file_nb += 1
    spinner.stop()
    spinner.ok('Finished.')

    return hash_array
except Exception as e:
    print(e)
    return None
finally:
    ssh.close() 
python ssh sftp paramiko
3个回答
10
投票

要使用 Paramiko 递归列出目录,并使用标准文件访问接口 SFTP,您需要使用

SFTPClient.listdir_attr
:

实现递归函数
from stat import S_ISDIR, S_ISREG
def listdir_r(sftp, remotedir):
    for entry in sftp.listdir_attr(remotedir):
        remotepath = remotedir + "/" + entry.filename
        mode = entry.st_mode
        if S_ISDIR(mode):
            listdir_r(sftp, remotepath)
        elif S_ISREG(mode):
            print(remotepath)

基于 Linux 中的 Python pysftp get_r 在 Linux 上运行良好,但在 Windows 上则不行


或者,pysftp 实现一个

os.walk
等效项:
Connection.walktree
。虽然 pysftp 已经死了,所以最好仅将其用作您自己实现的灵感,而不是直接使用。


此外,使用 SFTP 协议获取远程文件的 MD5 时也会遇到麻烦。

虽然 Paramiko 通过其

SFTPFile.check
支持它,但大多数 SFTP 服务器(特别是最广泛的 SFTP/SSH 服务器 – OpenSSH)不支持。 参见:
如何检查Paramiko是否成功将文件上传到SFTP服务器?
如何在 SFTP 文件传输过程中执行校验和以确保数据完整性?

因此,您很可能不得不求助于 shell

md5sum
命令(如果您有 shell 访问权限)。一旦您无论如何都必须使用 shell,请考虑使用 shell 列出文件,因为这比通过 SFTP 快得多。

查看 md5 目录树中的所有文件

使用

SSHClient.exec_command

使用 Python 将下载文件的 MD5 与 SFTP 服务器上的文件进行比较


强制警告:请勿使用

AutoAddPolicy
– 这样做您将失去针对 MITM 攻击的保护。正确的解决方案请参阅Paramiko“未知服务器”


2
投票

基于之前的答案,这里的版本不需要递归并返回路径列表,而不是使用

print
命令。

from stat import S_ISDIR, S_ISREG
from collections import deque

def listdir_r(sftp, remotedir):
    dirs_to_explore = deque([remotedir])
    list_of_files = deque([])

    while len(dirs_to_explore) > 0:
        current_dir = dirs_to_explore.popleft()

        for entry in sftp.listdir_attr(current_dir):
            current_fileordir = current_dir + "/" + entry.filename

            if S_ISDIR(entry.st_mode):
                dirs_to_explore.append(current_fileordir)
            elif S_ISREG(entry.st_mode):
                list_of_files.append(current_fileordir)

    return list(list_of_files)

1
投票

这是另一个尝试模仿 Python 中的

os.walk
(
python/cpython/os.py:walk
) 函数的实现,以便它可以用作需要内置函数的代码的直接替代。

class ParamikoWalkExample:
    def __init__(self, host, username=None):
        self._host = host
        self._username = username
        self._ssh = self._ssh_connect()
        self._sftp = self._ssh.open_sftp()

    def _ssh_connect(self):
        ssh = paramiko.SSHClient()
        ssh.load_system_host_keys()
        ssh.connect(self._host, username=self._username)
        return ssh

    def walk(
        self,
        top,
        topdown=True,
        onerror=None, # ignored
        followlinks=False,
    ):
        stack = [top]

        while stack:
            top = stack.pop()
            if isinstance(top, tuple):
                yield top
                continue

            dirs = []
            nondirs = []
            walk_dirs = []

            for entry in self._sftp.listdir_attr(top):
                if entry.st_mode is None:
                    nondirs.append(entry.filename)
                elif stat.S_ISDIR(entry.st_mode):
                    dirs.append(entry.filename)
                    walk_dirs.append(top + '/' + entry.filename)
                elif stat.S_ISREG(entry.st_mode):
                    nondirs.append(entry.filename)
                elif stat.S_ISLNK(entry.st_mode):
                    target = entry.filename
                    while True:
                        target = self._sftp.readlink(target)
                        if not target:
                            nondirs.append(entry.filename)
                            break
                        target_entry = self._sftp.stat(target)
                        if not target_entry.st_mode:
                            nondirs.append(entry.filename)
                            break

                        if stat.S_ISLNK(target_entry.st_mode):
                            continue

                        elif stat.S_ISDIR(target_entry.st_mode):
                            dirs.append(entry.filename)
                            if followlinks:
                                walk_dirs.append(top + '/' + entry.filename)
                            break
                        elif stat.S_ISREG(target_entry.st_mode):
                            nondirs.append(entry.filename)
                            break

            if topdown:
                yield top, dirs, nondirs
                for new_path in reversed(walk_dirs):
                    stack.append(new_path)
            else:
                # Yield after sub-directory traversal if going bottom up
                stack.append((top, dirs, nondirs))
                for new_path in reversed(walk_dirs):
                    stack.append(new_path)

旁注:由于深层文件层次结构中的递归错误,最近在主分支上重写了内置函数,以使用堆栈而不是递归:https://github.com/python/cpython/issues/89727

© www.soinside.com 2019 - 2024. All rights reserved.