2017-08-11 34 views
1

我想要的Perl的等效Python的os.path.normpath()的:如何规范Perl中的路径? (而不检查文件系统)

通过折叠冗余分离器和上一级的引用归一路径名,使得甲// B,A/B /,A /。/B和A/foo /../ B都成为A/B。此字符串操作可能会更改包含符号链接的路径的含义。 [...]

例如,我想将'/a/../b/./c//d'转换为/b/c/d

我操纵的路径并不代表本地文件树中的真实目录。没有涉及的符号链接。所以一个普通的字符串操作很好。

我试过Cwd::abs_pathFile::Spec,但他们没有做我想做的。

my $path = '/a/../b/./c//d'; 

File::Spec->canonpath($path); 
File::Spec->rel2abs($path, '/'); 
# Both return '/a/../b/c/d'. 
# They don't remove '..' because it might change 
# the meaning of the path in case of symlinks. 

Cwd::abs_path($path); 
# Returns undef. 
# This checks for the path in the filesystem, which I don't want. 

Cwd::fast_abs_path($path); 
# Gives an error: No such file or directory 

可能相关链接:

回答

1

鉴于File :: Spec几乎是我所需要的,我最终编写了一个从File::Spec->canonpath()中删除../的函数。 The full code including tests is available as a GitHub Gist

use File::Spec; 

sub path_normalize_by_string_manipulation { 
    my $path = shift; 

    # canonpath does string manipulation, but does not remove "..". 
    my $ret = File::Spec->canonpath($path); 

    # Let's remove ".." by using a regex. 
    while ($ret =~ s{ 
     (^|/)    # Either the beginning of the string, or a slash, save as $1 
     (     # Followed by one of these: 
      [^/]|   # * Any one character (except slash, obviously) 
      [^./][^/]|  # * Two characters where 
      [^/][^./]|  # they are not ".." 
      [^/][^/][^/]+ # * Three or more characters 
     )     # Followed by: 
     /\.\./    # "/", followed by "../" 
     }{$1}x 
    ) { 
     # Repeat this substitution until not possible anymore. 
    } 

    # Re-adding the trailing slash, if needed. 
    if ($path =~ m!/$! && $ret !~ m!/$!) { 
     $ret .= '/'; 
    } 

    return $ret; 
} 
1

删除'。'和“..”的路径是相当直接的,如果你处理的路径从右到左:

my $path= "https://stackoverflow.com/a/../b/./c//d"; 
my @c= reverse split [email protected]/@, $path; 
my @c_new; 
while (@c) { 
    my $component= shift @c; 
    next unless length($component); 
    if ($component eq ".") { next; } 
    if ($component eq "..") { shift @c; next } 
    push @c_new, $component; 
} 
say "/".join("/", reverse @c_new); 

(假设路径以开始/)

注意,这违反了UNIX pathname resolution标准,特别是这部分:

以两个连续的斜线开头的路径名可以用实现定义的方式解释,尽管两个以上的斜线应该被视为单斜线。

+0

此代码失败用于''A/B /../../ C/D'' –

1

Path::Tiny模块正是这一点:

use strict; 
use warnings; 
use 5.010; 

use Path::Tiny; 
say path('/a/../b/./c//d'); 

输出:

/b/c/d 
+0

不适合我。 'Path :: Tiny'似乎和'File :: Spec'完全一样:'/ a /../ b/c/d' –