【问题标题】:I want -e to work case-sensitively on OS X. Is it possible?我希望 -e 在 OS X 上区分大小写。有可能吗?
【发布时间】:2012-01-19 02:28:04
【问题描述】:

我正在开发一个使用-e 标志的脚本,如

unless (-e $fileName)
{
  ...
}

这在 OS X 上运行良好。或者,它不能正常运行。我希望它区分大小写。该脚本也在 Linux 机器上运行,-e 检查失败--正确!--因为区分大小写。

我试图用open <FILEHANDLE, '$fileName') 做一个备用路径,但它似乎也不区分大小写。

编辑:感谢下面回答的所有人。我知道 HFS+ 不区分大小写,但我认为我可以以某种方式“强制”它。我确实最终通过执行以下操作来强制检查:

opendir my($dh), $dirName or die "Couldn't open dir '$dirName'";
my @refFiles = readdir $dh;
closedir $dh;

foreach $refFile (@refFiles)
{
    if ($refFile eq $refFileName)
    {
        $found = 1;
    }
}

著名的口头禅:“它不漂亮,但它有效。”

【问题讨论】:

  • 你的 '$fileName' 根本不应该使用引号,如果是这样,它应该使用双引号(除非你是反常的 - 像我一样 - 并在单引号前加上 qq'$fileName') .同样,<FILEHANDLE 符号也很时髦。使用 3 参数版本:open my $fh, '<', $fileName or die "A horrid death";.
  • 另外,你的标题说“区分大小写”,而问题说“不区分大小写”;你真正想要的是哪个?
  • -e 只是将参数传递给系统的 stat 调用。
  • @Jonathan Leffler:需要大小写-敏感性;即,希望只有在文件名操作数是大小写精确的情况下才匹配 -e(不幸的是,在 OSX 等不区分大小写的文件系统上,-e 无法做到这一点);我已经更正了问题正文中的不一致之处。
  • 鉴于需要在不区分大小写的文件系统上进行区分大小写的匹配,我相信“从目录中读取文件名直到找到匹配或没有名称”的一些变体是唯一可行的解​​决方案。任何按名称访问文件的尝试都将不区分大小写(默认情况下)。

标签: macos perl case-sensitive case-insensitive


【解决方案1】:

不区分大小写是由于您使用的文件系统 (HFS+),而不是 Perl 函数 -eopen,也不是底层的 stat(2) open(2) 系统调用。

HFS+ 文件系统默认不区分大小写;但是,您可以选择在初始化新文件系统时创建一个区分大小写的 HFS+ 卷(使用 Disk Utilitydiskutil 或 *newfs_hfs* 等):只需选择“区分大小写”版本。

我已经看到(写得不好的)程序从区分大小写的启动卷运行时出现故障的报告,因此我建议您为区分大小写的工作使用单独的卷。例如,使用“Mac OS Extended (Case-sensitive, Journaled)”文件系统创建一个磁盘映像(即“稀疏磁盘包映像”),并在您需要进行区分大小写的工作时挂载它(例如open ~/case-sensitive.sparsebundle,然后cd /Volumes/Case-sensitive/foobar 从那里做区分大小写的工作)。

