]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
rename: add --all and --last parameters
authorTodd Lewis <todd_lewis@unc.edu>
Thu, 15 Jul 2021 04:15:55 +0000 (00:15 -0400)
committerTodd Lewis <todd_lewis@unc.edu>
Thu, 15 Jul 2021 04:15:55 +0000 (00:15 -0400)
Renaming files with rename often involves multiple passes in order
to, say, replace all spaces with underscores because traditionally
rename only replaces the first occurrence of the expression. The
--all parameter makes this task simple.

With the addition of --last, rename becomes much safer to use when
replacing file extensions, whereas before it would mangle a file
which had its extension also embedded elsewhere in its name.

The implied --first, together with --all and --last, round out the
common cases for renaming files.

bash-completion/rename
misc-utils/rename.1.adoc
misc-utils/rename.c
tests/expected/rename/basic
tests/ts/rename/basic

index 9fc1ceca38a0dd725940525b3985f46738bf9538..7edaa92c50e8c03dbb4e7685c57e92275bd825d0 100644 (file)
@@ -11,7 +11,7 @@ _rename_module()
        esac
        case $cur in
                -*)
-                       OPTS="--verbose --symlink --help --version --no-act --no-override --interactive"
+                       OPTS="--verbose --symlink --help --version --no-act --all --last --no-override --interactive"
                        COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
                        return 0
                        ;;
index 0f5203423d8d124f9d4448bcebf8c92e78d93810..6cc881e344857b392873e5519531a9a13d487501 100644 (file)
@@ -31,6 +31,12 @@ Show which files were renamed, if any.
 *-n*, *--no-act*::
 Do not make any changes; add *--verbose* to see what would be made.
 
+*-a*, *--all*::
+Replace all occurrences of _expression_ rather than only the first one.
+
+*-l*, *--last*::
+Replace the last occurrence of _expression_ rather than the first one.
+
 *-o*, *--no-overwrite*::
 Do not overwrite existing files. When *--symlink* is active, do not overwrite symlinks pointing to existing targets.
 
index fbabfe2841b66565e9c530318fd6804cd5f20465..3b0aed02279428815451b2a2f398e12363133d8b 100644 (file)
@@ -44,23 +44,39 @@ for i in $@; do N=`echo "$i" | sed "s/$FROM/$TO/g"`; mv "$i" "$N"; done
 #define RENAME_EXIT_UNEXPLAINED        64
 
 static int tty_cbreak = 0;
