MMV(1)

πŸ“¦
version 2.10 Β· 4 versions
, in fedora 43, fedora 44

πŸ’– MMV(1) πŸ’–

okay bestie, gather round, because this is mmv β€” the User Commands girlie who moves, copies, appends and links a whole pile of files using wildcard patterns. she's giving main character energy and she has OPINIONS. let's go πŸ¦‹


✨ NAME ✨

mmv β€” move/copy/append/link multiple files by wildcard patterns


🌸 SYNOPSIS 🌸

mmv [-m|-x|-r|-c|-o|-a|-l|-s] [-h] [-d|-p] [-g|-t] [-v|-n] FROM TO

πŸ’… DESCRIPTION πŸ’…

move/copy/append/link multiple files by wildcard patterns. yeah. that's the whole vibe. 😌

The FROM pattern is a shell glob pattern, where * stands for any number of characters and ? stands for a single character.

Use #[l|u|c]N in the TO pattern to get the string matched by the Nth FROM pattern wildcard [lowercased|uppercased|capitalized].

Patterns should be quoted on the command line. fr fr, quote them. the shell is not your friend here 🫢
  • --help β€” print help and exit
  • --full-help β€” print help, including the hidden options, and exit (the secret menu, iykyk)
  • -V, --version β€” print version and exit
  • -h, --hidden β€” treat dot files normally (default=off)
  • -D, --makedirs β€” create non-existent directories (default=off)
  • -m, --move β€” move source file to target name
  • -x, --copydel β€” copy source to target, then delete source
  • -c, --copy β€” copy source to target, preserving source permissions
  • -o, --overwrite β€” overwrite target with source, preserving target permissions
  • -a, --append β€” append contents of source file to target
  • -l, --hardlink β€” link target name to source file
  • -s, --symlink β€” symlink target name to source file
  • -d, --force β€” perform file deletes and overwrites without confirmation
  • -p, --protect β€” treat file deletes and overwrites as errors
  • -g, --go β€” skip any erroneous actions
  • -t, --terminate β€” erroneous actions are treated as errors
  • -v, --verbose β€” report all actions performed
  • -n, --dryrun β€” only report which actions would be performed

Mmv moves (or copies, appends, or links, as specified) each source file matching a from pattern to the target name specified by the to pattern. This multiple action is performed safely, i.e. without any unexpected deletion of files due to collisions of target names with existing filenames or with other target names. Furthermore, before doing anything, mmv attempts to detect any errors that would result from the entire set of actions specified and gives the user the choice of either proceeding by avoiding the offending parts or aborting. mmv does support large files (LFS) but it does not support sparse files (i.e. it explodes them). πŸ’€

