`multi' is also of releasable quality, given a man page. Move it
[sgt/utils] / multi / multi
diff --git a/multi/multi b/multi/multi
new file mode 100755 (executable)
index 0000000..7cdbe6c
--- /dev/null
@@ -0,0 +1,71 @@
+#!/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 {
+    $_;
+  }
+}