【讨论】:

    【解决方案2】:

    默认情况下,HFS+ 不区分大小写,因此 -e 也必须如此,因为它使用文件系统原语进行检查。让 -e 在不区分大小写的文件系统上区分大小写是不正当和邪恶的;您可以使用 -e 确认文件不存在的代码,然后随后打开截断现有文件,或者打开失败,因为该文件存在并且不可读/不可写。

    【讨论】:

      【解决方案3】:

      在 MacOS X 10.7.2 上使用自制的 Perl(5.14.1 - 我必须尽快升级)和以下脚本(称为 x.pl):

      #!/usr/bin/env perl
      use strict;
      use warnings;
      
      foreach my $file (@ARGV)
      {
          print "OK $file\n" if (-e $file);
      }
      

      我将命令调用为:

      $ perl x.pl x.pl X.pl X.PL xxx.sql
      OK x.pl
      OK X.pl
      OK X.PL
      OK xxx.sql
      $
      

      (我的当前目录中恰好有一个文件xxx.sql。)

      这是一个标准的 MacOS X 硬盘;我没有让它区分大小写(我相信这是一个选项)。

      因此,根据我现有的证据,-e 运算符在 MacOS X 上的 Perl 中适当地不区分大小写。

      【讨论】:

        【解决方案4】:

        现有答案的重点是解释为什么 -e 在 OSX 上不区分大小写地工作,以及为什么它这样做是有充分理由的。

        但是,有时您确实想知道路径是否以大小写精确的形式存在,因为即使 文件系统 可能不关心,其他软件 可能

        Perl 本身就是一个例子:
        在 OSX 上,Perl(不幸的是)接受带有 use 语句的模块名称的大小写变体,因为 use 执行文件系统查找,这本质上是不区分大小写的。但是,实际上 using 该模块将不起作用,除非模块名称是 case-EXACT;例如:perl -MData::Dumper -E 'print Dumper(666)'(OK)vs.perl -Mdata::dumper -E 'print Dumper(666)'(中断,因为即使加载模块看似成功,它的元素,例如Dumper(),也无法访问)。

        问题简而言之:如果路径 /FOO/BAR 存在于您的 HFS+ 文件系统中(在 OSX 上),
        -e '/foo/bar' 或其任何大小写变化都将表明该路径存在,并且没有简单的方法来确定是否存在/foo/bar 是一个大小写精确的表示,或者更一般地说,是路径的真实大小写。

        这是一个使用以下方法的简单实现

        • 利用File::Glob::bsd_glob() 默认情况下区分大小写这一事实,即使在 OSX 上也是如此,其文件系统 (HFS+) 默认情况下不区分大小写。
        • 但是,匹配只发生在包含实际 pattern 的路径组件上,而 literal 名称按原样返回。
        • 因此,从输入路径构造一个 glob,每个 通过将第一个字符括起来,将路径组件转换为自匹配模式。 [] 中的每个路径组件(仅匹配该字符本身的集合)。
        sub istruecasepath {
          use File::Glob qw/bsd_glob/;
          return defined bsd_glob join '', map { 
              m`^(//?|\.|\.\.|)$` ? 
                $_ 
                : 
                '[' . substr($_, 0, 1) . ']' . substr($_, 1)
            } split m`(//?)`, shift;
        }
        

        这适用于以下限制

        • 输入路径不得包含非 ASCII 字符
        • 输入路径不得包含模式元字符(即路径组件不得包含文字字符。例如 *[ 可能会被误解为模式(通配符)字符。

        这是一个稳健的实现,它需要额外的工作来克服这些限制;输入应为 Unicode 字符串:

        sub istruecasepath {
          use File::Glob qw/:bsd_glob/;
          use Unicode::Normalize;
          # Convert to NFD Unicode normal form, because that's how HFS+ stores names.
          my $path_nfd_quoted = NFD shift;
          # \-quote glob characters and '\' instances.
          $path_nfd_quoted =~ s/[][{}*?\\]/\\$&/;                                                   # /
          my $glob = join '', map { 
              m`^(//?|\.|\.\.|)$` ? 
                $_ 
                : 
                do { my $len = m`^\\` ? 2 : 1; '[' . substr($_, 0, $len) . ']' . substr($_, $len) } 
            } split m`(//?)`, $path_nfd_quoted;
          return defined bsd_glob "$glob", GLOB_QUOTE;
        }
        

        作为奖励,这里有一个变体,返回文件系统中存储的大小写精确路径,给定一个大小写变化:

        sub getruecasepath {
          use File::Glob qw/:bsd_glob/;
          use Unicode::Normalize;
          # Convert to NFD Unicode normal form, because that's how HFS+ stores names.
          my $path_nfd_quoted = NFD shift;
          # \-quote glob characters and '\' instances.
          $path_nfd_quoted =~ s/[][{}*?\\]/\\$&/;                                                   # /
          my $glob = join '', map { 
              m`^(//?|\.|\.\.|)$` ? 
                $_ 
                : 
                do { my $len = m`^\\` ? 2 : 1; '[' . substr($_, 0, $len) . ']' . substr($_, $len) } 
            } split m`(//?)`, $path_nfd_quoted;
          my $path_truecase = bsd_glob "$glob", GLOB_NOCASE | GLOB_QUOTE;
          return if not defined $path_truecase;
          # !! bsd_glob() returns a raw UTF8-encoded byte sequence that isn't
          # !! automatically reconverted to a Unicode string, so we must do it 
          # !! manually here.
          utf8::decode($path_truecase);
          return $path_truecase;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-05-26
          • 2012-01-18
          • 1970-01-01
          • 2010-10-05
          • 1970-01-01
          • 1970-01-01
          • 2014-12-17
          相关资源
          最近更新 更多