[file_utils] unln: handle exceptions, list errors

This commit is contained in:
Matteo Cypriani 2013-04-09 00:49:25 -04:00
parent 7879f04f15
commit eb37c52e8c
2 changed files with 44 additions and 9 deletions

View File

@ -25,6 +25,11 @@ equivalent of (cp -p file tmp && mv tmp file). Checks are done in order
to work only on regular files which have more than one hard link, so the
minimal amount of copies are done.
A summary of the file names on which error were raised is displayed at
the end of the execution. Note that if a file is under a directory which
is not executable (i.e. the program can't cd in), it will be considered
as inexistant and ignored, and its name won't be displayed.
/!\ Warning: the copy function used is shutil.copy2(), which tries to
preserve all the metadata it can, but as of Python 3.3 it is not
guaranteed that extended attributes will be preserved. See the

View File

@ -29,10 +29,8 @@
# inodes by doing the equivalent of (cp -p file tmp && mv tmp file).
#
# TODO:
# - Handle exceptions (permission denied on copy, etc.)
# - Option verbose
# - Option to follow symlinks
# - Summarise skipped regular files with more than one link
import sys
@ -90,6 +88,10 @@ usage_string = "Usage:\n\t{} <file1> [file2 ...]".format(progname)
# Error codes
ERR_BAD_USAGE = 1
# List of filenames that were supposed to be worked on but could not because of
# an error
error_filenames = []
# Check command-line arguments
if len(sys.argv) < 2:
@ -123,17 +125,31 @@ for filename in sys.argv[1:]:
# Reserve a temporary file name
dirname = os.path.dirname(filename)
handle, tmpfilename = tempfile.mkstemp(
prefix="{}-".format(filename),
suffix="-{}.tmp".format(progname),
dir=dirname)
try:
handle, tmpfilename = tempfile.mkstemp(
prefix="{}-".format(filename),
suffix="-{}.tmp".format(progname),
dir=dirname)
except:
warn("Cannot create temporary file to copy {}:".format(quote(filename)),
sys.exc_info()[1])
error_filenames.append(filename)
continue
os.close(handle) # we just need a temporary file name
# Copy the original file to the temporary file
verbose("Copying to temporary file", quote(tmpfilename), end="... ")
shutil.copy2(filename, tmpfilename) # cp -p
try:
shutil.copy2(filename, tmpfilename) # cp -p
except:
warn("Cannot copy {} to {}:".format(quote(filename), quote(tmpfilename)),
sys.exc_info()[1])
error_filenames.append(filename)
# Delete temporary file
os.remove(tmpfilename)
continue
# Try to sync if we are running Python >= 3.3
# Sync (if we are running Python >= 3.3)
if sys.version_info.minor >= 3:
verbose("Syncing", end="... ")
os.sync()
@ -141,4 +157,18 @@ for filename in sys.argv[1:]:
# Rename the temporary file with the original file name
verbose("Moving back {} to {}...".format(
quote(tmpfilename), quote(filename)))
os.rename(tmpfilename, filename)
try:
os.rename(tmpfilename, filename)
except:
warn("Cannot move {} to {}:".format(
quote(tmpfilename), quote(filename)),
sys.exc_info()[1], "Deleting temporary file", quote(tmpfilename))
error_filenames.append(filename)
# Delete temporary file
os.remove(tmpfilename)
# Display the list of files in error
if len(error_filenames):
warn("The following regular files still have more than one hard link:")
print(*error_filenames, sep="\n", file=sys.stderr)