Skip to content

Instantly share code, notes, and snippets.

@JakeWharton
Created April 13, 2011 15:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save JakeWharton/917815 to your computer and use it in GitHub Desktop.
Save JakeWharton/917815 to your computer and use it in GitHub Desktop.
Python script to facilitate the hardlinking of directories and their files.
#!/usr/bin/env python
#
# Python script to facilitate the hardlinking of directories and their files.
import sys
if sys.version_info < (2, 3):
raise RuntimeError('Python 2.3+ is required.')
import logging
import optparse
import os
version = '\nhardlinkify-1.0 - by Jake Wharton <jakewharton@gmail.com>\n'
#CLI argument parser
parser = optparse.OptionParser(usage='Usage: %prog [options] source [source ...] destination', version=version)
parser.add_option('-d', '--debug', dest='is_debug', action='store_true', default=False, help='print detailed execution information')
parser.add_option('-q', '--quiet', dest='is_quiet', action='store_true', default=False, help='no output except warnings and errors')
options, targets = parser.parse_args()
#Print version if not quiet
if not options.is_quiet:
parser.print_version()
#Logging setup
level = logging.INFO
if options.is_quiet:
level = logging.WARN
if options.is_debug:
level = logging.DEBUG
logging.basicConfig(format='%(asctime)s %(levelname)-7s %(message)s')
logger = logging.getLogger(os.path.basename(__file__))
logger.setLevel(level)
#Error checking
if options.is_quiet and options.is_debug:
logger.warn('Options --quiet and --debug are mutually exclusive. Ignoring --quiet.')
if len(targets) < 2:
logger.error('You must specify at least one source and the destination.')
sys.exit(1)
def link(file=None, dest=None, unroll=False):
if file is None:
raise ValueError('File parameter is required.')
if dest is None:
raise ValueError('Destination parameter is required.')
if not os.path.exists(file):
logger.warn('File "%s" does not exist. Skipping.', file)
return
elif os.path.isdir(file):
if unroll:
logger.debug('Unrolling directory "%s" in "%s".', file, dest)
new_dest = dest if unroll else os.path.join(dest, os.path.basename(file))
for new_file_name in os.listdir(file):
new_file = os.path.join(file, new_file_name)
logger.debug('> link("%s", "%s")', new_file, new_dest)
link(new_file, new_dest)
return
if not os.path.exists(dest):
logger.info('Directory "%s" does not exist. Creating.', dest)
os.makedirs(dest)
new_file = os.path.join(dest, os.path.basename(file))
if os.path.exists(new_file):
logger.warn('File "%s" already exists. Skipping.', new_file)
return
logger.info('Linking "%s" to "%s"...', new_file, file)
os.link(file, new_file)
#Get arguments and perform linking
destination = os.path.abspath(targets[-1])
logger.debug('Destination: %s', destination)
files = [os.path.abspath(file) for file in targets[:-1]]
for file in files:
logger.debug('File: %s', file)
for file in files:
logger.debug('> link("%s", "%s")', file, destination)
link(file, destination, True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment