File: fix-symlinks.py
#!/usr/local/bin/python3
"""
=================================================================
Mac OS Catalina locked down the '/' root, which breaks symlinks
using absolute paths from former root-level folders, as well as
content copiers that follow them (e.g., this site's PUBLISH.py).
Run this on a folder tree to redirect all its root symlinks.
© M.Lutz (learning-python.com) 2020, no warranties provided.
Caveats:
1) This does not copy modtimes or permissions as is because
they're irrelevant to the handful of links adjusted
2) Symlinks are notoriously platform specific; read more at:
learning-python.com/ziptools/ziptools/_README.html#Platforms
3) Absolute symlinks won't usually work on external drives or
other computers; use relative paths (e.g., '..') if possible
Coding notes: this cannot just loop over fileshere: os.walk()
doesn't return links to folders there; if [not followlinks],
[fileshere + subshere] works, but os.listdir() works as well.
Add an os.path.exists(oldlink) to detect/drop broken links.
=================================================================
"""
import sys, os
assert len(sys.argv) == 2, "Please pass in a folder tree's path"
folder = sys.argv[1]
# change these
LISTONLY = True # True = show, but don't mod
OLD_ROOT = '/MY-STUFF' # pre-catalina root-paths start
NEW_ROOT = '/Users/me' # prepend this to old root paths
nlinks = nfixes = 0
for (thisdir, subshere, fileshere) in os.walk(folder, followlinks=False):
for item in os.listdir(thisdir):
path = os.path.join(thisdir, item)
if os.path.islink(path):
nlinks += 1
oldlink = os.readlink(path)
context = '%s => %s' % (path, oldlink)
if not oldlink.startswith(OLD_ROOT):
print('Non-root: ', context)
elif LISTONLY:
print('Unchanged: ', context)
else:
try:
nfixes += 1
newlink = NEW_ROOT + oldlink
os.remove(path)
os.symlink(newlink, path)
print('Redirected:', context)
except:
print('Cannot fix:', context)
print('Links found:', nlinks)
print('Links fixed:', nfixes)
"""
=================================================================
Example output:
~/MY-STUFF/Websites$ py3 fix-symlinks.py .
Redirected: ./Posts/Current/_shrinkpix-info.txt => /MY-STUFF/Websites/_admin/Top-Level-Image-Shrinks-mar1020.txt
Non-root: ./Posts/Current/TOOLS.txt => ../../Books/Current/TOOLS.txt
...etc...
Redirected: ./OldMain/Current/_shrinkpix-info.txt => /MY-STUFF/Websites/_admin/Top-Level-Image-Shrinks-mar1020.txt
Non-root: ./OldMain/Current/TOOLS.txt => ../../Books/Current/TOOLS.txt
Non-root: ./OldMain/Current/Complete/_main.css => ../../../Books/Current/Complete/_main.css
Links found: 20
Links fixed: 7
~/MY-STUFF/Websites$ py3 fix-symlinks.py .
Non-root: ./Posts/Current/_shrinkpix-info.txt => /Users/me/MY-STUFF/Websites/_admin/Top-Level-Image-Shrinks-mar1020.txt
Non-root: ./Posts/Current/TOOLS.txt => ../../Books/Current/TOOLS.txt
..etc...
Non-root: ./OldMain/Current/_shrinkpix-info.txt => /Users/me/MY-STUFF/Websites/_admin/Top-Level-Image-Shrinks-mar1020.txt
Non-root: ./OldMain/Current/TOOLS.txt => ../../Books/Current/TOOLS.txt
Non-root: ./OldMain/Current/Complete/_main.css => ../../../Books/Current/Complete/_main.css
Links found: 20
Links fixed: 0
=================================================================
"""