[file_utils] unln: handle exceptions, list errors
This commit is contained in:
parent
7879f04f15
commit
eb37c52e8c
|
@ -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
|
to work only on regular files which have more than one hard link, so the
|
||||||
minimal amount of copies are done.
|
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
|
/!\ 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
|
preserve all the metadata it can, but as of Python 3.3 it is not
|
||||||
guaranteed that extended attributes will be preserved. See the
|
guaranteed that extended attributes will be preserved. See the
|
||||||
|
|
|
@ -29,10 +29,8 @@
|
||||||
# inodes by doing the equivalent of (cp -p file tmp && mv tmp file).
|
# inodes by doing the equivalent of (cp -p file tmp && mv tmp file).
|
||||||
#
|
#
|
||||||
# TODO:
|
# TODO:
|
||||||
# - Handle exceptions (permission denied on copy, etc.)
|
|
||||||
# - Option verbose
|
# - Option verbose
|
||||||
# - Option to follow symlinks
|
# - Option to follow symlinks
|
||||||
# - Summarise skipped regular files with more than one link
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
@ -90,6 +88,10 @@ usage_string = "Usage:\n\t{} <file1> [file2 ...]".format(progname)
|
||||||
# Error codes
|
# Error codes
|
||||||
ERR_BAD_USAGE = 1
|
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
|
# Check command-line arguments
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
|
@ -123,17 +125,31 @@ for filename in sys.argv[1:]:
|
||||||
|
|
||||||
# Reserve a temporary file name
|
# Reserve a temporary file name
|
||||||
dirname = os.path.dirname(filename)
|
dirname = os.path.dirname(filename)
|
||||||
handle, tmpfilename = tempfile.mkstemp(
|
try:
|
||||||
prefix="{}-".format(filename),
|
handle, tmpfilename = tempfile.mkstemp(
|
||||||
suffix="-{}.tmp".format(progname),
|
prefix="{}-".format(filename),
|
||||||
dir=dirname)
|
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
|
os.close(handle) # we just need a temporary file name
|
||||||
|
|
||||||
# Copy the original file to the temporary file
|
# Copy the original file to the temporary file
|
||||||
verbose("Copying to temporary file", quote(tmpfilename), end="... ")
|
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:
|
if sys.version_info.minor >= 3:
|
||||||
verbose("Syncing", end="... ")
|
verbose("Syncing", end="... ")
|
||||||
os.sync()
|
os.sync()
|
||||||
|
@ -141,4 +157,18 @@ for filename in sys.argv[1:]:
|
||||||
# Rename the temporary file with the original file name
|
# Rename the temporary file with the original file name
|
||||||
verbose("Moving back {} to {}...".format(
|
verbose("Moving back {} to {}...".format(
|
||||||
quote(tmpfilename), quote(filename)))
|
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)
|
||||||
|
|
Loading…
Reference in New Issue