moink
moink

Reputation: 888

Checking if a file on SFTP server is a symbolic link, and deleting the symbolic link, using Python Paramiko/pysftp

I have a directory on a linux-based server, that contains subdirectories, one of which contains a symbolic link to another directory elsewhere on the server.

I would like to, using a python script, remotely from a Windows computer on the network, remove the directory and all of its contents. While doing so, I would like to remove the symbolic link, but not delete the directory that the symbolic link points to, or any of its contents.

Here is the code that I have (partly taken from another stackoverflow answer https://stackoverflow.com/a/22074280/3794244):

def rm_tree(remote_path, connection):
    """Recursively remove a remote directory and all its contents

    The remote server must have a POSIX-standard file system

    Parameters
    ----------
    remote_path : str
        Directory on the remote server to remove
    connection : pysftp.Connection
        Connection to the remote server on which to find the directory

    Returns
    -------
    None
    """
    # https://stackoverflow.com/questions/3406734
    try:
        files = connection.listdir(remote_path)
    except FileNotFoundError:
        files = []
    for filename in files:
        rpath = posixpath.join(remote_path, filename)
        if connection.isdir(rpath):
            rm_tree(rpath, connection)
        else:
            connection.unlink(rpath)
    with contextlib.suppress(FileNotFoundError):
        connection.rmdir(remote_path)

When I run this I get an error from paramiko with very little information, it's an IOError with the message "OSError: Failure". It gives that error on the last line of my function, connection.rmdir(remote_path), when it tries to delete the directory that contains the symbolic link. The function has deleted the remainder of the contents of the directory, but the symbolic link is still there.

I think what I need to add to my function is something like:

if is_symlink(rpath):
   remove_symlink(rpath)

right before checking if it's a directory, but I can't find anything in the pysftp or paramiko documentation that are the equivalent of either the is_symlink or remove_symlink functions.

How do I find out if a remote file is a symbolic link, and how do I delete symbolic links remotely?

Upvotes: 1

Views: 1788

Answers (1)

Martin Prikryl
Martin Prikryl

Reputation: 202504

Do not use Connection.listdir and Connection.isdir. That's inefficient. Use Connection.listdir_attr to retrieve the listing including all attributes. Note that Connection.listdir internally calls Connection.listdir_attr and throws away the attributes.

Having the attributes, you can use stat.S_ISLNK to determine if the entry is a symlink.

import stat
for f in connection.listdir_attr(remote_path):
    rpath = posixpath.join(remote_path, f.filename)
    if stat.S_ISLNK(f.st_mode)):
        connection.unlink(rpath)
    elif stat.S_ISDIR(f.st_mode):
        rm_tree(rpath, connection)
    else:
        connection.unlink(rpath)

Upvotes: 1

Related Questions