+static int all = 0;
+static int last = 0;
 
 static int string_replace(char *from, char *to, char *s, char *orig, char **newname)
 {
        char *p, *q, *where;
+       int count = 0, fromlen = strlen(from);
 
-       where = strstr(s, from);
+       p = where = strstr(s, from);
        if (where == NULL)
                return 1;
+       count++;
+       while ((all || last) && p) {
+               p = strstr(p + (last ? 1 : fromlen), from);
+               if (p) {
+                       if (all)
+                               count++;
+                       if (last)
+                               where = p;
+               }
+       }
        p = orig;
-       *newname = xmalloc(strlen(orig) + strlen(to) + 1);
+       *newname = xmalloc(strlen(orig) - count * fromlen + count * strlen(to) + 1);
        q = *newname;
-       while (p < where)
-               *q++ = *p++;
-       p = to;
-       while (*p)
-               *q++ = *p++;
-       p = where + strlen(from);
+       while (where) {
+               while (p < where)
+                       *q++ = *p++;
+               p = to;
+               while (*p)
+                       *q++ = *p++;
+               p = where + fromlen;
+               where = strstr(p, from);
+       }
        while (*p)
                *q++ = *p++;
        *q = 0;
@@ -226,6 +242,8 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -v, --verbose       explain what is being done\n"), out);
        fputs(_(" -s, --symlink       act on the target of symlinks\n"), out);
        fputs(_(" -n, --no-act        do not make any changes\n"), out);
+       fputs(_(" -a, --all           replace all occurrences\n"), out);
+       fputs(_(" -l, --last          replace only the last occurrence\n"), out);
        fputs(_(" -o, --no-overwrite  don't overwrite existing files\n"), out);
        fputs(_(" -i, --interactive   prompt before overwrite\n"), out);
        fputs(USAGE_SEPARATOR, out);
@@ -246,6 +264,8 @@ int main(int argc, char **argv)
                {"verbose", no_argument, NULL, 'v'},
                {"version", no_argument, NULL, 'V'},
                {"help", no_argument, NULL, 'h'},
+               {"all", no_argument, NULL, 'a'},
+               {"last", no_argument, NULL, 'l'},
                {"no-act", no_argument, NULL, 'n'},
                {"no-overwrite", no_argument, NULL, 'o'},
                {"interactive", no_argument, NULL, 'i'},
@@ -258,11 +278,19 @@ int main(int argc, char **argv)
        textdomain(PACKAGE);
        close_stdout_atexit();
 
-       while ((c = getopt_long(argc, argv, "vsVhnoi", longopts, NULL)) != -1)
+       while ((c = getopt_long(argc, argv, "vsVhnaloi", longopts, NULL)) != -1)
                switch (c) {
                case 'n':
                        noact = 1;
                        break;
+               case 'a':
+                       all = 1;
+                       last = 0;
+                       break;
+               case 'l':
+                       last = 1;
+                       all = 0;
+                       break;
                case 'v':
                        verbose = 1;
                        break;
index 8a7a1d8042ef418ef74d66d0951aff7f9813348a..4a1809828d67ada64a31c9f460c035ac8020ddcc 100644 (file)
@@ -2,3 +2,17 @@
 `rename_basic.2' -> `rename_test.2'
 `rename_basic.3' -> `rename_test.3'
 what is rename_basic.? doing here?
+`rename_all file with spaces.1' -> `rename_all_file_with_spaces.1'
+`rename_all file with spaces.2' -> `rename_all_file_with_spaces.2'
+`rename_all file with spaces.3' -> `rename_all_file_with_spaces.3'
+what is rename_all* *.? doing here?
+`rename_zz_last_z.x' -> `rename_AAzzBB_last_z.x'
+`rename_zz_last_z.y' -> `rename_AAzzBB_last_z.y'
+`rename_zz_last_z.z' -> `rename_AAzzBB_last_z.z'
+`rename_zz_last_zz.x' -> `rename_zz_last_AAzzBB.x'
+`rename_zz_last_zz.y' -> `rename_zz_last_AAzzBB.y'
+`rename_zz_last_zz.z' -> `rename_zz_last_AAzzBB.z'
+`rename_zz_last_zzz.x' -> `rename_zz_last_zAAzzBB.x'
+`rename_zz_last_zzz.y' -> `rename_zz_last_zAAzzBB.y'
+`rename_zz_last_zzz.z' -> `rename_zz_last_zAAzzBB.z'
+what is rename*last* doing here?
index 60e171709102560ad2f5f042a6a6bb4b3b152f2c..fb20b49dd3090ff1b1bd1bdcaec3eb2441104c9a 100755 (executable)
@@ -38,4 +38,34 @@ for i in rename_test.{1..3}; do
        fi
 done
 
+
+touch rename_all\ file\ with\ spaces.{1..3}
+$TS_CMD_RENAME -v -a ' ' '_' rename_all*.? >> $TS_OUTPUT 2> $TS_ERRLOG
+
+for i in rename_all*\ *.?; do
+       echo "what is $i doing here?" >> $TS_OUTPUT
+done
+for i in rename_all_file_with_spaces.{1..3}; do
+       if [ ! -f $i ]; then
+               echo "file $i is missing" >> $TS_OUTPUT
+       else
+               rm -f $i
+       fi
+done
+
+touch rename_zz_last_{z,z{,z{,z}}}.{x..z}
+$TS_CMD_RENAME -v -l zz AAzzBB rename_zz_last_* >> $TS_OUTPUT 2> $TS_ERRLOG
+for i in rename_AAzzBB_last_z.x rename_AAzzBB_last_z.y rename_AAzzBB_last_z.z \
+       rename_zz_last_AAzzBB.x rename_zz_last_AAzzBB.y rename_zz_last_AAzzBB.z \
+       rename_zz_last_zAAzzBB.x rename_zz_last_zAAzzBB.y rename_zz_last_zAAzzBB.z ; do
+       if [ ! -f $i ]; then
+               echo "file $i is missing" >> $TS_OUTPUT
+       else
+               rm -f $i
+       fi
+done
+for i in rename*last* ; do
+       echo "what is $i doing here?" >> $TS_OUTPUT
+done
+
 ts_finalize