0

我的 Linux 系统挂载了一些 Samba 共享,一些文件是 Windows 用户存放的。这些文件的名称有时包含空格和其他不需要的字符。将这些字符更改为连字符-似乎是一个合理的解决方案。无需更改任何其他内容即可处理这些已清理的文件名。

几个问题,

  • 除了空格、括号外还有哪些字符需要翻译?

  • 应该检查哪些其他文件属性(除了文件类型(文件/目录)和权限)?

  • Perl 是否提供pushd/popd等价物,或者是chdir遍历目录树的合理解决方案?

这是我的 Perl 程序

#!/bin/env perl
use strict;
use warnings;

use File::Copy;

#rename files, map characters (not allowed) to allowed characters
#map [\s\(\)] to "-"

my $verbose = 2;
my $pat     = "[\\s\\(\\)]";

sub clean {
  my ($name) = @_;
  my $name2 = $name;
  $name2 =~ s/$pat/\-/g;

  #skip when unchanged, collision
  return $name if (($name eq $name2) || -e $name2);    #skip collisions

  print "r: $name\n" if ($verbose > 2);
  rename($name, $name2);

  $name2;
}

sub pDir {
  my ($obj) = @_;
  return             if (!-d $obj);
  return             if (!opendir(DIR, $obj));

  print "p: $obj/\n" if ($verbose > 2);
  chdir($obj);

  foreach my $dir (readdir DIR) {
    next if ($dir =~ /^\.\.?$/);    #skip ./, ../
    pDir(clean($dir));
  }
  close(DIR);
  chdir("..");
}

sub main {
  foreach my $argv (@ARGV) {
    print "$argv/\n" if ($verbose > 3);
    $argv = clean($argv);
    if (-d $argv) { pDir($argv); }
  }
}

&main();

这些帖子是相关的,但并没有真正解决我的问题,

4

2 回答 2

3

这是思考问题的另一种方式:

  1. Perl 有一个内置的重命名函数。你应该使用它。

  2. 创建一个将旧名称映射到新名称的数据结构。拥有这些数据将允许进行各种健全性检查:例如,您不希望清理过的名称覆盖现有文件。

  3. 由于您没有递归处理目录,因此您可以glob充分利用。无需经历打开目录、读取目录、过滤点目录等的麻烦。

  4. 不要使用前导 & 号调用子例程(搜索此问题以获取更多详细信息)。

  5. 许多类 Unix 系统包括一个基于 Perl 的重命名命令,用于快速而肮脏的重命名作业。即使您不将它用于当前项目,也很高兴知道。

这是一个粗略的大纲:

use strict;
use warnings;

sub main {
    # Map the input arguments to oldname-newname pairs.
    my @renamings = 
        map { [$_, cleaned($_)] }
        map { -f $_ ? $_ : glob("$_/*")  }
        @_;

    # Sanity checks first.
    #   - New names should be unique.
    #   - New should not already exist.
    #   - ...

    # Then rename.
    for my $rnm (@renamings){
        my ($old, $new) = @$rnm;
        rename($old, $new) unless $new eq $old;
    }
}

sub cleaned {
    # Allowed characters: word characters, hyphens, periods, slashes.
    # Adjust as needed.
    my $f = shift;
    $f =~ s/[^\w\-\.\/]/-/g;
    return $f;
}

main(@ARGV);
于 2013-10-16T03:21:17.863 回答
0

不要将问题归咎于 Windows。Linux 要宽松得多,它在文件名中禁止使用的唯一字符是 NUL。

目前还不清楚你在问什么。您是否发布了代码以供批评,或者您有问题?

至于你问的具体问题,

  • 除了空格、括号外还有哪些字符需要翻译?

    Windows 允许在其文件名中使用任何字符,但从 0x00 到 0x1F 的控制字符以及任何

    < > \ / * ? |

    DEL在 0x7F 很好。

    在 ASCII 集合内,留下

    !# $ % & ' ( ) + , - . : ; =@[]^_`{}~

    您需要翻译的字符集取决于您这样做的原因。您可能希望从排除非 ASCII 字符开始,因此您的代码应类似于

    $name2 =~ tr/\x21-\x7E/-/c

    这会将所有非 ASCII 字符、空格和 DEL 更改为连字符。然后您需要继续修复您认为不合适的所有 ASCII 字符。

  • 应该检查哪些其他文件属性(除了文件类型(文件/目录)和权限)?

    这个问题的答案必须根据你的目的。如果您只是指是否可以根据需要重命名文件或目录,那么我建议您让rename自己告诉您是否成功。如果操作失败,它将返回一个false值,原因将在$!.

  • Perl 是否提供了 pushd/popd 等价物,还是 chdir 是遍历目录树的合理解决方案?

    如果你想使用那个成语,那么你应该看看File::pushd,它可以让你暂时chdir到一个新的位置。Apopd在封闭块的末尾隐式完成。

我希望这有帮助。如果您有任何其他具体问题,请通过编辑您的原始帖子让他们知道。

于 2013-10-16T21:54:15.530 回答