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

问题描述 投票:0回答:2

我将 PowerShell 脚本从 PowerShell - 批量更改文件编码更改为 UTF-8

# 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://i.sstatic.net/LtSBS.jpg

这些是我测试过的:

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

如何让我的脚本很好地处理文件名中的

[
]

powershell path glob literals
2个回答
3
投票

tl;博士

确实,使用

-LiteralPath
参数是最好的解决方案(在 PowerShell (Core) v6+ 中,您可以缩短为
-lp
):

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

-LiteralPath
确保
$i.Fullname
被逐字记录(字面意思);也就是说,路径中的
[
]
 被解释为 
本身 而不是具有 特殊含义 ,因为它们被解释为 通配符表达式,因此它们将作为
-Path
参数 - 请注意,如果您只传递 (字符串)作为第一个参数,则
-Path位置隐含的
,就像您所做的那样 (Get-Content $i.FullName
)

注意:此答案类似地

适用于同时具有 -Path

-LiteralPath
参数
all
cmdlet,例如
Set-Content
Out-File
Set-Location


至于

你尝试过的

$content = Get-Content $i.Fullname
 实际上等同于:

$content = Get-Content -Path $i.Fullname
也就是说,传递给 

Get-Content 的(第一个)positional 参数隐式绑定到

-Path
参数
.

-Path参数接受

通配符表达式
以允许通过模式匹配路径;除了支持 *(任何字符串)和
?
(恰好 1 个字符)之外,通配符模式内的
[...] 表示
字符集
或范围(例如,[12][0-9]
)。
因此,包含 
[...]

(例如

foo[10].txt

)的实际路径
不会
被识别为这样,因为 
[10] 被解释为与 单个
字符匹配的字符集,即 
either 10
;也就是说 
foo[10].txt
 将匹配 
foo0.txt
foo1.txt
,但不是字面名为 
foo[10].txt
 的文件。
当(隐式)
使用

-Path

时,可以

转义
[]实例
,这些实例应该逐字解释,即通过
反引号(
`
,但请注意当涉及引用和/或变量引用时,这可能会变得很棘手
如果您知道路径是文字路径,最好养成使用 -LiteralPath

的习惯(在 PowerShell Core

 中您可以缩写为 
-lp)。
但是,如果您的路径包含 
literal

[] 并且您

also
需要通配符匹配,则必须使用
`
-escaping - 请参阅此答案
不幸的是,至少在两种情况下,该解决方案的好建议并不成立。


1
投票

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
  • ,后来公开),根据相同的文档不接受通配符,但这是一个谎言。命令:
  • 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[]"))
与 ItemType“SymbolicLink”相同。


© www.soinside.com 2019 - 2024. All rights reserved.