Skip to content

Instantly share code, notes, and snippets.

@macleginn
Created June 15, 2022 08:54
Show Gist options
  • Save macleginn/e317135b04e8ae21d4ed29a7760c939b to your computer and use it in GitHub Desktop.
Save macleginn/e317135b04e8ae21d4ed29a7760c939b to your computer and use it in GitHub Desktop.
import os
import sys
import shutil
def copy_tree(src, dst):
'''
Copy a directory tree from src to dst ignoring dangling
symlinks, retrieving files symlinks point to, and
breaking the cycles, i.e. never copying the same
source file twice.
'''
os.mkdir(dst)
visited_source_paths = set()
stack = []
for item in os.listdir(src):
stack.append(item)
while stack:
current_path_suffix = stack.pop()
current_path = os.path.join(src, current_path_suffix)
print(current_path)
if os.path.realpath(current_path) in visited_source_paths:
continue
if os.path.islink(current_path):
realpath = os.path.realpath(current_path)
print(f'-> {realpath}')
# Skip dangling and circular symlinks.
if not os.path.exists(realpath) or realpath in visited_source_paths:
continue
# We assume for simplicity that the stuff valid symlinks point to is valid.
shutil.copytree(realpath, os.path.join(
dst, current_path_suffix), ignore=shutil.ignore_patterns('*.pyc'))
elif os.path.isdir(current_path):
# Create the directory in the destination tree,
# add the contents to the stack, and continue.
os.mkdir(os.path.join(dst, current_path_suffix))
for item in os.listdir(current_path):
stack.append(os.path.join(current_path_suffix, item))
else:
# A simple file.
if not current_path.endswith('.pyc'):
shutil.copy(current_path, os.path.join(
dst, current_path_suffix))
visited_source_paths.add(os.path.realpath(current_path))
if __name__ == '__main__':
src = sys.argv[1]
dst = sys.argv[2]
copy_tree(src, dst)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment