Skip to content

Instantly share code, notes, and snippets.

@bow
Last active August 29, 2015 14:04
Show Gist options
  • Save bow/0be86b00f7db6af41dd6 to your computer and use it in GitHub Desktop.
Save bow/0be86b00f7db6af41dd6 to your computer and use it in GitHub Desktop.
Script for changing all absolute links in a given directory to symlinks, UNIX only
#!/usr/bin/env python
"""
Script for changing all absolute links in a given directory to symlinks, UNIX only.
Requirements:
* Python >= 2.7.x or Python 3.x
Author:
Wibowo Arindrarto <bow@bow.web.id>
No Rights Reserved. This script is in the public domain.
"""
from __future__ import print_function
import argparse
import os
from os import path
def is_abs_link(fname):
return path.islink(fname) and path.isabs(os.readlink(fname)) and path.exists(fname)
def calc_relative(frm, to):
# outputs relative path from frm to to
# both inputs must be absolute paths
common = path.commonprefix([path.dirname(frm), path.dirname(to)]) + path.sep
if not common.endswith(path.sep):
common += path.sep
to_uniq = to.replace(common, "")
frm_uniq = frm.replace(common, "")
dist = max(0, len(frm_uniq.split(path.sep)) - 1)
if dist > 0:
return (".." + path.sep) * dist + to_uniq
return to_uniq
if __name__ == "__main__":
usage = __doc__.split("\n\n\n")
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=usage[0], epilog=usage[1])
parser.add_argument("target_dir", type=str, help="Root directory to search for symlinks")
parser.add_argument("--not-dry", dest="dry", default=True,
action="store_false", help="Whether to do a dry run or not [default: do a dry run]")
args = parser.parse_args()
count = 0
for curdir, dirs, files in os.walk(args.target_dir):
files = [path.join(curdir, x) for x in files]
for f in [x for x in files if is_abs_link(x)]:
link_canon = path.abspath(path.expanduser(f))
target_canon = os.readlink(link_canon)
target_rel = calc_relative(link_canon, target_canon)
link_dir = path.dirname(link_canon)
if not args.dry:
# make sure link is writeable
if os.access(link_canon, os.W_OK):
os.remove(link_canon)
else:
print("Can not update '{0}'".format(link_canon))
continue
# need +wx bits for writing files in directory
if os.access(link_dir, os.W_OK) and os.access(link_dir, os.X_OK):
os.symlink(target_rel, link_canon)
else:
print("Can not write symlink in '{0}'".format(link_dir))
continue
print("Updated '{0}' to point to '{1}'".format(link_canon, target_rel))
count += 1
else:
print("Symlink target: '{0}', link name: '{1}'".format(target_rel, link_canon))
count += 1
if not args.dry:
print("Total updated symlinks:", count)
else:
print("Total symlinks to update:", count)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment