Skip to main content
6 of 6
added 1 character in body
Stéphane Chazelas
  • 565.2k
  • 96
  • 1.1k
  • 1.6k

You aren't executing anything. When you put something in single quotes (''), that's just a string, so 'mv "$file" "$var1"' is not a command, it's a string and doesn't do anything. A string by itself is just a true statement. In order to execute the command, you need to use:

system "mv", "--", $file, $var1;

(Not system("mv '$file' '$var1'") which would introduce a command injection vulnerability).

But there's no need for that, perl has a rename() function, so you can do:

rename($file, $var1)

Next, you really shouldn't parse the output of ls, that is a very bad idea and fragile. It is also completely unnecessary in Perl (or any other programming language). If you want to match all files in the current directory containing the string pre_ (which is what your grep does—you don't need the grep, by the way, you could have just used ls -d -- *pre_*), you can use glob() like this:

my @files = glob("*pre_*");

So, putting all that together, this is what you were trying to do:

#!/usr/bin/perl -w
use File::Glob qw(:globally :nocase);
my @files = glob("*pre_*");
foreach $file ( @files )
{
  my @names = split(/pre_/i, $file);
  rename($file, $names[1]);
}

But this isn't really a good idea. If you have two files with the same prefix (e.g. pre_file1 and apre_file1), then the first file will be overwritten by the second file since, in this example, both files would be renamed to file1 because your command would remove pre_ and everything before it. So you can add a warning and skip those files:

#!/usr/bin/perl -w
use File::Glob qw(:globally :nocase);

my @files = glob("*pre_*");
foreach $file ( @files )
{
  my @names = split(/pre_/i, $file);
  if (-e $names[1]) {
    warn "Warning: Not renaming '$file' to '$names[1]' " .
        "because '$names[1]' exists.\n";
    next;
  }
  rename($file, $names[1]) or
    warn "rename '$file': $!\n";
}

Of course, all of this is reinventing the wheel. You can just install perl-rename (on Debian, Ubuntu, etc. run apt install rename, on other systems it might be called prename or perl-rename), and then you can do (although this is not case insensitive, it only finds files containing pre_):

rename -n 's/.*?pre_//si' ./*

The -n causes rename to just print what it would do, without actually renaming. Once you are sure the renaming works as expected, run the command again without the -n to actually rename the files.

Here the shell passes all (non-hidden) files to rename and rename will ignore the files whose name the perl code doesn't change. You could also tell the shell to only pass the file names that contain pre_ case-insensitively with ./~(i)*pre_* in ksh93, ./(#i)*pre_* in zsh -o extendedglob, setting the nocaseglob option globally in bash, zsh or yash or use ./*[pP][rR][eE]_*.

terdon
  • 247.8k
  • 68
  • 471
  • 704