2017-08-11 68 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' –