Mmv

Uit De Vliegende Brigade
(Doorverwezen vanaf Bestandsnamen in bulk aanpassen)
Naar navigatie springen Naar zoeken springen

Commando mmv (afkorting van mass move) is verrekte handig voor bulk-aanpassen van bestandsnamen.

De basis

mmv "*.JPG #1.jpg"        # Hernoem *.JPG → *.jpg - Beide argumenten binnen dezelfde "
mmv '*.JP' '#1.jpg'       # Je kunt zowel " als ' gebruiken. In dit geval zijn beide argumenten apart omgeven - Vind ik intuïtiever
# mmv "*.JPG #.jpg"       # Werkt niet: Je moet een volgnummer geven na #, ook als er maar één wildcard wordt gebruikt.

Je kunt ' en " ook weglaten, maar dan moet je * en # escapen met \. Zie sommige voorbeelden hieronder.

mmv \*Logo\* \#1logo\#2   # Hernoem alle bestanden waar de reeks 'Logo' in voorkomt naar 'logo'
mmv \*-\*-\* \#1.\#2-\#3 # Hernoem bestanden waar twee streepjes in voorkomen. Vervang eerste streepje in een punt

oftewel

mmv "*Logo*" "#1logo#2"
mmv "*-*-*"  "#1.#2-#3"

Spaties in bestandsnamen

Op het moment dat je argumenten niet quote met " of ', moet je spaties escapen met een tegenschrap, zodat mmv snapt waar de verschillende argumenten eindigen en beginnen:

mmv naam\ .jpg naam.jpg   # Spatie in from-gedeelte
mmv naam.jpg naam\ .jpg   # Spatie in to-gedeelte.

Maar quoten is vermoedelijk handiger:

mmv "naam .jpg" "naam.jpg"
mmv "naam.jpg"  "naam .jpg"

Strings tussen dubbele aanhalingstekens

Dit is wél handig ivm. escapen van spaties. Bv.:

mmv \*" - REM.flac" \#1.flac
mmv \*"CD 1"\* \#1"CD 2"\#2

Syntaxis: Indentation

Je kunt indentation gebruiken om het leesbaarder te maken. Het karakter om een commando te laten continueren op de volgende regel, is "\" en da's weer niet handig. Voorbeeld:

mmv \
	\*"CD 1"\* \
	\#1"CD 2"\#2

of

mmv \
	\*"CD 1"\*         \
	\#1"CD 2"\#2

Variabelen

Je kunt variabelen gebruiken. Als je die tussen dubbele aanhalingstekens zet, worden eventuele spaties ook nog 's goed verwerkt:

in1="CD 2"
uit1="CD 3"

mmv \*"$in1"\* \#1"$uit1"\#2

Hapklare brokken

Ik heb sterk de indruk dat het 't handigste is om niet alles in één commando te proppen, want te ingewikkeld. Bv.:

# Eerst dit aanpassen
#####################
#
# s1=" - R.E.M..flac"
# s2=".flac"
# mmv \*"$s1" \#1$s2

# En daarna pas dit
###################
#
s1=". "
s2="REM - Live at the Olympia - CD 2 - "

mmv \*"$s1"\*	\
	"$s2"\#1" - "\#2

Convert to lowercase

Alles naar ondercast aanpassen, in één simpel commando:

mmv '*' '#l1'

Convert to uppercase

Converteer de eerste letter van elke bestandsnaam naar uppercase [1] (getest op 2022.06.23):

mmv "[a-z]*\.*" "#u1#2.#3"

Correspondentie:

  1. [a-z] → #1
  2. * → #2
  3. * → #3

Nog een keer (toegepast 2022.07.08). Ik snap niet waarom in het hoofdstuk hierboven, onderscheid wordt gemaakt tussen #2 & #3. Die kunnen toch samengevoegd worden, zoals ik hier doe?

########################################
# MMV - Capitalise words
########################################
#
mmv_capitalise_words()
{
	echo "mmv_capitalise_words()..."
	#
	# Capitalize first letter of file name
	########################################
	#
	mmv ";[a-z]*"	"#1#u2#3"


	# Capitalize first lette after "-"
	########################################
	#
	# * This is experimental - I don't have a use case for this yet
	#
	mmv ";*-[a-z]*"	"#1#2-#u3#4"	
}

Deliminate from-placeholders

Probleem:

mmv -n -- ";3.txt" "#13.TXT"
;3.txt -> #13.TXT : wildcard #13 does not exist.

Oplossing:

mmv -n -- ";3.txt" "#1\3.TXT"

folder/3.txt -> folder/3.TXT

