Скопируйте файл с квадратными скобками [] в имени файла и используйте * wildcard

Я использую PowerShell в Windows 7 и пишу script, чтобы скопировать кучу файлов из одной структуры папок в другую. Это похоже на компиляцию. Командлет PowerShell Copy-Item считает, что квадратные скобки, [], являются подстановочными знаками какого-либо типа, и я почему-то не могу их избежать.

Я не могу использовать -LiteralPath, потому что я хочу использовать подстановочный знак *, поскольку имя файла имеет дату как часть имени файла, а дата изменяется. Дата используется как номер версии.

Этот пост был полезен, но количество тиков (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?

Ответ 1

Существует разница между ' и ':

  • Первая - это одиночная кавычка, которая не является символом смены на клавише ".
  • Вторым является обратный удар, который я думал, что использовал, но фактически не был. Это символ без смещения на клавише ~.

Это работает:

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

Ответ 2

В этой ситуации вы должны использовать двойные обратные кавычки с одинарными кавычками, чтобы избежать скобок. Вы также можете использовать четверные обратные метки при использовании строк в двойных кавычках.

Таким образом, фиксированная строка кода:

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

Еще один хороший источник информации о путях к файлам, проводных символах и т.д. - прочитать эту статью: Взятие вещей (например, путей к файлам) буквально


EDIT

Спасибо @mklement0 за то, что он подчеркнул, что истинная причина этого несоответствие из-за ошибки в настоящее время в PowerShell1. Эта ошибка приводит к экранированию подстановочных знаков, а также кавычек с параметром -Path по умолчанию вести себя иначе, чем другие параметры, например параметры -Include и -Filter.

To expand on @mklement0 excellent answer, and comments, and other answers below:

Чтобы лучше понять, почему нам нужны одинарные кавычки и дваобратных тика в этой ситуации; (и, чтобы выделить ошибку bug и несоответствия), давайте рассмотрим несколько примеров, чтобы продемонстрировать, что происходит:

Get-Item и связанные с ними командлеты (Get-ChildItem, Copy-Item и т.д.) По-разному обрабатывают параметр -Path, когда имеют дело с комбинацией экранированных символов , экранированных символов и ,экранированных подстановочных знаков * одновременно ***!

TLDR. Основная причина, по которой нам нужна комбинация одинарных кавычек и двойных обратных кавычек, заключается в том, что базовый поставщик PowerShell анализирует строку параметра -Path для подстановочных знаков. Похоже, он анализируется один раз для escape-символов и второй раз для оценки подстановочного знака.

Давайте рассмотрим несколько примеров, чтобы продемонстрировать этот странный результат:

Сначала давайте создадим два файла для тестирования с именами File[1]a.txt и File[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'

    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

С двойными кавычками немного более детально оценить с помощью подстановочного знака. В этом случае нам потребуется четыре набора обратных тиков. Для двойных кавычек нам нужны два обратных кавычка, чтобы выйти за скобки, и еще два обратных кавычки, чтобы избежать экранирующих символов, когда дело доходит до оценки звездного подстановочного знака.


EDIT

Как упоминается в @mklement0, это поведение с параметром -Path противоречиво и ведет себя иначе, чем параметр -Include, где для правильного выхода из скобок требуется только один обратный удар. Это может быть "исправлено" в более поздней версии PowerShell.


1 As of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.3

Ответ 3

Способ, которым PowerShell автоматически добавляет имя файла, обычно является лучшим способом,

Пример:

copy-item '.\file`[test`].txt'

Ответ 4

В PowerShell версии 2.0 и выше escape-символ используется обратная косая черта. Например, если мы хотим удалить скобки из этой строки "[Servername: QA01]", который является видом вывода, который мы получаем из командлета Exchange PowerShell в System Center Orchestrator, мы используем следующую логику:

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

Это довольно странно. См., Вы должны использовать одну кавычку (которая обычно подразумевает в PowerShell "оценивать это точно как написано", поэтому это очень странный синтаксис).

Не чувствуйте себя плохо, потому что не понимаете это самостоятельно, это очень странный синтаксис.

Ответ 5

По-видимому, квадратные скобки нуждаются в двойных обратных шагах, чтобы избежать, что необычно. Ссылка здесь.

Ты уверен, что это не сработает? Я видел, как это упоминалось несколько раз.

Изменить: Да, это работает, вы использовали двойные кавычки вместо обратных ссылок.

Двойная цитата находится над символом апострофа, рядом с клавишей Enter. Backtick находится прямо под клавишей Escape, деля ключ с тильдой, ~.

Ответ 6

Я использую это:

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

Ответ 7

обзор и некоторая справочная информация:

  • Чтобы эффективно экранировать символ, который вы хотите интерпретировать дословно как часть подстановочного выражения, он должен быть ' -escaped, как его видит целевой командлет (его базовый диск PowerShell). провайдер).

  • Это может быть сложно, потому что ' (backtick) также используется в качестве escape-символа в строках с двойными кавычками ("...") и аргументах команды без кавычек (которые по большей части ведут себя как double- строки в кавычках).

Примечание. Сценарий, о котором идет речь, не позволяет использовать -LiteralPath, но в тех случаях, когда вы знаете, что путь является конкретным, буквальным путем, используйте -LiteralPath (который можно закорочить на
-lp в PowerShell Core) - лучший выбор
- см. этот ответ.

При передаче аргумента параметру -Path, поддерживающему с подстановочными знаками, командлета, связанного с поставщиком накопителя PowerShell (Get-ChildItem, Copy-Item, Get-Content,...) и требуется [ и ] следует рассматривать дословно, а не как набор символов/выражение диапазона:

  • Строково-буквенные представления:

    • 'file'[1'].txt'

      • ' Чарс. сохраняются как есть внутри '...', поэтому целевой командлет видит их как положено.
    • "file''[1''].txt"

      • '', т.е. внутри "..." необходимо удвоение, чтобы сохранить один ' в результирующей строке (первый ' является escape-символом (двойные кавычки) внутренней строки, а второй ' является символом, которого он избегает, чтобы пройти через него.
    • file''[1''].txt

      • То же самое для аргументов команды без кавычек, которые (по большей части) действуют как "..."
    • Предупреждение: Из-за ошибки - см. эту проблему в GitHub - смешивание (без выхода) ? или * с экранированием [ и ] требует, чтобы последний был дважды экранирован (с '', как видит целевой командлет/поставщик):

      • Если вы хотите сопоставить буквенное имя файла file[1].txt с шаблоном подстановки, который буквально совпадает с [ и ], а также содержит специальный символ * (для сопоставления с любым набором символов), вместо ожидаемого 'file'[1']*', вы Придется использовать 'file''[1'']*' (sic); с аргументом в двойных кавычках или без экранирования вы должны эффективно использовать четырехкратные обратные запятые: "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'
    

Ответ 8

Один из вариантов заключается в том, чтобы получить имена файлов, используя устаревший каталог, который позволит вам использовать символ подстановки *, но не пытается "покрасить" квадратные скобки. Затем загрузите этот список для перемещения элемента с помощью -literalpath

 cmd /c dir *]* /b |
  foreach { Move-Item -LiteralPath $_ -Destination <destination path>  }

Ответ 9

Предполагая, что больше ничего не подходит, вы можете использовать? вместо скобок. Файл с именем "a [h-j]", копируемый в каталог "foo":

 copy-item a?h-j? foo