检索具有双文件扩展名的 $_.Extension

Retrieving $_.Extension with double file extensions

Powershell 的 .Extension 方法似乎无法识别双重扩展名。 结果,像这样:

Get-ChildItem -Path:. | Rename-Item -NewName {
  '{0}{1}' -f ($_.BaseName -replace '\.', '_'), $_.Extension
}

最终将 file.1.tar.gz 重命名为 file_1_tar.gz,而我想要 file_1.tar.gz

只是一个疏忽? 还是可以缓解?

这是预期的行为。

引用自 Microsoft Doc。关于 [System.IO.Path]::GetExtension 方法,它似乎与其他类似功能和 Powershell Cmdlet 共享相同的实现逻辑。

This method obtains the extension of path by searching path for a period (.), starting with the last character in path and continuing toward the first character. If a period is found before a DirectorySeparatorChar or AltDirectorySeparatorChar character, the returned string contains the period and the characters after it; otherwise, String.Empty is returned.

您当然可以通过为 tar.gz 和其他双重扩展创建异常来缓解这种情况,方法是使用字典来定义应被视为双重异常的内容。这是一个缓解的例子。

Get-ChildItem -Path 'C:\temp' | % {
    $BaseName = $_.BaseName
    $Extension = $_.Extension
    if ($_.FullName.EndsWith('.tar.gz')) {
        $BaseName = $_.BaseName.SubString(0,$_.BaseName.Length -4)
        # I used the full name as reference to preserve the case on the filenames
        $Extension = $_.FullName.Substring($_.FullName.Length - 7)
    }

    Rename-Item -Path $_.FullName -NewName (
        '{0}{1}' -f ($BaseName -replace '.', '_'), $Extension
    )

}

请注意,由于我只有 .tar.gz,我实际上并没有使用字典,但如果您有多个双扩展类型,最好的方法是通过对每个扩展的循环来应用此方法以查看如果匹配。

以这个为例,它循环遍历一个数组来检查多个双重扩展


# Outside of the main loop to avoid redeclaring each time.
$DoubleExtensionDict = @('.tar.gz', '.abc.def')

Get-ChildItem -Path 'C:\temp' | % {
    $BaseName = $_.BaseName
    $Extension = $_.Extension

    Foreach ($ext in $DoubleExtensionDict) {
        if ($_.FullName.EndsWith($ext)) {
            $FirstExtLength = ($ext.split('.')[1]).Length 
            $BaseName = $_.BaseName.SubString(0, $_.BaseName.Length - $FirstExtLength -1)
            $Extension = $_.FullName.Substring($_.FullName.Length - 7)
            break
        }
    }
    
     Rename-Item -Path $_.FullName -NewName (
         '{0}{1}' -f ($BaseName -replace '.', '_'), $Extension
     )

}

参考资料

Path.GetExtension Method

所述,在设计上只有 last . 分隔的标记被认为是文件扩展名。

没有创建已知“多扩展”的哈希表(字典),正如 Sage 所建议的那样 - 这将不得不预料到 所有 - 你可以根据以下规则尝试heuristic(因为你需要排除考虑.1部分的多重扩展):

  • 任何非空 运行 的 . 分隔标记 不以数字 开头,在 结尾 [=31] =] 的文件名,被认为是多扩展名,包括 . 个字符:
@{ Name = 'file.1.tar.gz'},
@{ Name = 'file.1.tar.zip'},
@{ Name = 'file.1.html'},
@{ Name = 'file.1.ps1'},
@{ Name = 'other.1'} | 
  ForEach-Object {
    if ($_.Name -match '(.+?)((?:\.[^\d]+))$') {
      $basename = $Matches[1]
      $exts = $Matches[2]
    } else {
      $basename = $_.Name
      $exts = ''
    }
    ($basename -replace '\.', '_') + $exts
  }

以上结果:

file_1.tar.gz
file_1.tar.zip
file_1.html
file_1.ps1
other_1