Because one mv is rarely enough
Project description
mvs: Because one mv is rarely enough
Motivation
Renaming a bunch of files and directories can be tedious, error-prone work. Command-line tools to perform such tasks are numerous. Perhaps the most classic example was the Perl rename script, which has been available or installable on most Unix-inspired operating systems since the early 1990s.
The core idea of rename
was excellent. The user supplied a snippet of Perl
code as a command-line argument, followed by the original paths. Each original
path was pumped through the code snippet to generate the corresponding new
path. Because Perl was designed to make it easy to manipulate strings with very
little code, users could efficiently rename paths directly on the command line.
Even if you hardly knew Perl but at least understood how to operate its compact
regular-expression substitution syntax, you could become quite adept at bulk
path renaming.
$ rename 's/foo/bar/' *
Unfortunately, the script was a chainsaw – undeniably useful, but able to
inflict devastation after a single false move. As a result, I rarely used
rename
directly for my bulk renaming needs, which were extensive on several
projects I worked on. Instead, I wrote my own Perl script to the job. Its
operation was roughly the same, but it included precautions to help me avoid
disastrous mistakes. The most important were checking that the new paths did
not collide with existing paths on the file system and including an inspection
and confirmation step by default.
The mvs
library is an updated and enhanced version of those ideas, but
implemented in a language I use regularly (Python) rather than one in which
I have become rusty (Perl).
The mvs executable
The primary use case envisioned for the library is its executable. In broad
terms, there are two ways to perform bulk renaming with the mvs
command: (1)
the user provides original file paths and a snippet of Python code to perform
the original-to-new computation, or (2) the user provides both original paths
and new paths directly.
Either way, before any renaming occurs, the following checks occur: are the original paths different than their corresponding new paths; do all of the original paths exist; do any new paths already exist; do any new paths collide with each other; and are the parent directories of any new paths missing? If those checks look alright, the proposed renamings are listed for inspection by the user, and renaming occurs only after confirmation.
The script provides command-line options to customize its behavior:
-
Supply input paths in various ways: positional arguments, STDIN, a text data file, or the clipboard.
-
Specify the structure of the input paths data: a flat sequence, two blank-delimited paragraphs, alternating pairs of lines, or delimited rows.
-
Use a snippet of Python code to filter out original paths before renaming, which can be handy if you want to supply paths via a command-line glob pattern but do not want to rename all of them.
-
Specify in advance how the program should respond to the validation problems listed above: skip the item with the problem, rename in spite of the problem (even it that means overwriting other paths), or take remedial action (create a missing parent).
-
Customize the start and skip values for a sequence number that can be used in the renaming code snippet.
-
Request dryrun mode, which executes the filtering, checking, and listing behavior but does not rename anything.
Installation and examples
Install the library in the usual way.
$ pip install mvs
Get help and additional details regarding the options summarized above.
$ mvs --help
$ mvs --details
In general terms, the executable has the following usage. Note that the default
structure is flat and that the --rename
option is considered structural
because it implies that the input path data consists solely of original paths.
mvs SOURCE [STRUCTURE] [OTHER]
PATHS : positionals
SOURCE : PATHS | --stdin | --file PATH | --clipboard
STRUCTURE : --flat | --paragraphs | --pairs | --rows | --rename CODE
OTHER : other options
The different input structures can be illustrated with a simple renaming scenario that adds a file extension to the original paths. Note that if the paths were supplied via a source other than positional arguments, each path should be on its own line.
# The default: a flat sequence of paths.
$ mvs a b a.new b.new
$ mvs a b a.new b.new --flat
# Alternating pairs: old, new, etc.
$ mvs a a.new b b.new --pairs
# Paragraphs delimited by at least one blank.
$ mvs a b '' a.new b.new --paragraphs
The same renaming scenario could also be performed via a code snippet. The
snippet will be compiled into a function that receives the original path as the
local variable o
. See the program's help text for additional details about
user-supplied code.
$ mvs a b --rename 'return r"{o}.new"'
Programmatic usage
The mvs package also supports bulk renaming via a programmatic API. The first
step is to configure a RenamingPlan
. Initialization parameters and their
defaults are as follows.
from mvs import RenamingPlan
plan = RenamingPlan(
# Sequence of paths and their structure.
inputs,
structure = 'flat',
# User-supplied renaming and filtering code (str or callable).
# See mvs --details for additional information.
rename_code = None,
filter_code = None,
# Other parameters related to user-supplied code.
indent = 4,
seq_start = 1,
seq_step = 1,
# Problem controls. For each control mechanism, supply the
# names of the problems to be controlled via the mechanism,
# either as a sequence or space-delimited str.
# See mvs --details for the problem names.
skip = None,
clobber = None,
create = None,
)
plan.rename_paths()
If you do not want to rename paths immediately but do want to prepare everything for renaming, including performing the checks for problems, you can use the library in a more deliberative fashion: first prepare; then check the information provided by the plan; if desired, proceed with renaming; and in the event of unexpected failure, get information about which item led to the exception.
# The library's supported imports.
from mvs import RenamingPlan, MvsError, __version__
# Configure plan.
plan = RenamingPlan(...)
# Prepare for renaming.
plan.prepare()
# All relevant information about the plan and its original-new path pairs.
print(plan.as_dict)
# Whether preparation failed due to problems and what they are.
print(plan.failed)
print(plan.uncontrolled_problems)
# Try to rename.
try:
plan.rename_paths()
except Exception as e:
# The index of the original-new pair that was being renamed
# when the exception occurred. Pairs before that index were
# renamed succesfully; pairs after it were not attempted.
print(plan.tracking_index)
# The offending original-new pair.
print(plan.tracking_rp)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file mvs-1.0.0.tar.gz
.
File metadata
- Download URL: mvs-1.0.0.tar.gz
- Upload date:
- Size: 23.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.9.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 352c3d086545a826480d2a68422ba1ab6ae55fc647db096acf67b57984189b2b |
|
MD5 | 487b89a58b115ae4e73e88cd965a35f7 |
|
BLAKE2b-256 | 7a2fbb403adc96ac25ab09ea31d38cebe76af41a0839bad1f39fa8bae6d25834 |
File details
Details for the file mvs-1.0.0-py3-none-any.whl
.
File metadata
- Download URL: mvs-1.0.0-py3-none-any.whl
- Upload date:
- Size: 21.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.9.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c1211f7c07e7230105c1de7e91a8da7fe20b2fad5be3a9120ebd953275436745 |
|
MD5 | c234573ced67d3e1deaa1b256dbec1b8 |
|
BLAKE2b-256 | 29268fdaf6d90f107d0ea46191ca01bc981398b34d5a0ebadf0e1a34b5d6e18a |