Batch Rename Directories In Reverse Order
Solution 1:
One way using perl
. Code is commented.
Content of script.pl
:
#!/usr/bin/env perl
use warnings;
use strict;
use File::Basename qw|basename|;
die qq{Usage: perl $0 <real|trial>\n} unless @ARGV == 1;
my $real = $ARGV[0] eq q|real| ? 1 : 0;
## Filter filenames from the directory that match criteria of OP.
my @files = grep { -d $_ && m/\A(\d+)\s-\s.*\1/ } map { basename $_ } <./*>;
## Get numbers in reverse mode to substitute them later in filenames .
my @nums = map { m/\A(\d+)/; $1 || 0 } reverse @files;
## Process all files, change the number of the file and rename it.
for my $file ( @files ) {
(my $new_file = $file) =~ s/\A(\d+)/shift @nums/e;
if ( $real ) {
rename $file, $new_file or warn qq|WARN: Couldn't rename "$file"\n|;
}
else {
printf qq|%s\n|, qq|rename "$file" "$new_file"|;
}
}
List directory:
ls -1
With following output:
1 - file1file
2 - fi2le
3 - fileeee3
4 - fou4r
5 - donttouch
6 - donttouch5either
script.pl
Run the script:
perl script.pl
list the directoy again:
ls -1
With following output:
1 - fou4r
2 - fileeee3
3 - fi2le
4 - file1file
5 - donttouch
6 - donttouch5either
script.pl
EDIT: There was a suggestion in comments to let a trial run before the real one, so I added an argument real
or trial
. With first option it will do real changes, while with second one it will print
to output the real command but without running them. Now run the script like:
perl script.pl real
or
perl script.pl trial
Solution 2:
Let's first simulate some filenames:
$ printf 'kjdfh8\nskdjfh9\nckjhv10\naskjdh11\n'
kjdfh8
skdjfh9
ckjhv10
askjdh11
Then run 'em through a pipe:
$ printf 'kjdfh8\nskdjfh9\nckjhv10\naskjdh11\n' | sed 's/[^0-9]*\(.*\)/\1 &/' | sort -nr | cut -d\ -f2-
askjdh11
ckjhv10
skdjfh9
kjdfh8
How does this work? It's actually really simple. First, we use sed
to put a copy of any numeric content at the beginning of the line. Next, we sort. Finally, we use cut
to strip off the number at the beginning of the line. And voilĂ !
Now... That just reverses the order. That's one of the questions in your question, but your end goal is to rename things. We can extend the list above with another pipe that is also a while loop. This time I'll space things out for easier reading.
#!/bin/bash
n=0
printf 'kjdfh8\nskdjfh9\nckjhv10\naskjdh11\n' \
| sed 's/[^0-9]*\(.*\)/\1 &/' | sort -nr | cut -d\ -f2- \
| while read file; do
base="${file/[0-9]*/}"
((n++))
printf 'mv "%s" "%s%s"\n' "$file" "$base" "$n"
done
This while loop reads in the list of files we created above. It gets the "base" by removing everything in the filename starting with the first digit. It increments a counter (used for the new name), then prints a mv
command with the old filename, base and counter.
Again, the output of this script is a set of mv
commands which you can pipe through a shell.
Obviously, to use this on your files, you will need to replace the initial printf
command with something that generates a list of your files.
NOTE: It is usually inadvisable to process filenames in this way, due to the risk of unexpected or special characters (even spaces) turning up in filenames and throwing off your plans. Only do this if you're confident that you understand it.
Solution 3:
if this awk solution ok for you?
cmd:
ls -F|grep "/$"|sort|awk 'BEGIN{FS=" - ";l=5;}{o=$0;gsub("/$","",o);gsub($1"/$",l--);if(o!=$0)printf "mv \"%s\" \"%s\"\n",o,$0 }' |sh
here the 5 is hard coded, in your case would be 725. :) of course it could be set by wc -l, but I am abit lazy.
test:
kent$ tree -d
.
|-- 1 - foo1
|-- 2 - foo2
|-- 3 - foo3
|-- 4 - foo4
`-- 5 - foo5
5 directories
#this will only print the generated mv command:
kent$ ls -F|grep "/$"|sort|awk 'BEGIN{FS=" - ";l=5;}{o=$0;gsub("/$","",o);gsub($1"/$",l--);if(o!=$0)printf "mv \"%s\" \"%s\"\n",o,$0 }'
mv "1 - foo1" "1 - foo5"
mv "2 - foo2" "2 - foo4"
mv "4 - foo4" "4 - foo2"
mv "5 - foo5" "5 - foo1"
# if you pipe the generated command to "sh", they will be executed:
kent$ ls -F|grep "/$"|sort|awk 'BEGIN{FS=" - ";l=5;}{o=$0;gsub("/$","",o);gsub($1"/$",l--);if(o!=$0)printf "mv \"%s\" \"%s\"\n",o,$0 }' |sh
#result:
kent$ tree -d
.
|-- 1 - foo5
|-- 2 - foo4
|-- 3 - foo3
|-- 4 - foo2
`-- 5 - foo1
5 directories
Solution 4:
This:
find /path/to/the/directories/location/ -depth -exec mv '{}' $(basename '{}')$(echo $(date "+%H%M%S%N")) \;
Post a Comment for "Batch Rename Directories In Reverse Order"