19

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

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

这篇文章很有帮助,但没有多少刻度(每个括号 2 倍或 4 倍)逃脱方括号。

我没有收到错误;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-Itemcmdlet 的通配符文件名部分?

4

9 回答 9

32

在这种情况下,您必须使用带单引号的双反引号来转义括号。使用双引号字符串时,也可以使用四重反引号。

所以固定的代码行是:

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

关于文件路径和有线字符等的另一个好资源是阅读这篇文章:从字面上理解事物(如文件路径)


编辑

感谢 @mklement0 强调这种不一致的真正原因是由于PowerShell 1中当前的一个错误。此错误导致通配符转义,以及带有默认参数的反引号与其他参数(例如和参数-Path)的行为不同。-Include-Filter

要扩展@mklement0 的出色答案评论和以下其他答案:

为了更好地理解为什么在这种情况下我们需要单引号 两个反引号;(强调错误和不一致)让我们通过一些示例来演示发生了什么:

Get-Item, 和相关的 cmdlet ( Get-ChildItem,Copy-Item等),在处理转义通配符非转义通配符-Path的组合时以不同方式处理参数*同时***!

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

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

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

"MyFile" | Set-Content '.\File`[1`]a.txt'
"MyFriend" | Set-Content '.\File`[1`]b.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]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]*.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"
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 所提到的,这种与-Path参数的行为不一致,并且与参数的行为不同-Include,其中只需要一个反引号即可正确转义括号。这可能会在更高版本的 PowerShell 中“修复”。


1从 Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.3 开始

于 2014-01-09T00:49:24.097 回答
7

概述一些背景信息

  • 为了有效地转义您希望逐字解释为通配符表达式的一部分的字符,它必须按照`目标 cmdlet (其底层 PowerShell 驱动器提供程序)所见的方式进行转义。

  • 确保这可能会变得棘手,因为`(反引号)用作双引号字符串( "...") 和未引用的命令参数(在大多数情况下表现得像双引号字符串)中的转义字符。

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

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

  • 字符串文字表示

    • 'file`[1`].txt'

      • `字符。保持原样 inside '...',因此目标 cmdlet 可以按预期看到它们。
    • "file``[1``].txt"

      • ``,即内部需要加倍"...",以便在结果字符串中保留一个(第一个 (双引号)字符串内部转义字符,第二个是它转义的字符,要通过)。` ``
    • file``[1``].txt

      • 未引用的命令参数同上,它(在大多数情况下)表现得像"..."
    • 警告由于一个错误- 请参阅此 GitHub 问题-混合(未转义)?*转义[,并]要求后者双重转义(``如目标 cmdlet/提供者所见)

      • 如果您想将文字文件名file[1].txt与匹配的通配符模式匹配[并且] 字面上还包含特殊字符(以匹配任何字符运行),而不是预期的您将不得不使用(sic); 使用双引号或未转义的参数,您必须有效地使用四倍反引号:/ - 有关更多信息,请参阅此答案*'file`[1`]*''file``[1``]*'"file````[1````]*"file````[1````]*

      • 请注意,与运算符直接使用通配符不受影响-like

        • 'a[b' -like 'a`[*'是 - 正确 - $true,
        • 'a[b' -like 'a``[*'- 理所当然 - 抱怨一个无效的模式
      • 同样参数-Include-Exclude不受影响。

      • -Filter开始时遵循不同的规则[...]:根本不支持构造,以及[]字符。总是认为是文字(再次,请参阅此答案)。

  • 要通过变量以编程方式转义路径字符串,请使用:

      $literalName = 'file[1].txt'
      $escapedName = [WildcardPattern]::Escape($literalName) # -> 'file`[1`].txt'
    

于 2019-09-03T20:15:04.203 回答
5

我用这个:

 Copy-Item  $file.fullname.replace("[", "``[").replace("]", "``]") $DestDir
于 2019-02-19T13:53:00.203 回答
3

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

例子:

copy-item '.\file`[test`].txt'
于 2014-01-09T01:13:41.923 回答
2

显然,方括号需要双反引号才能转义,这是不寻常的。参考这里

你确定这行不通?我看过几次提到它。

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

双引号位于撇号字符上方,紧邻 Enter 键。反引号就在 Escape 键的下方,与波浪号共享键,~。

于 2014-01-08T22:57:49.223 回答
2

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

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

这很奇怪。看,您必须使用单引号(这通常在 PowerShell 中暗示“按书面形式精确评估”,因此这是非常奇怪的语法)。

不要因为自己没有弄清楚这一点而感到难过,这是非常奇怪的语法。

于 2015-03-25T15:11:57.367 回答
0

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

 cmd /c dir *]* /b |
  foreach { Move-Item -LiteralPath $_ -Destination <destination path>  }
于 2014-01-08T23:06:23.047 回答
0

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

 copy-item a?h-j? foo 
于 2018-08-22T16:43:53.107 回答
-1

'和之间有区别`

  • 第一个是单引号,它是键上的非移位字符"
  • 第二个是我认为我正在使用但实际上没有使用的反引号。它是键上的非移位字符~

这有效:

# to Fusion Server
Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\'``[RoomView``] Versions explained*.pdf' -Destination $FSG\$containerFolder\$rootFolder\"Fusion Server"\
于 2014-01-10T21:22:25.397 回答