复制文件名中带方括号[]的文件并使用*通配符

问题描述 投票:11回答:8

我在Windows 7上使用PowerShell,并编写脚本将一堆文件从一个文件夹结构复制到另一个文件夹结构。有点像编译。 PowerShell Copy-Item cmdlet认为方括号[]是某种通配符,并且由于某种原因我无法逃脱它们。

我不能使用-LiteralPath,因为我想使用星号*通配符,因为文件名有一个日期作为文件名的一部分,日期会改变。日期用作版本号。

This post很有帮助,但是方括号中没有任何数量的刻度(每个括号2x或4x)。

我没有收到错误; PowerShell的行为与我输入错误的文件名时的行为相同。

这是我正在研究的具体方法:

#to Fusion Server
Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\"[RoomView] Versions explained*.pdf" -Destination $FSG\$containerFolder\$rootFolder\"Fusion Server"\

这就是整个事情:

# Compiles the Fusion packet for distribution

###############################
###########Variables###########
###############################

#folder structure
$FSG = "F:\FSG"
$containerFolder = "Packet.Fusion for IT and AV Professionals"
$rootFolder      = "Fusion for IT and AV pros $(Get-Date -format “MM-dd-yyyy”)"
$subRoot1        = "Fusion Server"
$subRoot2        = "Scheduling Enhancement and Panels"
$subRoot2sub1    = "Scheduling Panels"
$subRoot3        = "SQL Server"

#source folders
$HW      = "0.Hardware"
$3SMDoc  = "0.Hardware\TPMC-3SM.Documentation"
$4SMDoc  = "0.Hardware\TPMC-4SM.Documentation"
$4SMDDoc = "0.Hardware\TPMC-4SM-FD.Documentation"
$730Doc  = "0.Hardware\TSW-730.Documentation"
$730OLH  = "0.Hardware\TSW-730.OLH"
$CENRVS  = "0.Hardware\CEN-RVS.Notes"

$ProjMgmt = "0.Project Management"

$SW            = "0.Software"
$RVLicensing   = "0.Software\0.RoomView.License"
$RVNotes       = "0.Software\0.RoomView.Notes"
$SQLLicensing  = "0.Software\database.SQL.Licensing"
$SQLNotes      = "0.Software\database.SQL.Notes"
$FRVMarketing  = "0.Software\Fusion RV.Marketing"
$FRVNetworking = "0.Software\Fusion RV.Networking"
$FRVNotes      = "0.Software\Fusion RV.Notes"


###############################
#create the directory structure
###############################

md -Path $FSG\$containerFolder -Name $rootFolder

cd $FSG\$containerFolder\$rootFolder
md "eControl and xPanels"
md "Fusion Server" #$subRoot1
md "Getting Started as a User"
md "Project Management"
md "RoomView Connected Displays"
md "Scheduling Enhancement and Panels" #$subRoot2
md "SQL Server" #$subRoot3

cd $FSG\$containerFolder\$rootFolder\$subRoot1
md "CEN-RVS"
md "Licenseing Information"
md "Networking"
md "Official Documentation"
md "Prerequisites, including powerShell script"
md "Product Info"
md "Requirements"
md "Tech Info"
md "Windows Authentication to Fusion RV"

cd $FSG\$containerFolder\$rootFolder\$subRoot2
md "Outlook Add-in"
md "Scheduling Panels" #$subRoot2sub1

cd $FSG\$containerFolder\$rootFolder\$subRoot2\$subRoot2sub1
md "TPMC-3SM"
md "TPMC-4SM"
md "TPMC-4SM-FD"
md "TSW-730"

cd $FSG\$containerFolder\$rootFolder\$subRoot3
md "Multi-database model only"
md "SQL Licensing"

cd $FSG\$containerFolder
#reset current folder


###############################
#copy the files
###############################

#Copy-Item -Path C:\fso\20110314.log -Destination c:\fsox\mylog.log

#To the root
Copy-item -Path $FSG\$ProjMgmt\starter\"Fusion Support Group Contact info*.pdf" -Destination $FSG\$containerFolder\$rootFolder\
Copy-item -Path $FSG\$containerFolder\"Fusion for IT and AV professionals release notes.txt" -Destination $FSG\$containerFolder\$rootFolder\

#to eControl and xPanels
Copy-item -Path $FSG\$SW\xpanel.Notes\starter\*.* -Destination $FSG\$containerFolder\$rootFolder\"eControl and xPanels"\

