When renaming a symlink, its source file with matching name is renamed as well

This commit is contained in:
Karl Voit 2017-08-29 17:55:57 +02:00
parent 95a22cf2a5
commit 4e40543e44
2 changed files with 150 additions and 4 deletions

View file

@ -1,4 +1,4 @@
## Time-stamp: <2016-09-11 17:43:41 vk>
## Time-stamp: <2017-08-29 17:55:01 vk>
## -*- coding: utf-8 -*-
## This file is best viewed with GNU Emacs Org-mode: http://orgmode.org/
@ -71,6 +71,54 @@ For a complete list of parameters, please try:
The file names within the current working directory is read in and all
found words can be completed via TAB.
-----------------------
#+BEGIN_SRC sh :results output :wrap src
./appendfilename.py --help
#+END_SRC
#+BEGIN_src
Usage:
./appendfilename.py [<options>] <list of files>
This tool inserts text between the old file name and optional tags or file extension.
Text within file names is placed between the actual file name and
the file extension or (if found) between the actual file namd and
a set of tags separated with " -- ".
Update for the Boss <NEW TEXT HERE>.pptx
2013-05-16T15.31.42 Error message <NEW TEXT HERE> -- screenshot projectB.png
When renaming a symbolic link whose source file has a matching file
name, the source file gets renamed as well.
Example usages:
./appendfilename.py --text="of projectA" "the presentation.pptx"
... results in "the presentation of projectA.pptx"
./appendfilename.py "2013-05-09T16.17_img_00042 -- fun.jpeg"
... with interactive input of "Peter" results in:
"2013-05-09T16.17_img_00042 Peter -- fun.jpeg"
:copyright: (c) 2013 or later by Karl Voit <tools@Karl-Voit.at>
:license: GPL v3 or any later version
:URL: https://github.com/novoid/filetag
:bugreports: via github or <tools@Karl-Voit.at>
:version: 2017-08-29
Options:
-h, --help show this help message and exit
-t TEXT, --text=TEXT the text to add to the file name
-s, --dryrun enable dryrun mode: just simulate what would happen,
do not modify file(s)
-v, --verbose enable verbose mode
-q, --quiet enable quiet mode
--version display version and exit
#+END_src
** Installation
Get it from [[https://github.com/novoid/appendfilename][GitHub]] or install it via «pip install appendfilename».

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
PROG_VERSION = u"Time-stamp: <2017-08-29 14:42:43 vk>"
PROG_VERSION = u"Time-stamp: <2017-08-29 17:54:10 vk>"
# TODO:
# * fix parts marked with «FIXXME»
@ -25,6 +25,7 @@ INVOCATION_TIME = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime())
FILENAME_TAG_SEPARATOR = ' -- ' # between file name and (optional) list of tags
BETWEEN_TAG_SEPARATOR = ' ' # between tags (not that relevant in this tool)
TEXT_SEPARATOR = ' ' # between old file name and inserted text
RENAME_SYMLINK_ORIGINALS_WHEN_RENAMING_SYMLINKS = True # if current file is a symlink with the same name, also rename source file
USAGE = "\n\
" + sys.argv[0] + " [<options>] <list of files>\n\
@ -39,6 +40,9 @@ a set of tags separated with \"" + FILENAME_TAG_SEPARATOR + "\".\n\
2013-05-16T15.31.42 Error message" + TEXT_SEPARATOR + "<NEW TEXT HERE>" \
+ FILENAME_TAG_SEPARATOR + "screenshot" + BETWEEN_TAG_SEPARATOR + "projectB.png\n\
\n\
When renaming a symbolic link whose source file has a matching file\n\
name, the source file gets renamed as well.\n\
\n\
Example usages:\n\
" + sys.argv[0] + " --text=\"of projectA\" \"the presentation.pptx\"\n\
... results in \"the presentation" + TEXT_SEPARATOR + "of projectA.pptx\"\n\
@ -173,12 +177,97 @@ def locate_and_parse_controlled_vocabulary():
return False
def is_broken_link(name):
"""
This function determines if the given name points to a file that is a broken link.
It returns False for any other cases such as non existing files and so forth.
@param name: an unicode string containing a file name
@param return: boolean
"""
if os.path.isfile(name):
return False
try:
return not os.path.exists(os.readlink(name))
except FileNotFoundError:
return False
def is_nonbroken_symlink_file(filename):
"""
Returns true if the filename is a non-broken symbolic link and not just an ordinary file. False, for any other case like no file at all.
@param filename: an unicode string containing a file name
@param return: bookean
"""
if os.path.isfile(filename):
if os.path.islink(filename):
return True
else:
return False
def get_link_source_file(filename):
"""
Return a string representing the path to which the symbolic link points.
@param filename: an unicode string containing a file name
@param return: file path string
"""
assert(os.path.islink(filename))
return os.readlink(filename)
def handle_file_and_symlink_source_if_found(filename, text, dryrun):
"""
Wraps handle_file() so that if the current filename is a symbolic link,
modify the source file and re-link its new name before handling the
current filename.
@param filename: string containing one file name
@param text: string that shall be added to file name(s)
@param dryrun: boolean which defines if files should be changed (False) or not (True)
@param return: error value or new filename
"""
# if filename is a symbolic link and has same basename, tag the source file as well:
if RENAME_SYMLINK_ORIGINALS_WHEN_RENAMING_SYMLINKS and is_nonbroken_symlink_file(filename):
old_sourcefilename = get_link_source_file(filename)
if os.path.basename(old_sourcefilename) == os.path.basename(filename):
new_sourcefilename = handle_file(old_sourcefilename, text, dryrun)
if old_sourcefilename != new_sourcefilename:
logging.info('Renaming the symlink-destination file of "' + filename + '" ("' +
old_sourcefilename + '") as well …')
if options.dryrun:
logging.debug('I would re-link the old sourcefilename "' + old_sourcefilename +
'" to the new one "' + new_sourcefilename + '"')
else:
logging.debug('re-linking symlink "' + filename + '" from the old sourcefilename "' +
old_sourcefilename + '" to the new one "' + new_sourcefilename + '"')
os.remove(filename)
os.symlink(new_sourcefilename, filename)
else:
logging.debug('The old sourcefilename "' + old_sourcefilename + '" did not change. So therefore I don\'t re-link.')
else:
logging.debug('The file "' + os.path.basename(filename) + '" is a symlink to "' + old_sourcefilename +
'" but they two do have different basenames. Therefore I ignore the original file.')
# after handling potential symlink originals, I now handle the file we were talking about in the first place:
return handle_file(filename, text, dryrun)
def handle_file(filename, text, dryrun):
"""
@param filename: one file name
@param text: string that shall be added to file name(s)
@param dryrun: boolean which defines if files should be changed (False) or not (True)
@param return: error value
@param return: error value or new filename
"""
assert(isinstance(filename, str))
@ -216,6 +305,8 @@ def handle_file(filename, text, dryrun):
except:
error_exit(9, "Error while trying to rename file: " + str(sys.exc_info()))
return new_filename
def main():
"""Main function"""
@ -272,7 +363,14 @@ def main():
logging.debug("iterate over files ...")
for filename in files:
handle_file(filename, text, options.dryrun)
if is_broken_link(filename):
# skip broken links completely and write error message:
logging.error('File "' + filename + '" is a broken symbolic link. Skipping this one …')
else:
# if filename is a symbolic link, tag the source file as well:
handle_file_and_symlink_source_if_found(filename, text, options.dryrun)
logging.debug("successfully finished.")
if options.verbose: