如何让PowerShell很好地处理文件名中的[ or ]?

wlp8pajw  于 2023-03-18  发布在  Shell
关注(0)|答案(2)|浏览(406)

我从PowerShell - Batch change files encoding To UTF-8修改了PowerShell脚本。

# Modified version of https://stackoverflow.com/q/18684793

[Threading.Thread]::CurrentThread.CurrentUICulture = 'en-US'

$Encoding = New-Object System.Text.UTF8Encoding($True) # If UTF8Encoding($False), It will be UTF-8 without BOM
$source = "C:\Users\AKULA\Desktop\SRC" # source directory
$destination = "C:\Users\AKULA\Desktop\DST" # destination directory

if (!(Test-Path $destination)) {
    New-Item -Path $destination -ItemType Directory | Out-Null
}

# Delete all previously generated file
Get-ChildItem -Path $destination -Include * -File -Recurse | ForEach-Object {$_.Delete()}

# Recursively convert all files into UTF-8
foreach ($i in Get-ChildItem $source -Force -Recurse -Exclude "desktop.ini") {
    if ($i.PSIsContainer) {
        continue
    }

    $name = $i.Fullname.Replace($source, $destination)

    $content = Get-Content $i.Fullname

    if ($null -ne $content) {
        [System.IO.File]::WriteAllLines($name, $content, $Encoding)
    } else {
        Write-Host "No content from: $i"   
    }
}

但是使用之后,我发现PS不能很好的处理[或者],我做了一些测试文件,文件名/内容都有差异。

Get-Content : An object at the specified path C:\Users\AKULA\Desktop\SRC\FILENAME[[[[[[]]]]]]]].txt does not exist, or
has been filtered by the -Include or -Exclude parameter.
At C:\Users\AKULA\Desktop\Convert_to_UTF-8.ps1:24 char:16
+     $content = Get-Content $i.Fullname
+                ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (System.String[]:String[]) [Get-Content], Exception
    + FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetContentCommand

由于我不能嵌入图像的问题,这里是IMGUR相册的链接。
完整图像列表:https://imgur.com/a/aN1RG2L
这些是我测试过的:

  • 测试文件有不同的名称。它们的名称包含空格、'[]。还组成了不同的语言(日语、韩语)。
  • 这些文件有相同的内容,编码与UCS-2 BE BOM(UTF-16 BE),以便我可以检查它是否已重新编码为UTF-8。

我怎样才能让我的脚本处理好文件名中的[]

ycl3bljg

ycl3bljg1#

TL;医生

实际上,使用**-LiteralPath参数是最佳解决方案(在PowerShell(Core)v6+中,您可以缩短为-lp**):

$content = Get-Content -LiteralPath $i.Fullname

-LiteralPath确保$i.Fullname被 * 逐字 *(照字面意思)采用;也就是说,由于被解释为wildcard expression,路径中的[]被解释为 * 它们自己 ,而不是具有 * 特殊含义 ,因为它们将具有-Path参数-注意,如果您仅将 value(字符串)作为第一个参数传递,则-Path是 * 位置隐含的***,就像你做的那样(Get-Content $i.FullName

注意:此答案类似地适用于同时具有-Path
-LiteralPath参数
,例如Set-ContentOut-FileSet-Location
至于你所尝试的

$content = Get-Content $i.Fullname

实际上等同于:

$content = Get-Content -Path $i.Fullname

也就是说,传递给Get-Content的(第一个) 位置 * 参数隐式绑定到
-Path参数
*。

-Path参数接受wildcard expressions以允许按 * 模式 * 匹配路径;除了支持*(任意字符串)和?(正好1个字符)之外,通配符模式中的[...]表示字符集****或范围(例如,[12][0-9])。

因此,包含[...](例如foo[10].txt)的实际路径 * 不 * 被如此识别,因为[10]被解释为匹配 * 单个 * 字符的字符集,该字符是 * 10;也就是说,foo[10].txt将匹配foo0.txtfoo1.txt,但不是字面上名为foo[10].txt的文件。
当(隐式地)使用-Path时,* 有可能 * 转义 * []示例,这些示例应该被逐字地解释,即通过反勾号(```),但是注意,当涉及引用和/或变量引用时,这可能会变得很棘手。

**如果您知道某个路径是文本路径,最好养成使用-LiteralPath**的习惯(在PowerShell Core 中,您可以将其缩短为-lp)。
但是,如果您的路径包含 literal[],并且您 * 还 * 需要通配符匹配,则必须使用```-转义-请参见this answer

nfzehxib

nfzehxib2#

不幸的是,至少在两种情况下,解决方案的好建议并不适用。

选择性错误处理(PS:仅在旧版Windows PowerShell中存在问题)

Get-Content -LiteralPath "nobox[]"给出错误消息和异常类型,就好像包含通配符一样:

Get-Content : An object at the specified path box[] does not exist, or has been filtered by the -Include or -Exclude parameter.
At line:1 char:1
+ Get-Content -Path "nobox[]"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (System.String[]:String[]) [Get-Content], Exception
    + FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetContentCommand

而没有括号,我们得到:

Get-Content : Cannot find path 'nobox' because it does not exist.
At line:1 char:1
+ Get-Content -LiteralPath "nobox"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (nobox:String) [Get-Content], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand

因此,要静默处理可选文件,而不是直接抑制每个异常,请执行以下操作:

try {
        $lines = Get-Content -LiteralPath $path -ErrorAction Stop
    }
    catch [System.Management.Automation.ItemNotFoundException] {
        $lines = @()
    }

带支架的路径上的扼流圈。

创建硬链接或符号链接

一个小的和一个大的警告:

  • 新项的名称Path参数“与其他cmdlet的LiteralPath参数工作方式类似”,New-Item的文档中对此有明确说明,这似乎是正确的,也是有意义的,尽管我希望我们可以通过编写-LiteralPath来澄清这一点。
  • Value参数,链接的目标(在v5中秘密地称为Target,在以后公开地称为Target),根据相同的文档不接受通配符,但这是一个谎言。
New-Item -ItemType "HardLink" -Path "whatever" -Target "*"

使Powershell发出“无法设置位置,因为路径”*“解析为多个容器。"。
所以你总是需要目标的转义,如果你有一个名为“f[]"的文件,那么这将显示一个错误:

New-Item -ItemType "HardLink" -Path "whatever" -Target "f[]"

这将创建一个链接:

New-Item -ItemType "HardLink" -Path "f[2]" -Target ([WildcardPattern]::Escape("f[]"))

项目类型“符号链接”也是如此。

相关问题