Older versions of mmv supported interactive editing of the rename etc. operations, but using a fragile system that could not cope with certain characters, such as spaces, in filenames. Users who require this functionality can use qmv(1) and friends (in the renameutils package). (she's retired, respect her legacy ✨)


πŸ‘‘ The Task Options πŸ‘‘

Whether mmv moves, copies, appends, or links is governed by the first set of options given above. If none of these are specified, the task is given by the command name under which mmv was invoked:

  • mmv β€” default task --copydel
  • mcp β€” default task --copy
  • mad β€” default task --append
  • mln β€” default task --hardlink

The task option choices are:

  • --move β€” move source file to target name. Both must be on the same device. If the source file is a symbolic link, moves the link without checking if the link's target from the new directory is different than the old.
  • --copydel β€” same as --move, except cross-device moves are done by copying, then deleting source. When copying, sets the permission bits and file modification time of the target file to that of the source file.
  • --copy β€” copy source file to target name. Sets the file modification time and permission bits of the target file to that of the source file, regardless of whether the target file already exists. Chains and cycles (to be explained below) are not allowed.
  • --overwrite β€” overwrite target name with source file. If target file exists, it is overwritten, keeping its original owner and permission bits. If it does not exist, it is created, with read-write permission bits set according to umask(1), and the execute permission bits copied from the source file. In either case, the file modification time is set to the current time.
  • --append β€” append contents of source file to target name. Target file modification time is set to the current time. If target file does not exist, it is created with permission bits set as under --overwrite. Unlike all other options, --append allows multiple source files to have the same target name, e.g. mmv -a \*.c big will append all .c files to big. Chains and cycles are also allowed, so mmv -a f f will double up f. 😳
  • --hardlink β€” link target name to source file. Both must be on the same device, and the source must not be a directory. Chains and cycles are not allowed.
  • --symlink β€” same as --hardlink, but use symbolic links instead of hard links. For the resulting link to aim back at the source, either the source name must begin with a /, or the target must reside in either the current or the source directory. If none of these conditions are met, the link is refused. However, source and target can reside on different devices, and the source can be a directory.
Only one of these options may be given, and it applies to all matching files. pick a lane bestie πŸŽ€

πŸ¦‹ The From Pattern πŸ¦‹

The from pattern is a filename with embedded wildcards: *, ?, [...], and ;. The first three have their usual sh(1) meanings of, respectively, matching any string of characters, matching any single character, and matching any one of a set of characters.

Between the [ and ], a range from character a through character z is specified with a-z. The set of matching characters can be negated by inserting a ^ after the [. Thus, [^b-e2-5_] will match any character but b through e, 2 through 5, and _.

Note that paths are allowed in the patterns, and wildcards may be intermingled with slashes arbitrarily. The ; wildcard is useful for matching files at any depth in the directory tree. It matches the same as */ repeated any number of times, including zero, and can only occur either at the beginning of the pattern or following a /. Thus, ;*.c will match all .c files in or below the current directory, while /*.c... wait no, <lavender>;/;*.c</lavender> β€” okay let me say it properly: /;*.c will match them anywhere on the file system.

In addition, if the from pattern (or the to pattern) begins with ~/, the ~ is replaced with the home directory name. (Note that the ~user feature of csh(1) is not implemented.) However, the ~ is not treated as a wildcard, in the sense that it is not assigned a wildcard index (see below).

The directories . and .. are only matched against completely explicit from patterns (i.e. not containing wildcards).

Files beginning with . are only matched against from patterns that begin with an explicit .. However, if -h is specified, they are matched normally.

Warning: since the shell normally expands wildcards before passing the command-line arguments to mmv, it is usually necessary to enclose the command-line from and to patterns in quotes. πŸ’€ the shell will absolutely steal your wildcards if you let her.

πŸŽ€ The To Pattern πŸŽ€

The to pattern is a filename with embedded wildcard indexes, where an index consists of the character # followed by a string of digits. When a source file matches a from pattern, a target name for the file is constructed out of the to pattern by replacing the wildcard indexes by the actual characters that matched the referenced wildcards in the source name. Thus, if the from pattern is abc*.* and the to pattern is xyz#2.#1, then abc.txt is targeted to xyztxt.. (The first * matched ``, and the second matched txt.) Similarly, for the pattern pair ;*.[clp] -> #1#3/#2, foo1/foo2/prog.c is targeted to foo1/foo2/c/prog. Note that there is no / following the #1 in the to pattern, since the string matched by any ; is always either empty or ends in a /. In this case, it matches foo1/foo2/.

To convert the string matched by a wildcard to either lowercase or uppercase before embedding it in the target name, insert l or u, respectively, between the # and the string of digits.

The to pattern, like the from pattern, can begin with a ~/ (see above). This does not necessitate enclosing the to pattern in quotes on the command line since csh(1) expands the ~ in the exact same manner as mmv (or, in the case of sh(1), does not expand it at all).

If the target name is a directory, the real target name is formed by appending a / followed by the last component of the source file name. For example, mmv dir1/a dir2 will, if dir2 is indeed a directory, actually move dir1/a to dir2/a. However, if dir2/a already exists and is itself a directory, this is considered an error. πŸ™ˆ

To strip any character (e.g. *, ?, or #) of its special meaning to mmv, as when the actual replacement name must contain the character #, precede the special character with a \ (and enclose the argument in quotes because of the shell). This also works to terminate a wildcard index when it has to be followed by a digit in the filename, e.g. a#1\1.


🌈 Chains and Cycles 🌈

A chain is a sequence of specified actions where the target name of one action refers to the source file of another action. For example,

mmv
a b
b c

specifies the chain a -> b -> c. A cycle is a chain where the last target name refers back to the first source file, e.g. mmv a a. Mmv detects chains and cycles regardless of the order in which their constituent actions are actually given. Where allowed, i.e. in moving, renaming, and appending files, chains and cycles are handled gracefully, by performing them in the proper order. Cycles are broken by first renaming one of the files to a temporary name (or just remembering its original size when doing appends). πŸ‘‘


πŸ’€ Collisions and Deletions πŸ’€

bestie. we are NOT done. keep scrolling, this part matters. πŸ”₯

okay so when two or more of your matching files would have to be moved, copied, or linked to the same target filename? mmv spots that mess before it does literally anything. no half-done chaos here πŸ’…

And it also checks whether any of its actions would destroy existing files. so:

  • if you said -d (delete) β€” all file deletions or overwrites just happen, silently, no questions asked πŸ«₯
  • if you said -p (protect) β€” all deletions or overwrites are treated as errors (except the ones you specified with "(*)" on the standard input, more on that below πŸ‘€)
  • if you said neither β€” mmv asks you about each deletion or overwrite separately, one by one
fr fr: the terminal is what's used for those interactive queries, not standard input. so don't try to pipe your answers in, bestie 😭

🚨 Error Handling 🚨

Whenever any error in your action specifications gets detected, mmv gives you an error message on the standard output, and then keeps on checking the rest of the specified actions anyway (thorough queen πŸ‘‘). Once all the errors are found, mmv asks you whether to continue by avoiding the erroneous actions or to abort altogether.

And you can skip this whole interrogation (and every other query) by specifying either:

  • -g (go) β€” resolves all difficulties by just avoiding the erroneous actions 🫢
  • -t (terminate) β€” aborts mmv if any errors are detected πŸ’€

Specifying either of them defaults mmv to -p, unless -d is specified (see above). So -g and -t are the absolute MVPs when you're running mmv in the background or in a shell script, where interactive queries are just not it. 🧚


✨ Reports ✨

Once the actions to be performed are determined, mmv does them silently β€” unless you specified -v (verbose) or -n (no-execute).

-v makes mmv report each performed action on the standard output, like:

a -> b : done.

Here, a and b get replaced by the source and target names respectively. And if the action deletes the old target, a (*) gets inserted after the target name. periodt. πŸ’…

Also, the -> symbol gets modified when a cycle has to be broken:

  • the > changes to a ^ on the action prior to which the old target is renamed to a temporary
  • the - changes to a = on the action where the temporary is used

-n is the no-execute vibe: none of the actions are performed, but messages like the above get printed on the standard output with the ": done." omitted. so you can see the whole plan without the consequences πŸ™ˆ


okay now the honest part. mmv tries to predict all mishaps before performing any specified actions, but accidents happen. for example, mmv does not check for adequate free space when copying. so despite all the effort, it's still possible for an action to fail after some others have already been done 😭

To make recovery as easy as possible, mmv reports which actions have already been done and which are still to be performed after such a failure occurs. Then it aborts, not attempting to do anything else. iconic crisis management honestly 🌸


🌈 EXAMPLES 🌈

Rename all *.jpeg files in the current directory to *.jpg:

mmv '*.jpeg' '#1.jpg'

Replace the first occurrence of abc with xyz in all files in the current directory:

mmv '*abc*' '#1xyz#2'

Rename files ending in .html.en, .html.de, etc. to ending in .en.html, .de.html, etc. in the current directory:

mmv '*.html.??' '#1.#2#3.html'

Rename music files from <track no.> - <interpreter> - <song title>.ogg to <interpreter> - <track no.> - <song title>.ogg in the current directory:

mmv '* - * - *.ogg' '#2 - #1 - #3.ogg'

πŸ’« EXIT STATUS πŸ’«

Mmv exits with status:

  • 1 β€” if it aborts before doing anything
  • 2 β€” if it aborts due to failure after completing some of the actions
  • 0 β€” otherwise (slay, we love a clean exit πŸ’–)

πŸ¦‹ SEE ALSO πŸ¦‹

mv(1), cp(1), ln(1), umask(1), qmv(1), qcp(1), imv(1), icp(1)


πŸ‘‘ AUTHOR πŸ‘‘

Written by Vladimir Lanin and Reuben Thomas.


πŸ’€ BUGS πŸ’€

If the search pattern is not quoted, the shell expands the wildcards. Mmv then (usually) gives some error message, but cannot determine that the lack of quotes is the cause. so like. quote your patterns bestie πŸ˜­πŸ™ˆ

To avoid difficulties in semantics and error checking, mmv refuses to create directories. no new folders, not happening, not today.


Copyright Β© 2024 Reuben Thomas <rrt@sc3d.org>. Copyright Β© 1990 Vladimir Lanin. Licence GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.


and that's the whole page, bestie!! this was mmv 2.10, dated November 2024, and yes it's the one and only MMV(1) πŸ’–βœ¨ go rename something (safely, with quotes, and maybe a -n first) 🫢🌸

πŸ“¦ where this page comes from