Reputation: 888
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
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