--- /dev/null
+#!/usr/bin/env perl
+
+# Perform multiple moves/copies.
+
+# Usage: multi mv 's/$/.old/' *.c
+# multi cp 's:/tmp/(.*):$1:' *
+# multi - svn mv - 's/foo/bar/' *foo*
+#
+# Options: -n don't actually do anything, just print what it would do
+# -r reverse the arguments to each command
+# -q don't print the commands as they are executed
+
+# Note that all variables in this script begin with a double
+# underscore. This is because the <action> parameter is a piece of
+# user-supplied Perl, which might perfectly legitimately want to
+# define and use its own variables in the course of doing a complex
+# transformation on file names. Thus, I arrange that all _my_
+# variables stay as far out of its likely namespace as they can.
+
+$__quiet = $__donothing = $__reverse = 0;
+
+while ($ARGV[0] =~ /^-(.+)$/) {
+ shift @ARGV;
+ $__quiet = 1, next if $1 eq "q";
+ $__quiet = 0, $__donothing = 1, next if $1 eq "n";
+ $__reverse = 1, next if $1 eq "r";
+}
+
+die "usage: multi <cmd> <action> <files>\n" .
+ " e.g. multi mv 'tr/A-Z/a-z/' *\n" if $#ARGV < 2;
+ " also: multi - <multiple-word-cmd> - <action> <files>\n" .
+ " or multi - svn mv - 'tr/A-Z/a-z/' *\n" if $#ARGV < 2;
+
+@__cmd = ();
+if ($ARGV[0] eq "-") {
+ shift @ARGV;
+ while (defined $ARGV[0] and $ARGV[0] ne "-") {
+ push @__cmd, shift @ARGV;
+ }
+ if (!defined $ARGV[0]) {
+ die "multi: no terminating - in multiple-word command mode\n";
+ }
+ shift @ARGV; # eat trailing -
+} else {
+ $__cmd[0] = shift @ARGV;
+}
+$__action = shift @ARGV;
+
+while (@ARGV) {
+ $_ = $__origfile = shift @ARGV;
+ eval $__action;
+ $__newfile = $_;
+ ($__origfile, $__newfile) = ($__newfile, $__origfile) if $__reverse;
+ &pcmd(@__cmd, $__origfile, $__newfile) if !$__quiet;
+ system @__cmd, $__origfile, $__newfile if !$__donothing;
+}
+
+sub pcmd {
+ my (@words) = @_;
+ printf "%s\n", join " ", map { &fmt($_) } @words;
+}
+
+sub fmt {
+ local ($_) = @_;
+ if (/[ !"#$&'()*;<>?\\`|~]/) {
+ s/'/'\\''/g;
+ "'$_'";
+ } else {
+ $_;
+ }
+}
--- /dev/null
+\cfg{man-identity}{multi}{1}{2004-11-20}{Simon Tatham}{Simon Tatham}
+\cfg{man-mindepth}{1}
+
+\C{multi-manpage} Man page for \cw{multi}
+
+\H{multi-manpage-name} NAME
+
+\cw{multi} - bulk file rename/copy utility using Perl regexps
+
+\H{multi-manpage-synopsis} SYNOPSIS
+
+\c multi [ -n | -q ] [ -r ] cmd perlfragment file [file...]
+\e bbbbb bb bb bb iii iiiiiiiiiiii iiii iiii
+\c multi [ -n | -q ] [ -r ] - cmd cmd - perlfragment file [file...]
+\e bbbbb bb bb bb iii iii iiiiiiiiiiii iiii iiii
+
+\H{multi-manpage-description} DESCRIPTION
+
+\cw{multi} is a utility which allows you to invoke a command
+(typically, but not always, \cw{mv} or \cw{cp}) on a lot of files in
+a complicated way.
+
+The command-line arguments to \cw{multi} include a command, a set of
+filenames, and a fragment of Perl. for each of the filenames,
+\cw{multi} will use the fragment of Perl to transform the filename
+into a new filename, and will then invoke the given command, passing
+the old and new filenames as arguments.
+
+\cw{multi} is most often useful as a bulk rename or copy utility, by
+passing \cw{mv} or \cw{cp} as the command. However, it can have more
+complex uses as well; see the examples below.
+
+\H{multi-manpage-args} ARGUMENTS
+
+\dt \e{cmd}
+
+\dd Provides the command to which pairs of filenames will eventually
+be passed. If this is just one word (typically \c{cp} or \c{mv}),
+you can simply supply that word on the command line.
+
+\lcont{
+
+A multiple-word command (such as \c{ln -s} or \c{svn mv}) can be
+used if you place it between two arguments containing only minus
+signs.
+
+}
+
+\dt \e{perlfragment}
+
+\dd This fragment of Perl will be run for each file name you supply.
+The file name will be passed in in the special Perl variable
+\cw{$_}, and the altered file name should be passed out in \cw{$_}
+as well. (Therefore, the simplest kind of Perl fragment you could
+use is a single \cw{s///} substitution command.)
+
+\lcont{
+
+All the Perl variable names used internally by \cw{multi} itself
+begin with two underscore characters. Therefore, your Perl fragment
+can safely define its own variables (provided they do not begin with
+two underscores), without worrying about upsetting the functioning
+of \cw{multi}.
+
+}
+
+\dt \e{files}
+
+\dd After the Perl fragment, \cw{multi} expects a list of file names
+to be transformed. Typically these will be generated by typing one
+or more wildcard expressions on the shell command line.
+
+\H{multi-manpage-options} OPTIONS
+
+By default, \cw{multi} will print every command it executes on
+standard output, so that you can see what it has just done (in case
+it turns out to be wrong!).
+
+Bourne-shell-style quoting is provided, so that copying the output
+of \cw{multi} and pasting it into a shell script or on to a shell
+command line should work correctly.
+
+\dt \cw{-n}
+
+\dd Do not actually execute the commands. Instead, \e{only} print
+them on standard output as they would be executed. (Useful for a dry
+run to make sure your Perl does what you meant it to do. When you've
+got it right, take off the \cw{-n} option and let it run for real.)
+
+\dt \cw{-q}
+
+\dd \e{Only} execute the commands, without printing them. (Useful
+for running within a larger script.)
+
+By default, the two arguments passed to each invocation of the
+subcommand are the original filename and the transformed filename,
+in that order.
+
+\dt \cw{-r}
+
+\dd Reverse the order of arguments to the subcommand, so that it
+receives the transformed file name \e{before} the original one.
+
+\H{multi-manpage-examples} EXAMPLES
+
+The simplest use of \cw{multi} is to rename a large number of files.
+Suppose, for example, you have a lot of text files with \cw{.txt}
+extensions, and you prefer to use \cw{.text} extensions:
+
+\c $ multi mv 's/.txt$/.text/' *.txt
+\e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+\c mv bar.txt bar.text
+\c mv baz.txt baz.text
+\c mv foo.txt foo.text
+
+If you wanted to copy the files rather than moving them, the command
+becomes
+
+\c $ multi cp 's/.txt$/.text/' *.txt
+\e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+\c cp bar.txt bar.text
+\c cp baz.txt baz.text
+\c cp foo.txt foo.text
+
+If you wanted to create symbolic links, you now need the command
+\cw{ln -s}, which is composed of two words. So you need to tell
+\cw{multi} where the command words stop and the Perl begins, using
+two single-dash arguments:
+
+\c $ multi - ln -s - 's/.txt$/.text/' *.txt
+\e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+\c ln -s bar.txt bar.text
+\c ln -s baz.txt baz.text
+\c ln -s foo.txt foo.text
+
+Note that simply quoting the two-word command would not have worked,
+because \cw{multi} would have assumed you genuinely meant a one-word
+command which had a space in the middle...
+
+\c $ multi "ln -s" 's/.txt$/.text/' *.txt
+\e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+\c 'ln -s' bar.txt bar.text
+\c 'ln -s' baz.txt baz.text
+\c 'ln -s' foo.txt foo.text
+
+... which was almost certainly not what you wanted!
+
+The version control utility \e{Subversion} has a subcommand for
+moving files around within your working directory. However, it does
+not support wildcards, because \cw{svn mv} expects to see
+\e{precisely} two arguments. So if you want to move a whole load of
+files into a subdirectory, a command such as \cw{svn mv win*.c
+windows} will not work. \cw{multi} comes to the rescue:
+
+\c $ multi - svn mv - 's:^:windows/:' win*.c
+\e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+\c svn mv winmain.c windows/winmain.c
+\c svn mv winprint.c windows/winprint.c
+\c svn mv winutils.c windows/winutils.c
+
+Of course, your Perl fragment can be more complex than just a
+\cw{s///} command. Here's a means of tidying up after extracting a
+MS-DOS zip file containing all filenames in upper case:
+
+\c $ multi mv 'y/A-Z/a-z/' *[A-Z]*
+\e bbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+\c mv HEADER.H header.h
+\c mv MAIN.C main.c
+\c mv STUFF.C stuff.c
+
+Here's an example using \cw{-r}. Suppose you have lots of small C
+programs and you want to quickly compile them all into binaries:
+
+\c $ multi -r - cc -o - 's/.c$//' *.c
+\c cc -o bar bar.c
+\c cc -o baz baz.c
+\c cc -o foo foo.c
+
+Finally, here's a general technique for going beyond the limits of
+\cw{multi}, in the case where you need to do something more
+ambitious with your two file names. Suppose you want to use one file
+name as the target of a shell redirection operator, for example.
+
+\c $ multi - sh -c 'grep foo $0 > $1' - 's/.txt$/.grepped/' *.txt
+\e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+\c sh -c 'grep foo $0 > $1' bar.txt bar.grepped
+\c sh -c 'grep foo $0 > $1' baz.txt baz.grepped
+\c sh -c 'grep foo $0 > $1' foo.txt foo.grepped
+
+As each of these commands will be executed, the (explicitly invoked)
+shell will substitute the two filename arguments in place of \cw{$0}
+and \cw{$1}, so that the \e{effect} will be that of running a set of
+commands like
+
+\c grep foo bar.txt > bar.grepped
+\c grep foo baz.txt > baz.grepped
+\c grep foo foo.txt > foo.grepped
+
+\H{multi-manpage-ack} ACKNOWLEDGEMENTS
+
+The O'Reilly book \q{Programming Perl} includes a simple example
+script which contains the core idea of this program. It takes a
+single Perl argument followed by filenames, and invokes Perl's
+internal \cw{rename} function. \cw{multi} is a complete rewrite of
+this basic idea, supplying more options and configurability.