Dry-run & verbose

  • Gebruik switch -n om te testen zonder dat er iets wordt uitgevoerd
  • Gebruik switch -v (verbose) voor feedback
  • Vreemd genoeg mag je deze twee switches niet samen gebruiken.

Recursie

mmv kan overweg met recursie.

In den beginne

Recursie gaat met het ; keyword. De truuk is, dat de betreffende boom ook als een '#x' keyword behandeld moet worden.

Voorbeeld:

$ tree
.
├── 1.txt
├── 2.txt
└── folder
    ├── 3.txt
    └── 4.txt

$ mmv ";*.txt" "#1#2.TXT"

$ tree
.
├── 1.TXT
├── 2.TXT
└── folder
    ├── 3.TXT
    └── 4.TXT

Man page

Eerst wat theorie, voor we met meer ingewikkelde gevallen aan de slag gaan.

Uit de man page:

From-pattern:

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  pat‐
tern  or  following  a '/'.  Thus ";*.c" will match all ".c" files in or below the current directory, while
"/;*.c" will match them anywhere on the file system.


To-pattern:

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/".

Voorbeeld (1)

$ tree
.
├── 1.txt
├── 2.txt
└── folder
    ├── 3.txt
    └── 4.txt

$ mmv ";*.txt" "#1#2.TXT"

Mapping:

  • ;#1
  • *#2.

Voorbeeld man page

Patroon: ";*.[clp]" → "#1#3/#2".

Mapping:

  • ;#1
  • *#2
  • [clp]#3

Wat er gebeurt:

  • Actief binnen de huidige map en bijbehorende submappen
  • #3/: In de betreffende map wordt een submap c, l of p aangemaakt - Vandaar de '/'
  • In die submap wordt het bijbehorende bestand geplaatst, zonder extentie.

Recursie met exact match

In de boom hierboven, wil ik dat alleen de bestandsnaam 3.txt wordt aangepast. De eerste poging lukt niet, omdat mmv niet weet waar de aanduiding van de wildcard eindigt in het to-patroon:

$ mmv -n -- ";3.txt" "#13.TXT"

;3.txt -> #13.TXT : wildcard #13 does not exist.
Nothing done.

Da's simpel opgelost:

$ mmv -n -- ";3.txt" "#1\3.TXT"

Right-side exact match

# mmv -n -- ";*.txt" "#1#2.TXT"

1.txt -> 1.TXT
2.txt -> 2.TXT
folder/3.txt -> folder/3.TXT
folder/4.txt -> folder/4.TXT

Left-side exact match (1)

De map heeft er een bestand bijgekregen: 3.abc.

$ mmv -n -- ";3.*" "#1\3.#2"

folder/3.abc =^ folder/3.abc
folder/3.txt =^ folder/3.txt

Er wordt nu niets veranderd. En dat kun je oa. zien aan de tekens =^ (komt overeen met) in de uitvoer.

Left-side exact match (2)

Het voorbeeld hiervoor is nu aangepast, zodat er wél iets wordt aangepast. Dit is trouwens de originele casus waarom ik dit hoofdstuk begon: Ik wilde namen van grafische bestanden van vlaggen aanpassen. Dit zijn bestandsnamen van maar twee tekens lang (bv. an.png) en daarom vereist het vrij nauw-gedefineerde maskers, om te voorkomen dat andere bestandsnamen onbedoeld ook aangepast worden:

$ mmv -n -- ";3.*" "#1Flag-3.#2"

folder/3.abc -> folder/Flag-3.abc
folder/3.txt -> folder/Flag-3.txt

Left-side exact match (3) - Vlaggen

Het voorbeeld hierboven kan met een nog nauwkeuriger masker gedaan worden (maar dan met de bestaande map & bestanden):

mmv -n -- ";?.txt" "#1Flag-#2.png"
1.txt -> Flag-1.png
2.txt -> Flag-2.png
folder/3.txt -> folder/Flag-3.png
folder/4.txt -> folder/Flag-4.png

Laatste opmerking over de actuele casus: Daar spelen wildcards eigenlijk geen rol! Het gaat steeds om specifieke matches, bv. an.png → Flag-Andorra.png. Wellicht dat mmv niet het passende gereedschap hiervoor is. De reden waarom ik het met mmv het aangepast: Ik gebruik dat al voor casussen die hier sterk op lijken en waar wildcards wel een rol spelen.

Doorgaan bij fouten

De switch -g zorgt ervoor dat mmv doorgaat bij foutmeldingen of vragen. Voorbeeld: Of bestanden wel of niet overschreven mogen worden.

  • Switches moeten voor de argumenten staan
  • Na switches is het keyword -- verplicht.

Voorbeeld:

