使用空格规范化路径的Perl函数?

时间:2016-03-27 15:24:17

标签: perl filenames pathname

我处于维护模式,我正在使用在Apple,Linux,Windows和Unix上运行的Perl脚本。一些Apple和Linux以及大多数Windows在路径中都有空格。在Windows上,长文件名需要引号。在Apple和Linux上,空间需要反斜杠。如果没有空间,则无需进行任何操作。

Perl的File::CopyFile::Spec了解系统差异,并将其抽象为不同的文件系统。通过查看其他File函数,我不会看到用于规范化或规范化路径名的内容,该路径名会根据需要添加引号,斜杠,移动引号等。

Perl版本要求是v5.10。所以我应该能够毫无困难地期望至少v5.10。

使用空格规范化或规范化路径的Perl函数是什么?

这是Windows上一个过于简单的示例:

my $testcat = catfile(catdir("\"C:\\Program Files\"", "My Program"), "test.txt");
print "Test cat: $testcat\n";

结果如下。请注意引用不正确,路径分隔符错误。

Test cat: "C:/Program Files"/My Program/test.txt

以下是我在Windows系统上的专家(或错误):

Test cat: "C:\Program Files\My Program\test.txt"

有类似的问题,但它们似乎都是一次性的。例如,How to handle filenames with spaces?表示手动为Windows添加引号。我正在寻找Perl例程来实现它。

2 个答案:

答案 0 :(得分:2)

我不确定为什么你认为你需要一个库函数来用双引号包装东西?

你太早报道混合报价/逃脱。它们仅在某些情况下才需要,因为它们是较长字符串的一部分,将被视为以空格分隔的子字符串列表。最明显的例子是cmd / bash的命令行

当你在程序中使用字符串时,你需要只是普通路径字符串而不需要任何装饰。一旦你构建了你的路径,用它周围的引号创建你的命令行(或其他),它应该全部工作

我从来没有能够获得Windows cmd的逃逸字符(这是一个回旋^)可靠地工作,所以我总是用双引号包装任何包含空格字符的字符串。这适用于Windows和任何类型的Unix,包括OSX

以下是使用问题中的代码的示例。请注意,不必非常谨慎地使用catdircatfile:除非您正在构建像C:\这样的根目录,否则它们在没有语法区别的系统上的行为相同文件和目录(),其中包括您在问题中提到的所有平台

use strict;
use warnings 'all';

use File::Spec::Functions qw/ catfile /;

my $testcat = catfile('C:\Program Files', 'My Program', 'test.txt');

print qq{Test cat: "$testcat"\n};

system qq{type "$testcat"};

输出

Test cat: "C:\Program Files\My Program\test.txt"
TESTCAT CONTENTS


更新

这是另一个示例,说明在使用之前,已到达程序的路径段是如何未加引号。我已经定义了三个标量变量。其中一些或全部可能源自您的程序之外,而其他可能像这样定义为字符串文字。关键是$root包含在不需要的双引号中;它是一个无效的路径段,如果将其传递给catfile

,则无效

所以我编写了一个小子程序unquote并将其应用于所有三个,因为我们假装我们不知道哪些段被引用而哪些不是。正如您在输出中看到的那样,它会从$root中删除引号,但不会触及其他两个字符串。现在它们都是有效的,可以传递给catfile

输出显示catfile返回Test cat: C:\Program Files\My Program\test.txt这是我们想要的。现在假设我们想输入它,所以我们需要创建命令行

type "C:\Program Files\My Program\test.txt"

在命令行的上下文中,双引号是分隔路径字符串所必需的,但它们不是路径的一部分

再次,正如您所看到的,对system的调用工作正常。我的文件包含TESTCAT CONTENTS,这就是我的程序打印

我希望有帮助吗?

use strict;
use warnings 'all';
use feature 'say';

use File::Spec::Functions qw/ catfile /;

my ($root, $dir, $file) = ( '"C:\Program Files"', 'My Program', 'test.txt');

print <<END;
Original:
Root: $root
Dir:  $dir
File: $file

END


unquote($_) for $root, $dir, $file;


print <<END;
Unquoted:
Root: $root
Dir:  $dir
File: $file

END


my $testcat = catfile($root, $dir, $file);

say "Full path: $testcat";

my $cmd = qq{type "$testcat"};
say "Command is:\n$cmd\n";

system $cmd;


sub unquote {
    $_[0] =~ s/\A"([^"]*)"\z/$1/;
    $_[0];
}

输出

Original:
Root: "C:\Program Files"
Dir:  My Program
File: test.txt

Unquoted:
Root: C:\Program Files
Dir:  My Program
File: test.txt

Full path: C:\Program Files\My Program\test.txt
Command is:
type "C:\Program Files\My Program\test.txt"

TESTCAT CONTENTS

答案 1 :(得分:2)

我不确定你是如何设法得到你描述的输出的。在Windows上我得到:

  

测试猫:“C:\ Program Files”\ My Program \ test.txt

在OSX上,我得到:

  

测试猫:“C:\ Program Files”/我的程序/ test.txt

您使用的是哪种操作系统和Perl版本?是否有可能遗漏了脚本的一些相关部分。

你的example显示出在Perl中引用和转义字符串的困惑。它可能有助于将其分解成更小的部分以查看正在发生的事情并将这些部分组合在一起:

print "\"C:\\Program Files\""
  

“C:\ Program Files”

这可能是你的预期。它使用原始插值来构建您要使用的字符串。注意:您可以使用非插值字符串简化此语句:

print '"C:\Program Files"'

追加目录,开始使用File :: Spec:

use File::Spec::Functions;
print catdir('"C:\Program Files"', "My Program")
  

“C:\ Program Files”\ My Program

这是事情变得时髦的地方。 catdir expects a list of directories,但您提供的字符串几乎肯定是目录作为列表中的第一项。

如果您在目录前加上C:\卷,那么您很可能真的想要使用catpath function

  
      
  • catpath()

         

    获取卷,目录和文件部分并返回整个路径。在Unix下,$ volume被忽略,并且目录和文件被连接在一起。如果需要,插入'/'。在其他操作系统上,$ volume非常重要。

    $full_path = File::Spec->catpath( $volume, $directory, $file );
    
  •   

如果有空格,结果字符串将不能直接在命令行上使用,因为Perl做了一些相当的Unixish假设。但是,正如related question的答案所指出的那样,您可以在构建路径后插入双引号。事实证明,OSX和Linux上的双引号转义保护空间;你不需要逃离每个单独的空间。

或者,使用专为完成您要做的任何事情而设计的模块。例如,File::Copy可以很好地解决跨平台问题。