#to Fusion Server
Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\"[RoomView] Versions explained*.pdf" -Destination $FSG\$containerFolder\$rootFolder\"Fusion Server"\

我可以做什么来逃避方括号并仍然使用Copy-Item cmdlet的通配符文件名部分?

powershell copy square-bracket
8个回答
18
投票

感谢@ mklement0突出显示这种不一致的真正原因是因为bug currently in PowerShell1。此错误导致通配符转义,以及使用默认-Path参数的反引号,其行为与其他参数不同,例如-Include参数。


(在修复bug之前),在这种情况下,使用单引号的双反引号来转义括号。使用双引号字符串时,也可以使用四重反引号。

所以固定的代码行是:

Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\'``[RoomView``] Versions explained*.pdf' -Destination $FSG\$containerFolder\$rootFolder\'Fusion Server'\

文件路径和有线字符等的另一个好资源是阅读这篇文章:Taking Things (Like File Paths) Literally


编辑

要扩展@mklement0's excellent answercomments,以及其他答案如下:

为了更好地理解为什么在这种情况下我们需要单引号和两个后退? (并突出显示bug和不一致)让我们通过一些例子来演示正在发生的事情:

Get-Item和相关的cmdlet(Get-ChildItemCopy-Item等)处理-Path参数时,处理转义通配符的组合并同时应用普通的通配符!

TLDR:我们需要组合单引号和双引号的根本原因是底层PowerShell提供程序如何解析字符串中的通配符。它似乎为转义字符解析一次,并且第二次解析通配符。

让我们通过一些例子来证明这个奇怪的结果:

首先,让我们创建两个文件来测试,调用File[1]a.txtFile[1]b.txt

"MyFile" | Set-Content '.\File`[1`]a.txt'
"MyFriend" | Set-Content '.\File`[1`]b.txt'

我们将尝试不同的方式来获取文件。我们知道方括号[ ]wildcards,所以我们需要用backtick character逃脱它们。

我们将尝试明确地获取一个文件。使用双引号字符串,我们得到:

PS C:\> Get-Item "File[1]a.txt"
PS C:\> Get-Item "File`[1`]a.txt"
PS C:\> Get-Item "File``[1``]a.txt"

    Directory: C:\  

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2019-09-06   5:42 PM              8 File[1]a.txt

对于双引号字符串,正如预期的那样,我们可以看到我们需要两个反引号来使它工作。

对于单引号文字字符串,事情更容易:

PS C:\> Get-Item 'File[1]a.txt'
PS C:\> Get-Item 'File`[1`]a.txt'

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2019-09-06   5:42 PM              8 File[1]a.txt

PS C:\> Get-Item 'File``[1``]a.txt'

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2019-09-06   5:42 PM              8 File[1]a.txt

对于单引号字符串,只需要一个反引号来检索文件,但两个反引号也可以工作。

现在,我们想要使用通配符检索这两个文件。让我们从双引号开始:

PS C:\> Get-Item "File[1]*.txt"
PS C:\> Get-Item "File`[1`]*.txt"
PS C:\> Get-Item "File``[1``]*.txt"
PS C:\> Get-Item "File```[1```]*.txt"
PS C:\> Get-Item "File````[1````]*.txt"

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2019-09-06   5:42 PM              8 File[1]a.txt
-a----       2019-09-06   5:49 PM             10 File[1]b.txt

使用双引号和通配符,我们需要四组后退。这是因为看起来我们必须转义反引号转义字符(对于双引号我们需要两个反引号),以便在我们评估星形通配符时让转义字符转义括号。当我们使用单引号时,我们也可以更容易地看到这一点:

PS C:\> Get-Item 'File[1]*.txt'
PS C:\> Get-Item 'File`[1`]*.txt'
PS C:\> Get-Item 'File``[1``]*.txt'

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2019-09-06   5:42 PM              8 File[1]a.txt
-a----       2019-09-06   5:49 PM             10 File[1]b.txt

使用单引号,我们可以更容易地看到它。当我们有通配符时,我们需要两组反引号。一个用于逃避括号,另一个用于在评估通配符时逃避反引号的反引号。


编辑

作为@mklement0 mentions-Path参数的这种行为是不一致的,并且行为与-Include参数不同,在1参数中只需要一个反引号就可以正确地转义括号。这可能在PowerShell的更高版本中“修复”。


copy-item '.\file`[test`].txt' 自Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.3起


3
投票

