Perl script to make first 8 characters of all headers, but not all filename

Which Perl script should I use to change only the first 8 characters in the filename for all caps, instead of a script to change the entire filename for all caps?

This is how I set it up:

#!/usr/bin/perl

chdir "directory path";
#@files = `ls *mw`;
@files = `ls | grep mw`;
chomp @files;

foreach $oldname (@files) {
  $newname = $oldname;
  $newname =~ s/mw//;
  print "$oldname -> $newname\n";
  rename("$oldname","$newname");
}

      

+3


source to share


6 answers


You can use this regex:



my $str = 'Hello World!';
$str =~ s/^(.{8})/uc($1)/se; # $str now contains 'HELLO WOrld!'

      

+8


source


Substitution

s/^(.{1,8})/\U$1/

      



will set the first eight characters of the string to uppercase. The complete program looks like this:

use strict;
use warnings;

chdir "directory path" or die "Unable to change current directory: $!";

opendir my $dh, '.' or die $!;
my @files = grep -f && /mw/, readdir $dh;

foreach my $file (@files) {
  (my $new = $file) =~ s/mw//;
  $new =~ s/^(.{1,8})/\U$1/s;
  print "$file -> $new\n";
  rename $file, $new;
}

      

+4


source


What about:

#!/usr/bin/perl
use strict;
use warnings;
use File::Copy;

chdir'/path/to/directory';
# Find all files that contain 'mw'
my @files = glob("*mw*");

foreach my $file(@files) {
    # skip directories
    next if -d $file;
    # remve 'mw' from the filename
    (my $FILE = $file) =~ s/mw//;
    # Change filename to uppercase even if the length is <= 8 char
    $FILE =~ s/^(.{1,8})/uc $1/se;
    move($file, $FILE);
}

      

As the doc says for rename , you are better off using File :: Copy to be platform independent.

+3


source


Always check the return values ​​of system calls!

When accessing OS services, you should always check the return value. For example, the Perl documentation for chdir

(with emphasis)

chdir EXPR


  chdir FILEHANDLE


  chdir DIRHANDLE


  chdir

Change the working directory to EXPR if necessary. If EXPR is omitted, changes to the directory specified $ENV{HOME}

, if installed; if not, changes to the directory specified $ENV{LOGDIR}

. (In VMS, the variable is $ENV{SYS$LOGIN}

also checked and used if set.) If none of these are set, chdir

does nothing. It returns true on success, false otherwise. See example under die.

On systems that support fchdir (2), you can pass a file descriptor or directory descriptor as an argument. On systems that do not support fchdir (2), passing descriptors throw an exception.

As stated in your question, your code is throwing away important information: whether the system calls were chdir

both rename

successful or unsuccessful.

Providing helpful error messages

An example of a common idiom for checking return values ​​in Perl is

chdir $path or die "$0: chdir $path: $!";

      

The error message contains three important bits of information:

Also note that die

also the filename and line number where programmatic control was set, unless your error message ends with a newline character. When chdir

not working, the standard error will be like

./myprogram: chdir: No such file or directory at ./myprogram line 3.

Logically or true when at least one of its arguments is true. The do-or-die idiom works because if the chdir

above fails, it returns false and requires or

the right-hand side to evaluate and exits with die

. In the lucky case where chdir

succeeds and returns true, there is no need to evaluate the right-hand side because we already have one true argument for the boolean or.

Recommended code improvements

For what you are doing, I recommend using readdir

to avoid problems if one of the filenames contains spaces. Note defined

in the code below to stop the file named 0

(i.e. One null character) ending your loop.

#! /usr/bin/env perl

chdir "directory path" or die "$0: chdir: $!";

opendir $dh, "." or die "$0: opendir: $!";

while (defined($oldname = readdir $dh)) {
  next unless ($newname = $oldname) =~ s/mw//;
  $newname =~ s/^(.{1,8})/\U$1/;
  rename $oldname, $newname or die "$0: rename $oldname, $newname: $!";
}

      

In rename

order to have any hope, you must store the value $oldname

, so immediately the code above copies it to $newname

and starts replacing the copy, not the original. You will see

($new = $old) =~ s/.../.../;  # or /.../

      

in Perl code, so this is also an important idiom to understand.

The perlop documentation defines convenient escape sequences for use in strings and regular expression substitutions:

\l

lowercase next character
\u

titlecase only (not uppercase!) next character
  \l

lowercase only all characters before \ E seen
  \u

uppercase all characters before \ E seen
  \Q

quote non-word characters before \ E
  \E

complete either case modification or quote (in depending on what happened last)

The above code grabs the first eight characters (or less if $newname

shorter in length) and replaces them with their upcased counterparts.

Output example

See the code in action:

$ ls directory \ path /
defmwghijk mwabc nochange qrstuvwxyzmw

$ ./prog

$ ls directory \ path /
ABC DEFGHIJK QRSTUVWXyz nochange
+2


source


I rely more on your requirements than you tell us, for example not on the upper end of the parts of the file extension. Instead of matching the first eight characters, I'll match the first eight letters:

use v5.14;
use utf8;

chdir "/Users/brian/test/";
my @files = glob( 'mw*' );

foreach my $old (@files) {
    my $new = $old =~ s/\Amw(\pL{1,8})/\U$1/ir;
    print "$old → $new\n";
    }

      

Some other notes:

  • You can glob directly in Perl. You don't need to ls

    .
  • It looks like you were disconnecting mv

    , which is why I did it. If that's not what you want, it's easy to change.
+1


source


Instead of a regular expression to repeat the first eight characters, you can use the 4-argument form substr . This offers an on-site replacement.

my $old = q(abcdefghij);
my $new = $old;
substr( $new, 0, 8, substr( uc($old), 0, 8 ) );
print "$old\n$new\n";

abcdefghij
ABCDEFGHij

      

Use rename

or File::Copy::move

(as shown in M42) to perform the actual rename.

0


source







All Articles