find . -type f -name '*vana*'
#
mmv -g -- ";*vana*" "#1#2naarb#3"
#
find . -type f -name '*vana*'

Parameter substitution

Parameter substitution werkt. Voorbeeld:

i=1
for ((i; i<=$number_of_rows; i++))
do

   s1=${tr_fn[$i,1]}
   s2=${tr_fn[$i,2]}
   echo "	"$i": "$s1" → "$s2}

   mmv -g -- ";*${s1}*" "#1#2${s2}#3"

done

Dit werkt ook:

i=1
for ((i; i<=$number_of_rows; i++))
do

   mmv -n -- ";${tr_fn_e[$i,1]}.*" "#1${tr_fn_e[$i,2]}.#2"

done

Overwrite

Standaard worden bestaande bestanden niet overschrijven. Maw., als een bestandsnaam wordt aangepast en er bestaat al een bestand met die nieuwe naam, dan gebeurt er niets.

Ik geloof dat overschrijven gaat middels de switch -d. Ik vind de uitleg uit de man-page onduidelijk, maar tot op heden lijkt dit gewoon te werken.

Installatie zonder sudo-rechten?

Is het mogelijk om mmv te gebruiken zonder het met sudo te installeren?

Ja: Gewoon de binary-file overnemen.

Create aliassen

Aliassen kun je aanmaken met switch -l, of door commando mmv aan te roepen met de naam mln - Zie man page. Tot op heden geef ik aan de eerste optie de voorkeur: Vind ik logischer dan onthouden van een aparte commandonaam.

Overwrite vs. protected

  • Met switch -d vinden overschrijfacties plaats zonder dialoog
  • Met switch -p (protected) worden overschrijfacties zonder foutmelding overgeslagen.

Voorbeeld van een actie met switch -p:

5308_logo_01.jpg -> 5308_logo.jpg : old 5308_logo.jpg would have to be deleted.
5334_logo_01.jpg -> 5334_logo.jpg : old 5334_logo.jpg would have to be deleted.
5350_logo_01.jpg -> 5350_logo.jpg : old 5350_logo.jpg would have to be deleted.
5356_logo_01.jpg -> 5356_logo.jpg : old 5356_logo.jpg would have to be deleted.
5390_logo_01.jpg -> 5390_logo.jpg : old 5390_logo.jpg would have to be deleted.
5412_logo_01.jpg -> 5412_logo.jpg : old 5412_logo.jpg would have to be deleted.
5453_logo_01.jpg -> 5453_logo.jpg : old 5453_logo.jpg would have to be deleted.
Nothing done.

Bronnen

Appendix: man mmv

MMV(1)                                        General Commands Manual                                       MMV(1)

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]

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 direc‐
       tory:

          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'

