【问题标题】:Detecting invalid (Windows) filenames检测无效 (Windows) 文件名
【发布时间】:2021-09-14 14:52:32
【问题描述】:

我们有供 Windows 和 Mac 客户端使用的 SMB 共享。我们想将一些数据移动到 Sharepoint,但需要根据 Windows 中不允许的字符验证文件名。尽管 Windows 用户无论如何都无法创建包含非法字符的文件,但 Mac 用户仍然可以创建包含 Windows 中非法字符的文件。

问题在于,对于名称中包含非法字符的文件,Windows/Powershell 会将这些字符替换为私有地址 unicode 代码点。这些因输入字符而异。

$testfolder = "\\server\test\test*dir"   # created from a Mac
$item = get-item -path $testfolder
$item.Name                               # testdir
$char = $($item.Name)[4]                 # 
$bytes = [System.Text.Encoding]::BigEndianUnicode.GetBytes($char) # 240:33
$unicode = [System.BitConverter]::toString($bytes)                # F0-21

对于名称为 pipe| 的文件,上面的代码会产生输出 F0-27,因此它不仅仅是一个通用的“无效”字符。

当我实际上无法获取值时,如何检查文件名中的无效值??

【问题讨论】:

  • 文件应该可以通过在 MacOS 上运行 PowerShell 来识别。 github.com/PowerShell/PowerShell
  • 没错,这绝对是一个可行的解决方案。但是我很固执,想找到一个解决方案,不需要我为了运行脚本而切换平台。感谢您指出这一点!

标签: windows powershell filesystems


【解决方案1】:

正如经常发生的那样,在尝试尽可能准确地提出我的问题时,我找到了一个解决方案。对于如何更优雅地解决这个问题,我仍然很喜欢任何其他答案,但由于我没有找到任何其他资源与此信息,我在这里提供我的解决方案,希望它可以帮助其他人解决同样的问题。

无效字符映射到特定代码点

注意:我是从我所做的观察中推断出所有这些。我很高兴有人发表评论或提供更完整或更正确的替代答案。

有一组字符对 Windows 文件名无效,但这是操作系统的限制,而不是文件系统的限制。这意味着可以在 SMB 共享上设置在另一个操作系统(例如 MacOS)上有效但在 Windows 上无效的文件名。当 Windows 遇到这样的文件时,无效字符会被一组代理 unicode 代码点遮蔽,这允许 Windows 与文件交互而无需重命名它们。这些代码点位于覆盖0xE000-0xF8FF 的unicode Private Use Area。由于这些代码点未映射到可打印字符,Powershell 将它们全部显示为 ▯ (U+25AF)。在我的特定用例中,我需要报告文件名中存在哪些无效字符,因此这个通用字符消息没有帮助。

通过实验,我能够确定每个可打印受限字符的代理代码点。我已将它们包含在下面以供参考(注意:关于此的 YMMV,我尚未在多个系统上对其进行测试,但我怀疑它在版本之间是一致的)。

Character Unicode
" 0xF020
* 0xF021
/ 0xF022
< 0xF023
> 0xF024
? 0xF025
\ 0xF026
| 0xF027
(trailing space) 0xF028

: 不允许在我可以轻松访问的任何系统上的文件名中使用,因此我无法测试那个。

在 Powershell 中测试名称

既然我们知道了这一点,那么在 powershell 中解决这个问题就很简单了。我创建了一个哈希表,其中所有代理 unicode 点作为键,“真实”字符作为值,然后我们可以将其用作查找表。在测试名称之前,我选择替换文件名字符串中的字符。这使得调试更容易。

#Set up regex for invalid characters
$invalid = [Regex]::new('^\s|[\"\*\:<>?\/\\\|]|\s$') 

#Create lookup table for unicode values
$charmap = @{
    [char]0xF020 = '"'
    [char]0xF021 = '*'
    [char]0xF022 = '/'
    [char]0xF023 = '<'
    [char]0xF024 = '>'
    [char]0xF025 = '?'
    [char]0xF026 = '\'
    [char]0xF027 = '|'
    [char]0xF028 = ' '
} 

Get-ChildItem -Path "\\path\to\folder" -Recurse | Foreach-Object {
    # Get the filename 
    $fixedname = split-path -path $_.FullName -leaf

    #Iterate through the hashtable and replace all the proxy characters with printable versions
    foreach($key in $charmap.getEnumerator()){
         $fixedname = $fixedname.Replace($key.Name,$key.Value)
    }
     #Build a list of invalid characters to include in report (not shown here)
     $invalidmatches = $invalid.Matches($fixedname)
     if ($invalidmatches.count -gt 0) {
         $invalidchars = $($invalidmatches | foreach-object {
           if ($_.value -eq ' '){"Leading or trailing space"} else {$_.value}}) -join ", "
     }
}

扩展解决方案

理论上,您还可以扩展它以涵盖其他禁止的字符,例如 ASCII 控制字符。由于这些代理 unicode 点位于 PUA 中,并且没有关于如何处理的文档(据我所知),因此发现这些关联取决于实验。我很乐意在这里停下来,因为我已经遍历了 MacOS 系统上的用户可以轻松放入文件名的所有字符。

【讨论】:

    猜你喜欢
    • 2013-08-31
    • 1970-01-01
    • 2022-06-21
    • 2016-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-08
    相关资源
    最近更新 更多