Contents of this directory is archived and no longer updated.

Из-за сильной занятости на работе в последнее время не получается уделять время блогу. Но сегодня выкроил немного времени и решил рассказать одну, на мой взгляд, интересную тему, которую каждый решает по-своему и не всегда очень удобным способом (о чём свидетельствует книга по PowerShell и ньюсгруппы). И на это есть весомые причины. А так же расскажу о весьма странном поведении ряда командлетов (на примере Get-Acl) при работе с файлами.

Давайте рассмотрим вопрос с начала и выйдем на нашу проблему. При поиске группы файлов PowerShell, как и многие другие командные оболочки, позволяет создавать маску поиска, как "*", "?". Безусловно с ними проблем быть не может, т.к. эти знаки в именах файлов встречаться не могут. Но если в имени файла используется символ открывающейся или закрывающейся квадратной скобки - "[" и "]", соответственно. Данные символы допустимы для именования файлов, но интерпретатор PowerShell их воспринимает как мета-символы подстановочного шаблона/маски, например:

[a-z]* - все объекты, которые начинаются с букв латинского алфавита
*[0-9] - все объекты, которые заканчиваются любой цифрой и т.д.

При этом встаёт вопрос, как сказать интерпретатору, что квадратная скобка в данном случае является литералом, а не мета-символ? Для начала я в папке создал 3 файла, 2 из которых содержат квадратные скобки:

[Folder] dir


    Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         02.12.08.      0:47         11 text.txt
