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

发布于 2025-01-10 05:28:00 字数 2210 浏览 3 评论 0 原文

我从 PowerShell - 批量更改文件编码为 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://i.sstatic.net/LtSBS.jpg

这些是我已经测试过:

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

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

I modified PowerShell script from PowerShell - Batch change files encoding To 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"   
    }
}

But after using it, I've found that PS cannot handle [ or ] well.
I made some test files that has diversity in name/content.

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

Since I cannot embed images in question, here is link of IMGUR album.
Full image list: https://i.sstatic.net/LtSBS.jpg

These are what I've tested:

  • Test files have different names. Their name contains space, ',
    []. Also made up different language(Japanese, Korean).
  • These files have same content, encoded with UCS-2 BE BOM(UTF-16 BE) so
    that I can check if it has re-encoded to UTF-8.

How can I make my script handle [ or ] in file name well?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

谎言月老 2025-01-17 05:28:01

tl;dr

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

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

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

注意:此答案类似地适用于所有同时具有这两个功能的 cmdlet -Path
-LiteralPath 参数
,例如 设置内容Out-File设置位置


至于您尝试过的

$content = Get-Content $i.Fullname

实际上与

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

传递给Get-Content的(第一个)位置参数 相同隐式绑定到
-Path 参数

-Path 参数接受 通配符表达式允许按模式匹配路径;除了支持 *(任何字符运行)和 ?(正好 1 个字符)之外,还支持 [...]通配符模式表示字符集或范围(例如[12][0-9]< /代码>)。

因此,包含[...](例如foo[10].txt)的实际路径不会被识别为这样,因为[10] 被解释为与单个字符匹配的字符集,该字符是10 ;即 foo[10].txt 将匹配 foo0.txtfoo1.txt,但不匹配字面名为 foo[ 的文件10].txt

当(隐式)使用-Path时,可以转义[] 实例 应逐字解释,即通过反引号 (`),但请注意,这可能会变得棘手 在涉及引用和/或变量引用时得到正确的结果。

如果您知道路径是文字路径,那么最好养成使用 -LiteralPath 的习惯(在 PowerShell (Core) 7 您可以缩短为 -lp )。

但是,如果您的路径包含文字 [] 并且您需要通配符匹配,则必须使用 `-escaping - 请参阅此答案

tl;dr

Indeed, use of the -LiteralPath parameter is the best solution (in PowerShell (Core) v6+, you can shorten to -lp):

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

-LiteralPath ensures that $i.Fullname is taken verbatim (literally); that is, [ and ] in the path are interpreted as themselves rather than having special meaning, as they would have as a -Path argument, due to being interpreted as a wildcard expression - note that -Path is positionally implied if you only pass a value (a string) as the first argument, as you did (Get-Content $i.FullName)

Note: This answer analogously applies to all cmdlets that have both -Path and
-LiteralPath parameters
, such as Set-Content, Out-File, and Set-Location.


As for what you tried:

$content = Get-Content $i.Fullname

is effectively the same as:

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

That is, the (first) positional argument passed to Get-Content is implicitly bound to the
-Path parameter
.

The -Path parameter accepts wildcard expressions to allow matching paths by patterns; in addition to support for * (any run of characters) and ? (exactly 1 character), [...] inside a wildcard pattern denotes a character set or range (e.g., [12] or [0-9]).

Therefore an actual path that contains [...], e.g., foo[10].txt, is not recognized as such, because the [10] is interpreted as a character set matching a single character that is either 1 or 0; that is foo[10].txt would match foo0.txt and foo1.txt, but not a file literally named foo[10].txt.

When (implicitly) using -Path, it is possible to escape [ and ] instances that should be interpreted verbatim, namely via the backtick (`), but note that this can get tricky to get right when quoting and/or variable references are involved.

If you know a path to be a literal path, it is best to form a habit of using -LiteralPath (which in PowerShell (Core) 7 you can shorten to -lp).

However, if your path contains literal [ and ] and you also need wildcard matching, you must use `-escaping - see this answer.

苍风燃霜 2025-01-17 05:28:01

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

选择性错误处理(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:

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 = @()
    }

在带有括号的路径上阻塞。

创建硬链接或符号链接

一个小问题和一个主要警告:

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”相同。

There are at least two situations where the solution's good advice doesn't hold, unfortunately.

Selective error handling (PS: only an issue in legacy Windows PowerShell)

Get-Content -LiteralPath "nobox[]" gives an error message and exception type as if wildcards are involved:

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

whereas without the brackets, we get:

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

Therefore, to silently deal with an optional file, but not bluntly suppress every exception, something like:

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

chokes on paths with brackets.

Creating a hard or symbolic link

A minor and a major caveat:

  • The Path parameter, the name of the new item, "works like the LiteralPath parameter of other cmdlets", says the documentation of New-Item clearly, and that seems true and makes sense. Though I wish we could clarify that by writing -LiteralPath.
  • The Value parameter, the target of the link (also known as Target secretly in v5 and openly later), does not accept wildcard characters according to the same documentation, but that's a lie. The command:
New-Item -ItemType "HardLink" -Path "whatever" -Target "*"

makes Powershell squeal "Cannot set the location because path '*' resolved to multiple containers.".

So you always need the escapes for the target. If you have a file named "f[]", then this will display an error:

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

and this will create a link:

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

Same for ItemType "SymbolicLink".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文