DESCRIPTION
       Mmv moves (or copies, appends, or links, as specified) each source file matching a from pattern to the tar‐
       get name specified by the to pattern.  This multiple action is performed safely,  i.e.  without  any  unex‐
       pected  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 offend‐
       ing parts or aborting.  mmv does support large files (LFS) but it does *NOT* support sparse files (i.e.  it
       explodes them).

                                                    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 (argv[0]):

            command name   default task

            mmv            -x
            mcp            -c
            mad            -a
            mln            -l

       The task option choices are:

       -m :   move source file to target name.  Both must be on the same device.  Will not move  directories.   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.

       -x :   same as -m, 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.

       -r :   rename  source  file or directory to target name.  The target name must not include a path: the file
              remains in the same directory in all cases.  This option is the only  way  of  renaming  directories
              under mmv.

       -c :   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.

       -o :   overwrite target name with source file.  If target file exists, it is overwritten, keeping its orig‐
              inal 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.

       -a :   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 -o.  Unlike
              all other options, -a 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".

       -l :   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.

       -s :   same as -l, 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  cur‐
              rent  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 option may be given, and it applies to all matching files.  Remaining options need not be
       given separately, i.e. "mmv -mk" is allowed.

                                  Multiple Pattern Pairs / Reading Patterns from STDIN

       Multiple  from  --  to pattern pairs may be specified by omitting the pattern pair on the command line, and
       entering them on the standard input, one pair per line.  (If a pattern pair is given on the  command  line,
       the standard input is not read.)  Thus,

          mmv
          a b
          c d

       would  rename  "a"  to "b" and "c" to "d".  If a file can be matched to several of the given from patterns,
       the to pattern of the first matching pair is used.  Thus,

          mmv
          a b
          a c

       would give the error message "a -> c : no match" because file "a" (even if it exists) was  already  matched
       by the first pattern pair.

       WARNING:   This   operation   mode   does   not   work   if   the  patterns  itself  contain  spaces.   See
       http://bugs.debian.org/149873 for details.

                                                    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 charac‐
       ter, 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  pat‐
       tern  or  following  a '/'.  Thus ";*.c" will match all ".c" files in or below the current directory, while
       "/;*.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).

       Since matching a directory under a task option other than -r or -s would result in an  error,  tasks  other
       than  -r and -s match directories only against completely explicit from patterns (i.e. not containing wild‐
       cards).  Under -r and -s, this applies only to "." and "..".

       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 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 man‐
       ner as mmv (or, in the case of sh(1), does not expand it at all).

       For all task options other than -r, 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 replace‐
       ment name must contain the character '#', precede the special character with a ´\' (and enclose  the  argu‐
       ment in quotes because of the shell).  This also works to terminate a wildcard index when it has to be fol‐
       lowed 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

       When any two or more matching files would have to be moved, copied, or linked to the same target  filename,
       mmv detects the condition as an error before performing any actions.  Furthermore, mmv checks if any of its
       actions will result in the destruction of existing files.  If the -d (delete) option is specified, all file
       deletions  or  overwrites are done silently.  Under -p (protect), all deletions or overwrites (except those
       specified with "(*)" on the standard input, see below) are treated as errors.  And  if  neither  option  is
       specified, the user is queried about each deletion or overwrite separately.  (A new stream to "/dev/tty" is
       used for all interactive queries, not the standard input.)

                                                     Error Handling

       Whenever any error in the user's action specifications is detected, an error message is given on the  stan‐
       dard  output,  and  mmv proceeds to check the rest of the specified actions.  Once all errors are detected,
       mmv queries the user whether he wishes to continue by avoiding the erroneous  actions  or  to  abort  alto‐
       gether.   This  and  all  other  queries  may be avoided by specifying either the -g (go) or -t (terminate)
       option.  The former will resolve all difficulties by avoiding the erroneous actions; the latter will  abort
       mmv  if any errors are detected.  Specifying either of them defaults mmv to -p, unless -d is specified (see
       above).  Thus, -g and -t are most useful when running mmv in the background or  in  a  shell  script,  when
       interactive queries are undesirable.

                                                         Reports

       Once the actions to be performed are determined, mmv performs them silently, unless either the -v (verbose)
       or -n (no-execute) option is specified.  The former causes mmv to report each performed action on the stan‐
       dard output as

       a -> b : done.

       Here,  "a"  and  "b" would be replaced by the source and target names, respectively.  If the action deletes
       the old target, a "(*)" is inserted after the the target name.  Also, the "->" symbol is  modified  when  a
       cycle  has to be broken: the '>' is changed to a '^' on the action prior to which the old target is renamed
       to a temporary, and the '-' is changed to a '=' on the action where the temporary is used.

       Under -n, none of the actions are performed, but messages like the above are printed on the standard output
       with the ": done." omitted.

       The  output  generated  by  -n can (after editing, if desired) be fed back to mmv on the standard input (by
       omitting the from -- to pair on the mmv command line).  To facilitate this, mmv ignores lines on the  stan‐
       dard  input  that  look  like  its own error and "done" messages, as well as all lines beginning with white
       space, and will accept pattern pairs with or without the intervening "->" (or "-^", "=>", or "=^").   Lines
       with  "(*)"  after  the  target  pattern have the effect of enabling -d for the files matching this pattern
       only, so that such deletions are done silently.

       WARNING: This means that unexpected things may happen if files matched by the patterns contain spaces.  See
       http://bugs.debian.org/149873 for details.

       When  feeding  mmv  its  own output, one must remember to specify again the task option (if any) originally
       used to generate it.

       Although mmv attempts to predict all mishaps prior to performing any specified actions, accidents may  hap‐
       pen.   For example, mmv does not check for adequate free space when copying.  Thus, despite all efforts, it
       is 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.  It then aborts, not attempting to do anything else.  Once the user has  cleared  up
       the  problem, he can feed this report back to mmv on the standard input to have it complete the task.  (The
       user is queried for a file name to dump this report if the standard output has not been redirected.)

EXIT STATUS
       Mmv exits with status 1 if it aborts before doing anything, with status 2 if it aborts due to failure after
       completing some of the actions, and with status 0 otherwise.

SEE ALSO
       mv(1), cp(1), ln(1), umask(1)

AUTHOR
       Vladimir Lanin
       lanin@csd2.nyu.edu

BUGS
       If  the search pattern is not quoted, the shell expands the wildcards.  Mmv then (usually) gives some error
       message, but can not determine that the lack of quotes is the cause.

       To avoid difficulties in semantics and error checking, mmv refuses to move or create directories.

                                            November 20, 2001 (v1.0lfs)                                     MMV(1)