-a---         02.12.08.      0:47        296 text[0].txt
-a---         02.12.08.      0:48        769 text[text.txt


[Folder]

Давайте просмотрим содержимое файла text[text.txt:

[Folder] Get-Content text[text.txt
Get-Content : Cannot retrieve the dynamic parameters for the cmdlet. The specified wildcard pattern is not valid: text[
text.txt
At line:1 char:12
+ Get-Content  <<<< text[text.txt
[Folder]

занятно, не правда ли? Здесь мы столкнулись с проблемой, что интерпретатор PowerShell воспринял скобку в имени файла за мета-символ. Бороться с этим можно различными методами. Как вариант - использовать параметр -LiteralPath, что будет говорить интерпретатору о буквальном чтении пути. Проверим:

[Folder] Get-Content -LiteralPath text[text.txt
Quest, Quest Software, the Quest Software logo, Aelita, Benchmark Factory, Big Brother,
DataFactory, DeployDirector, ERDisk, Fastlane, Final, Foglight, Funnel Web, I/Watch,
Imceda, InLook, InTrust, IT Dad, JClass, JProbe, LeccoTech, LiveReorg, NBSpool, NetBase,
PerformaSure, PL/Vision, Quest Central, RAPS, SharePlex, Sitraka, SmartAlarm, Speed
Change Manager, Speed Coefficient, Spotlight, SQL Firewall, SQL Impact, SQL LiteSpeed,
SQL Navigator, SQLab, SQLab Tuner, SQLab Xpert, SQLGuardian, SQLProtector, SQL
Watch, Stat, Stat!, Toad, T.O.A.D., Tag and Follow, Vintela, Virtual DBA, and XRT are
trademarks and registered trademarks of Quest Software, Inc. Other trademarks and
registered trademarks used in this guide are property of their respective owners.
[Folder]

Как видите, мы получили содержимое файла (там отрывок из дисклаймера Quest Software). Однако, параметром -LiteralPath обладают далеко не все командлеты. Например, Get-Acl. У него нету такого параметра, поэтому получить ACL список такого файла будет очень проблематично. Посмотрим пример:

[Folder] Get-Item -LiteralPath text[text.txt


    Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         02.12.08.      0:59        769 text[text.txt


[Folder] Get-Item -LiteralPath text[text.txt | Get-Acl
Get-Acl : The specified wildcard pattern is not valid: text[text.txt
At line:1 char:45
+ Get-Item -LiteralPath text[text.txt | Get-Acl <<<<

Get-Item спокойно работает с LiteralPath, но при передаче его по конвейеру в командлет Get-Acl мы получаем ошибку. Хотя, по идее это должно было сработать. Кстати говоря, это первый обнаруженный недостаток. Вместо LiteralPath можно использовать 4 backtick (text````[text.txt), либо проще (что мне показалось более очевидным) - поместить литеральную скобку в подстановочный шаблон:

[Folder] Get-Item text````[text.txt


    Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         02.12.08.      0:59        769 text[text.txt


[Folder] Get-Item text[[]text.txt


    Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         02.12.08.      0:59        769 text[text.txt


[Folder]

В первом примере я использовал рецепт из книги Windows PowerShell In Action (страница 310), второй я додумал сам. Однако, оба эти приёма заменяют параметр -LiteralPath, но так же не решают проблемы командлетов, как Get-Acl. Если передавать эти объекты в Gat-Acl, то всё равно мы будем получать ошибки. Как вариант решения - использование промежуточной функции-фильтра (это рецепт решения Kiron из ньюсгрупп):

filter Escape-Bracket {
    $_.psPath = $_.psPath -replace '\[|]','`$
    $_
}

При использовании фильтра наш литеральный символ заменяем подстановочным знаком "?", пропускаем через фильтр и подаём на командлет Get-Acl:

[Folder] Get-Item text?text.txt | Escape-Bracket | Get-Acl | ft -a


    Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder


Path          Owner        Access
----          -----        ------
text[text.txt Thor\vPodans Everyone Allow  FullControl...


[Folder]

Так оно работает. В качестве первого предположения я подумал на тип объектов:

[Folder] (Get-Item 'text[[]text.txt').GetType().fullname
System.IO.FileInfo
[Folder] (Get-Acl text.txt).GetType().fullname
System.Security.AccessControl.FileSecurity
[Folder]

Что при Get-Item мы получаем тип объектов FileInfo, который не содержит ACL, поэтому данный командлет передаёт в конвейер свойство PsPath, который уже Get-Acl использует для повторного извлечения уже ACL объекта.

Однако, я обратил внимание, что при использовании UNC путей вместо локальных промежуточная функция не требуется и вполне работает подстановочная маска заключённая в квадратные скобки:

[Folder] Get-Acl 'text[[]text.txt'
Get-Acl : The specified wildcard pattern is not valid: text[text.txt
At line:1 char:8
+ Get-Acl  <<<< 'text[[]text.txt'
[Folder] Get-Acl 'text[[]0[]].txt'
Get-Acl : Cannot find path 'D:\Users\_Shared Documents\Folder\text[0].txt' because it does not exist.
At line:1 char:8
+ Get-Acl  <<<< 'text[[]0[]].txt'
[Folder] Get-Acl '\\Thor\_Shared Documents\Folder\text[[]text.txt' | ft -a


    Directory: Microsoft.PowerShell.Core\FileSystem::\\Thor\_Shared Documents\Folder


Path          Owner        Access
----          -----        ------
text[text.txt Thor\vPodans Everyone Allow  FullControl...


[Folder] Get-Acl '\\Thor\_Shared Documents\Folder\text[[]0[]].txt' | ft -a


    Directory: Microsoft.PowerShell.Core\FileSystem::\\Thor\_Shared Documents\Folder


Path        Owner        Access
----        -----        ------
text[0].txt Thor\vPodans Everyone Allow  FullControl...


[Folder]

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

[Folder] (Get-Item '\\thor\_Shared Documents\Folder\text[[]text.txt').gettype().fullname
System.IO.FileInfo
[Folder] (Get-Acl '\\thor\_Shared Documents\Folder\text[[]text.txt').gettype().fullname
System.Security.AccessControl.FileSecurity
[Folder]

Следовательно, моё предположение было неверным. Вполне возможно, что это баг командлетов, поэтому я на всякий случай запостил проблему на Connect'е:

 https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=386138&SiteID=99

Желающие могут перейти по ссылке и подтвердить данную ошибку. Всем спасибо за участие :)


Share this article:

Comments:

AndruXO

Можно воспользоваться вот такой конструкцией: $item = get-item -literalpath '[whatever].txt' $item.GetAccessControl() отсюда: http://social.technet.microsoft.com/Forums/en/ITCG/thread/88fae82e-d7e4-49a6-87ee-746e35b3e5f0

Comments are closed.