Perl脚本使前8个字符全部大写,但不是整个文件名



我应该使用哪个Perl脚本只将文件名中的前8个字符更改为全大写,而不是将整个文件名更改为全小写?

以下是我如何设置它:

#!/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 -> $newnamen";
  rename("$oldname","$newname");
}

您可以使用以下正则表达式:

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

替代

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

将字符串的前八个字符设置为大写。完整的程序看起来像这个

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 -> $newn";
  rename $file, $new;
}

怎么样:

#!/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);
}

正如重命名文档中所说,最好使用File::Copy来实现平台独立。

始终检查系统调用的返回值

当您调用操作系统服务时,应始终检查返回值。例如,chdir的Perl文档是(加上强调)

chdir EXPR
chdir FILEHANDLE
chdir DIRHANDLE
chdir

如果可能,将工作目录更改为EXPR。如果省略EXPR,则更改为$ENV{HOME}指定的目录(如果已设置);如果不是,则更改为$ENV{LOGDIR}指定的目录。(在VMS下,变量$ENV{SYS$LOGIN}也会被检查,如果设置了,则使用它。)如果两者都未设置,则chdir不执行任何操作成功时返回true,否则返回false参见die下的示例。

在支持fchdir(2)的系统上,可以传递文件句柄或目录句柄作为参数。在不支持fchdir(2)的系统上,传递句柄会引发异常。

正如您在问题中所写的,您的代码将丢弃重要信息:系统调用chdirrename是成功还是失败。

提供有用的错误消息

在Perl中检查返回值的一个常见习惯用法是

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

错误消息包含三个重要的信息位:

  • 发出错误的程序$0
  • 它试图做什么,在这种情况下是chdir
  • 为什么失败,$!

还要注意,如果错误消息没有以换行符结尾,die还会显示文件名和程序控制所在的行号。当chdir失败时,标准错误将类似

/myprogram:chdir:上没有这样的文件或目录/myprogram第3行。

逻辑的或在其至少一个参数为真时为真。“要么做某事,要么死掉";这个习惯用法之所以有效,是因为如果上面的chdir失败,它将返回一个错误值,并要求or评估右侧,并使用die终止执行。在chdir成功并返回真值的愉快情况下,不需要评估右侧,因为我们已经有一个真参数要逻辑或。

对代码的建议改进

为了您;为此,我建议使用readdir,以避免其中一个文件名包含空白时出现问题。请注意下面代码中的defined测试:;它可以阻止一个名为0,即,一个零字符)的文件终止循环。

#! /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: $!";
}

为了让rename有任何希望,您必须保留$oldname的值,所以上面的代码立即将其复制到$newname,并开始更改副本而不是原始副本。你会看到

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

在Perl代码中,因此它也是一个需要理解的重要习惯用法。

perlop文档定义了用于字符串和正则表达式替换的方便的转义序列:

仅限l小写下一个字符
u标题大小写(非大写!)仅下一个字符
L小写显示\E之前的所有字符
U将所有字符大写,直到\E出现
Q引用非单词字符直到\E
E结束案例修改或引用章节(以最后看到的为准)

上面的代码获取前八个字符(如果$newname的长度较短,则更少),并将它们替换为大写的对应字符。

示例输出

查看操作代码:

$ls目录\路径/defmwghijk mwabc nochange qrstuvxyzmw$/prog$ls目录\路径/ABC DEFGHIJK QRSTUVXyz nochange

我认为您的需求比您告诉我们的要多,比如文件扩展名的大小写部分。与其匹配前八个字符,不如匹配前八位字母:

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 → $newn";
    }

其他注意事项:

  • 您可以直接在Perl中执行glob。您不需要ls
  • 看起来你脱下了mv,所以我就这么做了。如果这不是你想要的,很容易改变

您可以使用substr的4参数形式来代替正则表达式对前八个字符进行大写。这提供了现场更换。

my $old = q(abcdefghij);
my $new = $old;
substr( $new, 0, 8, substr( uc($old), 0, 8 ) );
print "$oldn$newn";
abcdefghij
ABCDEFGHij

使用renameFile::Copy::move(如M42所示)执行实际重命名。

最新更新