Using the perl rename
utility.
Note: perl rename is also known as file-rename
, perl-rename
, or prename
. It is not to be confused with the rename
utility from util-linux
which has completely different and incompatible capabilities and command-line options.
$ rename -n ' BEGIN {
# This block runs only once when the script starts, there's no need
# to redefine these vars on every pass through the loop. File::Rename
# scripts run with `use strict vars`, so we need to be careful about
# variable scope. See `perldoc -f our`
our %patterns=(1234 => "Pattern1.txt", 5678 => "Pattern2.txt");
our $re = "^HDR#+(" . join("|",keys %patterns) . ")#+$";
};
# The remainder of the script runs once for every filename
our (%patterns, $re); # these vars are in File::Rename lexical scope
open(my $fh,"<",$_); my $line=<$fh>; close($fh);
if ($line =~ /$re/) { $_ = $patterns{$1} }' File*
rename(File1.txt, Pattern1.txt)
rename(File2.txt, Pattern2.txt)
(this was run on two text files, copies of your sample data. File1.txt contain 1234 exactly the same as your File.txt example, while File2.txt was edited to contain 5678 instead).
The -n
option makes it a dry run, so it will only show what it would do without actually renaming any files. Remove the -n
, or replace it with -v
for verbose output, when you've confirmed that it does what you want.
This rename script uses a hash %patterns
to hold the patterns to search for AND the filenames to rename that files containing that pattern should be renamed to. It constructs a regular expression from the keys of the hash in variable $re
.
Then it opens the current filename, and reads in the first line. If that first line matches one of the patterns, it is renamed to the corresponding filename.
Using a hash like this allows the script to be easily extended with more or different patterns. Apart from the %patterns
hash (and the ^HDR#+(
and )#+$
in $re
), nothing is hard-coded.
NOTE: rename will not overwrite an existing file unless you force it to with the -f
or --force
option. This applies to existing files and to just-renamed files - if more than one file contains the same pattern, only the first will be renamed. Using -f
will, of course, overwrite any existing/previously-renamed files - a better alternative would be to use a counter variable (e.g. a hash with the filenames or patterns as keys) to renamed the files to, e.g., Pattern1.txt.001
, Pattern1.txt.002
, etc.
Or with the help of the fileparse()
function from the File::Basename module to Pattern1-001.txt
, Pattern1-002.txt
, etc.
e.g.
$ rename -n 'BEGIN {
use File::Basename;
our %seen=();
our %patterns=(1234 => "Pattern1.txt", 5678 => "Pattern2.txt");
our $re = "^HDR#*(" . join("|",keys %patterns) . ")"
};
our(%seen, %patterns, $re);
open(my $fh,"<",$_); my $line=<$fh>; close($fh);
if ($line =~ /$re/) {
my($name,$path,$suffix) = fileparse($patterns{$1}, qr/\.[^.]*$/);
$_ = sprintf "%s-%03i%s", $name, ++$seen{$1}, $suffix
}' File*
rename(File1.txt, Pattern1-001.txt)
rename(File2.txt, Pattern2-001.txt)
rename(File3.txt, Pattern1-002.txt)
HDR
and the number, or do you want to test the first line that starts withHDR
for the number?