如何让 PowerShell 很好地处理文件名中的 [ 或 ] ?
我从 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。
如何让我的脚本很好地处理文件名中的 [
或 ]
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
tl;dr
事实上,使用
-LiteralPath
参数是最好的解决方案(在 PowerShell (Core) v6+ 中,您可以缩短为 >-lp
):-LiteralPath
确保$i.Fullname
逐字记录 >(字面意思);也就是说,路径中的[
和]
被解释为它们本身,而不是像它们那样具有特殊含义由于被解释为 通配符表达式 - 请注意,如果您仅通过,则-Path
是位置隐含的 值(字符串)作为第一个参数,就像您所做的那样 (Get-Content $i.FullName
)注意:此答案类似地适用于所有同时具有这两个功能的 cmdlet
-Path
和-LiteralPath
参数,例如设置内容
,Out-File
和设置位置
。至于您尝试过的:
实际上与
:传递给
Get-Content
的(第一个)位置参数 相同隐式绑定到-Path
参数。-Path
参数接受 通配符表达式允许按模式匹配路径;除了支持*
(任何字符运行)和?
(正好 1 个字符)之外,还支持[...]
通配符模式表示字符集或范围(例如[12]
或[0-9]< /代码>)。
因此,包含
[...]
(例如foo[10].txt
)的实际路径不会被识别为这样,因为[10]
被解释为与单个字符匹配的字符集,该字符是1
或0
;即foo[10].txt
将匹配foo0.txt
和foo1.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
):-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 asSet-Content
,Out-File
, andSet-Location
.As for what you tried:
is effectively the same as:
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 either1
or0
; that isfoo[10].txt
would matchfoo0.txt
andfoo1.txt
, but not a file literally namedfoo[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.不幸的是,至少在两种情况下,该解决方案的好建议并不成立。
选择性错误处理(PS:仅旧版 Windows PowerShell 中的一个问题)
Get-Content -LiteralPath "nobox[]"
给出错误消息和异常类型,就好像涉及通配符一样:而如果没有括号,我们get:
因此,要默默地处理可选文件,但不要直截了当地抑制每个异常,例如:
在带有括号的路径上阻塞。
创建硬链接或符号链接
一个小问题和一个主要警告:
-LiteralPath
来澄清这一点。Value
参数,链接的目标(在 v5 中秘密地称为Target
,后来公开),根据相同的文档不接受通配符,但这是一个说谎。该命令:使 Powershell 尖叫“无法设置位置,因为路径 '*' 解析为多个容器。”。
所以你总是需要目标的逃生。如果您有一个名为“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:whereas without the brackets, we get:
Therefore, to silently deal with an optional file, but not bluntly suppress every exception, something like:
chokes on paths with brackets.
Creating a hard or symbolic link
A minor and a major caveat:
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
.Value
parameter, the target of the link (also known asTarget
secretly in v5 and openly later), does not accept wildcard characters according to the same documentation, but that's a lie. The command: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:
and this will create a link:
Same for ItemType "SymbolicLink".