Powershell自动选项卡完成文件名的方式通常是最好的方法,

例:

$string -replace '\[','' -replace '\]',''
>Servername: QA01

3
投票

在PowerShell v 2.0及更高版本中,要使用的转义字符是反斜杠。例如,如果我们要从此字符串“[Servername:QA01]”中删除括号,这是我们从System Center Orchestrator中的Exchange Admin PowerShell cmdlet活动获得的输出,我们使用以下逻辑:

Reference here

这很奇怪。请注意,您必须使用单引号(通常在PowerShell中暗示'精确地将其评估为写入',因此这是非常奇怪的语法)。

不要因为没有自己解决这个问题而感到难过,这是非常奇怪的语法。


1
投票

显然,方括号需要双反推才能逃脱,这是不寻常的。 wildcard expression

你确定不行吗?我已经看过几次了。

编辑:是的,它有效,您使用双引号而不是反引号。

双引号位于撇号字符上方,在Enter键旁边。 Backtick正好位于Escape键下方,与代字号共享键,〜。


1
投票

概述和一些背景信息:

  • 为了有效地转义您希望逐字解释为`的字符,它必须是`-escaped,如目标cmdlet(其底层的PowerShell驱动程序提供程序)所示。
  • 确保可能变得棘手,因为"..."(反引号)也被用作双引号字符串(-LiteralPath)中的转义字符和不带引号的命令参数(大多数情况下表现得像双引号字符串)。

注意:问题中的场景不允许使用-LiteralPath,但是如果您知道路径是具体的文字路径,则使用-lp(可以缩短为 PowerShell Core中的this answer是最佳选择 - 请参阅-Path

将参数传递给PowerShell驱动程序提供程序相关cmdlet(Get-ChildItemCopy-ItemGet-Content,...)的支持通配符的[参数时,您希望]'file`[1`].txt'被逐字处理而不是字符集/范围表达式:

  • 字符串文字表示: ` '...' chars。保存在"file``[1``].txt"中,因此目标cmdlet按预期方式查看它们。 `` "...",即在`内需要加倍,以便在结果字符串中保留单个`(第一个`是(双引号)字符串内部转义字符,第二个file``[1``].txt是它逃脱的字符,要通过)。 "..." 对于不带引号的命令参数同样如此(大多数情况下)就像this GitHub issue一样 警告:由于一个错误 - 请参阅? - 混合(未转义)*[与逃逸的]``要求后者双重逃脱(使用file[1].txt,如目标cmdlet /提供商所见): 如果你想将文字文件名[与字面上匹配]*的通配符匹配,同时还包含特殊字符'file`[1`]*'(以匹配任何字符集),而不是预期的'file``[1``]*',你将不得不使用"file````[1````]*"(sic);使用双引号或未转义的参数,你必须有效地使用四重反引号:file````[1````]* / this answer - 请参阅-like了解更多信息。 请注意,使用'a[b' -like 'a`[*'运算符的通配符不会受到影响: $true是 - 正确 - 'a[b' -like 'a``[*', 而-Include - 理所当然 - 抱怨无效的模式。 同样,参数-Exclude-Filter不受影响。 [...]以不同的规则开始:[作为一个结构根本不受支持,]this answer字符。总是被视为文字(再次,请参阅$literalName = 'file[1].txt' $escapedName = [WildcardPattern]::Escape($literalName) # -> 'file`[1`].txt' )。
  • 要通过编程以编程方式转义路径字符串,请使用: cmd /c dir *]* /b | foreach { Move-Item -LiteralPath $_ -Destination <destination path> }


0
投票

一种选择是使用遗留目录获取文件名,这将允许您使用*通配符,但不会尝试“blob”方括号。然后使用-literalpath将该列表提供给move-item

 copy-item a?h-j? foo 

0
投票

假设没有其他匹配,你可以使用?而不是括号。一个名为“a [h-j]”的文件,复制到目录“foo”:

 Copy-Item  $file.fullname.replace("[", "``[").replace("]", "``]") $DestDir

0
投票

我用这个:

'

-1
投票

`#to Fusion Server Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\'``[RoomView``] Versions explained*.pdf' -Destination $FSG\$containerFolder\$rootFolder\"Fusion Server"\ 之间有区别:

  • 第一个是单引号,即“键”上的非移位字符。
  • 第二个是我认为我正在使用的反击,但实际上并非如此。它是〜键上的非移位字符。

这有效:

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