Глава 2. Считывание и запись вывода
Содержание
- Глава 2. Считывание и запись вывода
- Технические требования
- Работа с выводом
- Сохранение данных
- Доступные только на чтение переменные и константы
- Область действия переменных
- Шесть потоков
- Перенаправление потоков
- Работа с поставщиком файловой системы
- Работа с поставщиком реестра
- Работа с поставщиком сертификатов
- Создание вашего собственного поставщика
- Альтернативные потоки данных NTFS
Работа с выводом и данными является наиболее общим моментом, который вы будете делать в PowerShell. Начиная с выработки данных для отчётов, вплоть до быстрого просмотра состояния системы, до вставления пачкой значений в некую таблицу базы данных: всё это должно иметь дело с данными и выводом.
Мы рассмотрим в этой главе целый круг вопросов, причём на примере простых рецептов вы узнаете как получить доступ к хранилищам сертификатов, реестру в машине с Windows и к имеющейся файловой системе в любой ОС. Вы также узнаете как надлежащим образом работать с переменными.
В этой главе мы рассмотрим такие рецепты:
-
Работа с выводом
-
Сохранение данных
-
Доступные только для чтения и постоянные переменные
-
Область действия переменных
-
Шесть потоков
-
Переназначение потоков
-
Работа с поставщиком файловой системы
-
Работа с поставщиком реестра
-
Работа с поставщиком сетрификата
-
Создание вашего собственного поставщика
-
Альтернативные потоки NTFS
Применяемый в данной главе код можно найти в GitHub.
Этот очень простой рецепт показывает вам необходимые снасти когда вам требуется собирать обыкновенный вывод из
некой локальной системы. Чтобы получать выходные данные, как правило, мы используем cmdlet с глаголом
Get
. Однако вы обнаружите что прочие cmdlet, которые создаю, изменяют, либо
удаляют элементы, также способны возвращать данные для работы с ними.
Чтобы быть гтовым к началу, вам требуется иметь установленным PowerShell Core. Данный рецепт работает в любой операционной системе, способной работать с PowerShell Core.
Давайте выполним такие шаги:
-
Откройте PowderShell
-
Выполните
Get-UICulture
, а затемGet-Process
. Обратите внимание что получаемый по умолчанию для этих двух cmdlet выглядит похожим на табличный, что демонстрируется ниже:
-
Выполните
Get-TimeZone
, а следомGet-Uptime
. На этот раз назначенное по умолчанию форматирование вывода выглядит перечнем, как показано далее:
-
Теперь исполните
Get-ChildItem -Path *DoesNotExist*
. Для этого cmdlet вы можете и вовсе не получать вывод. -
Наконец, запустите
New-Item -Name file -Value 'Test file'
. Хотя этот cmdlet и не применяет соответствующий глаголGet
, он всё ещё возвращает некий вывод для своей обработки.
Когда вы выполняете некий cmdlet Get
, PowerShell обычно пытается выполнить
выборку данных из какого- то источника. К примеру, это могут быть запущенные в системе процессы, установленный сетевой
адаптер, а также время работы данной текущей системы.
-
Для получения дополнительных сведений относительно форматирования, просмотрите следующую тему справки:
Get-Help about_Format.ps1xml
-
Для получения дополнительных сведений относительно типа системы, просмотрите следующую тему справки:
Get-Help about_Types.ps1xml
Наличие данных в получаемом выводе может удовлетворять неких системных администраторов, однако применяя PowerShell мы бы желали взаимодействовать со своими данными. Данный рецепт покажет вам что та информация, которую вы видите на своём экране может предложить вам намного больше когда вы рассмотрите на объекты в контексте сценариев PowerShell.
Воспользовавшись переменными вы можете сохранять некую ссылку на один или более объектов для последующего доступа к ним. Это не просто удобство при интерактивной работе с командной строкой, но также может означать более быстрое исполнение сценариев, так как ваши данные не извлекаются пока они не требуются.
В этом рецепте мы обнаружим, что наблюдаемый нами результат совсем не то чем он может казаться.
Установите и откройте PowerShell Core в любой из предпочитаемых вами систем и осуществите следующие этапы:
-
Наберите
Get-Help about_Variables
. -
для создания новых переменных выполните три следующих строки:
$timestamp = Get-Date $processes = Get-Process $nothing = $null
-
Запуск вашей переменной просто повторяет вывод:
$timestamp
-
Введите
$timestamp.
, а затем нажмите клавишуTab
в CLI, либоCtrl + Space
в CLI и VS Code. Ознакомьтесь с различными свойствами и методами, которые тут могут применяться. -
Попробуйте вомпользоваться
$timestamp.DayOfWeek
и$timestamp.IsDaylightSavingTime()
. -
Также прекрасно работает доступ к свойствам и методам в списках. Попробуйте выполнить следующее:
$processes.Name $processes.Refresh()
-
Будьте внимательными при использовании не инициализированных переменных, так как результат потенциально может быть разрушительным:
# Значением свойства также может быть и $null $nothing.SomeProperty # Вызовы метода способны вызывать ошибку $nothing.SomeMethod() # Особенной аккуратности требуют cmdlets подобные Get-ChildItem! # Значением пути по умолчанию выступает текущий рабочий каталог Get-ChildItem -Path $nothing -File | Remove-Item -WhatIf
-
Наконец, вам не требуется вовсе создавать некую переменную если вам всего лишь один раз требуется получить доступ к некому свойству или методу:
# Accessing properties and methods while discarding the # original object requires expressions, $( ) $(Get-TimeZone).BaseUtcOffset $(Get-Process).ToString()
PowerShell будет сохранять в ваших переменных либо получаемое значение, либо ссылку на некий объект. Подробнее типы значений и типы ссылок мы изучим в Главе 3, Работа с объектами. На данный момент вам следует запомнить сам термин нотации точки и просто пытаться применять его ко всякой обнаруженной вами переменной.
Применяя нотацию точки можно выполнять доступ к индивидуальным свойствам или методам одного или более объектов, а при помощи выражений вам даже не нужно создавать какую бы то ни было переменную. Вычисляется всё выражение целиком и полученный результат затем применяется для доступа к некому свойству или методу.
В этом рецепте вы обнаружили, что получаемый на экране отформатированный вывод не содержит все возможные свойства и методы. Всегда важно целиком зучать возвращаемые данные, например, при помощи нотации точки, чтобы понять с чем вы имеете дело.
Может так случиться, что вы пожелаете воспользоваться в своём коде доступными только для чтения или постоянными переменными. Данный рецепт покажет как создавать переменные для особых случаев, например, только для чтения или константы и зачем вам это может потребоваться. Вы также обнаружите некоторые важные автоматические переменные, которые, между прочим, также константы.
Установите и запустите PowerShell Core и выполните такие шаги:
-
Попробуйте выполнить такой блок кода:
$true = $false $pid = 0 $Error = "Я никогда не ошибаюсь!"
-
Чтобы понять почему Шаг 1 не сработал как ожидалось выполните следующий cmdlet:
Get-Variable | Where-Object -Property Options -like *Constant*
-
При помощи cmdlet
variable
вы имеете возможность создавать собственные доступные только для чтения и не изменяемые переменные:# Создание доступных только для чтения и не изменяемых переменных требует cmdlet переменных $logPath = 'D:\Logs' Set-Variable -Name logPath -Option ReadOnly
-
Изменение доступных только для чтения переменных всё ещё возможно при помощи параметра
-Force
:Set-Variable -Name logPath -Value '/var/log' -Force
-
Значение
Option
параметра также полезно и для иных целей, например, для создания частных и глобальных переменных:Get-Help -Name New-Variable -Parameter Option
Cmdlet variable
предоставляют вам слегка больший контроль над тем как
создаются переменные, по сравнению с простым присвоением, $variable = "Значение"
,
которое вы видели в предыдущем примере. Предоставляя параметры вы можете управлять тем где будут видны задаваемые
переменные и будут ли они доступными только для чтения или константами (не изменяемыми).
Не изменяемые переменные (константы) могут определяться исключительно при их создании с посощью
New-Variable
и могут создаваться или удаляться лишь при создании нового сеанса
PowerShell.
Области действия важни для ваших повседневных сценариев и они управляют значениями видимости ваших переменных, функций и псевдонимов. Хотя PowerShell и проделывает автоматически огромное задание по обработке сфер действия таким образом, что это ощущается очень естественным для его пользователя, в своих сценариях вы также можете применять области действия и в явном виде.
Установите и запустите PowerShell Core , а потом выполните следующие шаги:
-
Выполните слудующий блок кода:
$outerScope = 'Variable outside function' function Foo { Write-Host $outerScope $outerScope = 'Variable inside function' Write-Host $outerScope } Foo Write-Host $outerScope
Вы должны получить такой вывод:
-
Слегка измените свой код, применяя модификаторы области действия
private
,script
иlocal
, а потом попробуйте снова:<# Определяя область действия в явном виде, вы можете изменять значение состояние практически любой переменной Доступны следующие области действия. Ко внутренним областям действия могут выполнять доступ переменные из внешних областей global: Самая внешняя область действия, т.е. ваш текущий сеанс script: Значение области действия сценария или модуля local: Значение области действия внутри некого блока сценария private: В любой области действия, скрывая от дочерних областей действия using: Это особый случай. #> $outerScope = 'Переменная вне function' $private:invisible = 'Не видимая из дочерних областей' function Foo { Write-Host $outerScope $script:outerScope = 'Переменная внутри function' $local:outerScope = 'Могут существовать обе' Write-Host "Доступ к содержимому переменной private не может быть осуществлён: $invisible" Write-Host $outerScope } Foo Write-Host $outerScope
На этот раз вы можете заметить, что вы можете создавать множество переменных с одним и тем же самым названием в различных областях действия.
-
Для того чтобы увидеть как работает применение области действия попробуйте далее такой код:
$processName = 'pwsh' $credential = New-Object -TypeName pscredential -ArgumentList 'user',$('password' | ConvertTo-SecureString -AsPlainText -Force) Start-Job -ScriptBlock { Get-Process -Name $processName} | Wait-Job | Receive-Job # Ошибка Start-Job -ScriptBlock { Get-Process -Name $using:processName} | Wait-Job | Receive-Job # Работает Start-Job -ScriptBlock { $($using:credential).GetNetworkCredential().Password } | Wait-Job | Receive-Job # Также работает
Как вы можете видеть из вывода предыдущего cmdlet, применение области действия делает возможным доступ к переменным изнутри некого блока сценария.
PowerShell обычно автоматически обрабатывает области действия переменной. При вашей интерактивной раьоте в CLI вы автоматически применяете его глобальную область действия. Вызов некого сценария исполнит этот сценарий в области действия этого сценария. Обычно исполнение функции или некого блока сценария запускает их в некой иной области действия, а именно, в локальной области.
Приявном применении модификаторов области действия вы можете изменять переменные, псевдонимы и функции к которым обычно вы не можете выполнять доступ при текущем положении. Хотя это и позволяет вам работать вне пределов ваших обычных ограничений, вам следует быть внимательным и не перекрывать имеющийся код своих пользователей используя имеющуюся глобальную область в большей степени.
Когда вы работаете с ними не внимательно, например, используя точку как источник, области действия могут создавать
некие проблемы. Применяя в качестве источника точку в . 'Full Script Path.ps1'
будет означать, что весь этот сценарий целиком вместе со всем своим содержимым будет импортирован в вашу текущую
облать действия. Однако это также означает и то, что данный сценарий способен перекурывать ваши переменные, псевдонимы,
функции, cmdlet и тому подобное.
PowerShell кардинально изменил сам способ автоматизации, а именно, вместо того чтобы предоставлять нам коды выхода, к которым многие привыкли. Вместо этого PowerShell применяет совершенно разные потоки для передачи вывода, ошибок, предупреждений и многого иного. Освоение таких потоков позволяет вам писать более мощные и универсальные сценарии.
Работа с шестью потоками оказывается настолько общей, что cmdlet поддерживают так называемые общие параметры,
которые совместно применяются всеми cmdlet. В данном рецепте вы будете работать с такими потоками как
Output
, Error
,
Warning
, Verbose
,
Debug
и Information
.
Установите и запустите PowerShell Core, а затем выполните следующие шаги:
-
Испробуйте следующие cmdlet и просмотрите полученный результат:
# Данная команда вернёт как ошибку, так и один объект вывода Get-Item -Path $home,'doesnotexist' # Обычно данная команда ничего не возвращает. Параметр Verbose однако включает другой поток. Remove-Item -Path $(New-TemporaryFile) -Verbose
-
Параметр
Verbose
на самом деле был частью общих параметров. Выполните следующие строки и посмотрите что произойдёт теперь:Get-Item -Path $home,'doesnotexist' -ErrorAction SilentlyContinue -OutVariable file -ErrorVariable itemError $file.FullName # Да, это ваш файл $itemError.Exception.GetType().FullName # Это точно выглядит как ваша ошибка
-
Наравне с потоками
Verbose
иDebug
, по умолчанию потокInformation
невидим. Попробуйте выполнить наш следующий код чтобы увидеть универсальность такого информационного потока.function Get-AllTheInfo { [CmdletBinding()]param() Write-Information -MessageData $(Get-Date) -Tags Dev,CIPipelineBuild if ($(Get-Date).DayOfWeek -notin 'Saturday','Sunday') { Write-Information -MessageData "Get to work, you slacker!" -Tags Encouragement,Automation } } # Как и потоки Verbose с Debug, Information по умолчанию не виден # Раьота с этой информацией намного улучшается теми cmdlet которые в действительности обрабатывают имеющиеся теги Get-AllTheInfo -InformationVariable infos Get-AllTheInfo -InformationAction Continue # Информация может фильтроваться и обрабкатываться, делая сообщения ваших сценариев более выразительными $infos | Where-Object -Property Tags -contains 'CIPipelineBuild'
-
Потоками также можно управлять глобально через переменные
Preference
, как это показано в примере ниже:$ErrorActionPreference = 'SilentlyContinue' $VerbosePreference = 'Continue' Import-Module -Name Microsoft.PowerShell.Management -Force Get-Item -Path '/somewhere/over/the/rainbow'
-
С целью смягчения риска имеются дополнительные параметры. Вы можете быть знакомы с ними в качестве
WhatIf
иConfirm
. Взгляните на следующий пример:Remove-Item -Path (New-TemporaryFile) -Confirm Remove-Item -Path (New-TemporaryFile) -WhatIf # Автоматическими переменными также обслуживаются WhatIf и Confirm $WhatIfPreference = $true New-TemporaryFile $WhatIfPreference = $false # Сброс в установленное по умолчанию значение $ConfirmPreference = 'Low' # None,Low,Medium,High New-TemporaryFile
-
Для записи в различные потоки в качестве разработчика попробуйте применять cmdlet
Write
(за исключениемWrite-Host
):Write-Warning -Message 'Предостережение выглядит так' Write-Error -Message 'В то время как ошибка представляется так' Write-Verbose -Message 'Verbose, Debug и Information по умолчанию скрыты'
При выполнении некого cmdlet, PowerShell автоматически применяет различные потоки для передачи сведений. Обычно
разработчики применяют эти потоки. Для некого cmdlet сценария это означает использование cmdlet
Write
. Возможность управления неким потоком предоставляется посредством применения
общих параметров.
В отличии от от управления самой доступности некого потока, весь поток целиком также может сохраняться в некой переменной прежде чем он будет продолжен через соответствующий конвейер.
Основная причина того почему мы не применяем Write-Host
при разработке
какого- то сценария состоит в том, что cmdlet Write-Host
не выполняет запись ни в
один из имеющихся потоков. Вместо этого для отображения текста используется консоль его хоста. Основная проблема такого
поведения состоит в том, что его пользователь не имеет контроля над видимостью таких сообщений хоста.
Если вы желаете добавить свои собственные параметры WhatIf
и
Confirm
, вам нет нужды реализовывать их как обычные параметры, как это показано
ниже:
# Не делайте этого
function Test-WrongRiskMitigation
{
param
(
[switch]
$WhatIf
)
if ($WhatIf)
{
Write-Host "Имитация уничтожения улик"
}
}
Вместо этого вы можете воспользоваться атрибутом CmdletBinding
, котоый
автоматически делает доступными все общие параметры и способен добавить как надлежит
WhatIf
и Confirm
:
function Test-RiskMitigation
{
[CmdletBinding(SupportsShouldProcess)]
param ( )
if ($PSCmdlet.ShouldProcess('Целевой объект, здесь: Evidence - Улика','Дейвствие, у нас: Shred - Угичтожить'))
{
Write-Host -ForegroundColor Red -Object 'Уничтожаем улики...'
}
}
Test-RiskMitigation -WhatIf
Test-RiskMitigation
Таким образом вы можете просто реагировать на тот cmdlet, который отправляет сообщение, в качестве пользователя. Не
требуется никакой дополнительной реализации кроме применения привязки этого cmdlet и автоматической переменной
PSCmdlet
.
Хотя оно и не применяется очень часто в PowerShell, время от времени для различных целей применяется перенаправление. В частности, внешние приложения имеют тенденцию путать имеющиеся потоки ошибок и вывода. Такое препятствие можно достаточно просто преодолевать при помощи перенаправления потока.
В этом рецепте вы изучите номера потоков. Все снабжённые номерами потоки могут перенаправляться при помощи операторов
перенаправления, >
и >>
. Данный
рецепт покажет как и когда применять такие операторы перенаправления вместо таких параметров как ErrorAction
или Verbose
.
Установите и запустите PowerShell Core, а потом осуществите такие шаги:
-
Переназначать можно все потоки, тем самым создавая некий новый файл как в таком примере:
Get-Item -Path $home,'nonexistant' 2> error.txt 1> success.txt Get-Content -Path error.txt, success.txt
-
Вы также можете добавлять различные потоки в конец файлов применяя соответствующий оператор
>>
:Get-Item -Path $home,'nonexistant' 2>> error.txt 1>> success.txt Get-Content -Path error.txt, success.txt
-
Потоки также можно сочетать в общий вывод при помощи оператора
>&1
:# Это, к примеру, помогает при плохо ведущих себя внешних приложениях Get-Module -Verbose -List -Name 'PackageManagement','nonexistant' 2>&1 4>&1
-
Однако сочетание потоков в общем выводе переполнит такой вывод:
$modules = Get-Module -Verbose -List -Name 'PackageManagement','nonexistant' 2>&1 4>&1 $modules.Count # Это должно содержать лишь один... $modules[0] # Это несомненно не выглядит как модуль
Перенаправление потоков работает совершенно иначе чем те параметры Variable
,
которые вы наблюдали в нашем предыдущем рецепте. Перенаправление, в том виде как оно реализовано во многих операционных
системах, будет перенаправлять некий поток, такой как Error
, либо в некий файл,
либо в имеющийся поток вывода. Вряд ли это можно с пользой применять как- то за исключением случая с приложениями,
которые неверно выдают потоки.
Как вы наблюдали на Шаге 4, применение переназначения потока в прочие потоки
следует применять аккуратно. Получаемый вывод нашего cmdlet Get-Module
был
переполнен некими записями об ошибках. Это совершенно другой тип данных и он несомненно помешает вам.
PowerShell расширятся различными поставщиками, которые делают возможным доступ к различным хранилищам данных. Наиболее повсеестным, тем не менее, выступает поставщик файловой системы. Мы автоматически применяем его во многих сценариях и при интерактивной работе в командной строке.
Даный рецепт представляет поставщиков PowerShell и показывает вам что может делать, а чего нет поставщик файловой системы.
Установите и запустите PowerShell Core. Если вы хотите отображать совместный ресурс CIFS, убедитесь что вы имеете возможность подключения к нему.
Давайте выполним следующие шаги:
-
Как только вы запустите PowerShell Core, имеющийся поставщик файловой системы начинает работать и импортирует ваши смонтированные устройства. Обычно вас помещают в ваш собственный каталог
home
или - когда вы системный администратор - в корне вашей системы. -
Взгляните на вывод
Get-PSProvider
чтобы посмотреть на всех доступных поставщиков, как это показано ниже:
-
Для просмотра того, какие cmdlet поддерживаются поставщиками, выполните следующий код:
# те cmdlet, которые работают с поставщиками Get-Command -Noun Location, Item, ItemProperty, ChildItem, Content, Path
-
По умолчанию различные поставщики соответствуют различным устройствам. Посмотрите на некоторых из них, выполнив такой код:
# Поставщики обычно автоматически монтируют свои устройства Get-PSDrive
-
Поставщики делают возможным для вас применение тех же самых основных операций, например,
Set-Location
по любому пути. Попробуйте это в любой имеющейся файловой системе:# Перемещение по вашей файловой системе Set-Location -Path $home
-
Для конкретного поставщика файловой системы cmdlet
Get-ChildItem
может применять дополнительные параметры. Давайте взглянем на некоторые из них в своём следующем примере:# Параметры File и FollowSymlink доступны только в случае примененеия поставщика файловой системы Get-ChildItem -Recurse -File -FollowSymlink Get-ChildItem -Path env: # Only default parameters here # Замена универсальными символами поддерживается вне зависимости от файловой системы и поставщика Get-ChildItem -Path /etc/*ssh*/*config Get-ChildItem -Path C:\Windows\*.dll Get-ChildItem -Path env:\*module*
-
Для начала заслуживает особенного рассмотрения имеющийся синтаксис для строения cmdlet
Get-Command
. Взгляните на следующий кодовый блок:# Рассмотрите следующий синтаксис упрощения операций, # например, создание множества элементов из некого массива $folders = @( "$home/test1" "$home/test2/sub1/sub2" "$home/test3" ) New-Item -Path $folders -ItemType Directory -Force # либо создание некого файла во множестве местоположений New-Item -Path $folders -Name 'someconfig.ini' -ItemType File -Value 'key = value'
-
Поставщик файловой системы ведёт себя аналогично обычным инструментам управления файловой системой, но ваши результаты могут отличаться, к примеру, от получаемого вывода окна свойств Windows Explorer с большим числом файлов по сравнению с
Get-ChildItem
:New-Item $home\hidden\testfile,$home\hidden\.hiddentestfile -ItemType File -Force $(Get-Item $home\hidden\.hiddentestfile -Force).Attributes = [System.IO.FileAttributes]::Hidden Get-ChildItem -Path $home\hidden # .hiddentestfile не появится Get-ChildItem -Path $home\hidden -Hidden # Покажет лишь скрытый файл Get-ChildItem -Path $home\hidden -Force # Возвратит все файлы
-
Параметры
Include
иExclude
могут оказаться мощными опциями фильтрации дляGet-ChildItem
:# Параметры Include и Exclude могут служить полезными фильтрами # / в установках Windows по умолчанию для системных устройств # Делая возможной более сложные филтрации чем обычный параметр Filter Get-ChildItem -Path $pshome -Recurse -Include *.dll,*.json -Exclude deps.ps1
-
cmdlet
content
позволяют что- нибудь изменять. Для определённой файловой системы вы как правило используете их для считывания и измененеия содержимого файла:# Это также работает и для прочих cmdlet поставщиков Get-Content -Path $pshome/Modules/PackageManagement/* -Include *.psm1 Set-Content -Path $home\testfile -Value "File content`nMultiple lines" Add-Content -Path $home\testfile -Value 'Another new line' Get-Content -Path $home\testfile
-
Наконец, выполните следующий код для отображения нового совместного ресурса CIFS с помощью PowerShell:
# Именно поставщик файловой системы является тем, что позволяет монтировать дополнительные устройства поставщика New-PSDrive -Name onlyInShell -Root \\someserver\someshare -PSProvider FileSystem Get-ChildItem -Path onlyInShell: Remove-PSDrive -Name onlyInShell
В зависимости от вашей операционной системы будут иметься такие поставщики:
-
Alias: Делает возможным доступ к псевдонимам
-
Environment: Позволяет выполнять доступ к переменным среды
-
Function: Разрешает доступ к блокам функций сценария
-
Variable: Предлагает доступ к переменным
-
FileSystem: Делает возможным доступ к самой файловой системе и имеющимся совместным ресурсам CIFS
-
Certificate: На данный момент только для Windows позволяет выполнять доступ к хранилищам сертификатов
-
Registry: Исключительно для Windows открывает доступ к имеющемуся реестру Windows
-
WSMan: В настоящий момент лишь в Windows позволяет выполнять доступ к конфигурации WinRM через WSMan
Это далеко не исчерпывающий перечень, так как новые поставщики добавляются посредством модулей или могут создаваться при помощи соответствующего модуля SHiPS, который мы рассмотрим в своём рецепте Создание вашего собственного поставщика.
Имеющийся у поставщика код реализует различные cmdlet, такие как Get-ChildItem
и применяет свою собственную логику в этом коде для того чтобы выполнять то, что подразумевает соответствующее название
cmdlet. Не все поставщики реализуют все операции. Хотя будет совершенно исключительным применение New-Item
в файловой системе, он работает совершенно иначе в поставщике сертификата, который всего лишь позволяет создавать новые
хранилища сертификатов.
Поставщик реестра является исключеительно поставщиком Windows, который делает возможным доступ к локальному реестру некого хоста. При помощи обычных cmdlet поставщика можно перемещаться по всем локальным ульям как и по некой файловой системе.
Установите и запустите PowerShell Core в Windows, а затем выполните такие шаги:
-
Для перечисления всех элементов в определённом улье реестра своей локальной машины выполните такой код:
# Как и для файловой системы, имеется возможность обхода улья локального реестра. # Применяются ACL, поэтому нередки ошибки AccessDenied Get-ChildItem HKLM:\SOFTWARE
-
Поскольку нет никаких дополнительных фильтров, у вас нет особого контроля над
Get-ChildItem
, который лишь возвращает ключи реестра и отображает их значения. Попытка перечисления значений следующим обазом завершится отказом:# По умолчанию Get-ChildItem возвращает ключи и их значения Get-ChildItem -Recurse -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
-
Для перечисления значений реестра применяется cmdlet
Get-ItemProperty
. Попробуйте такой образец кода:# Для выборки только свойств вместо этого применяется Get-ItemProperty # Без предоставления названия, Get-ItemProperty возвращает для данного пути все значения Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' # Когда применяется только значение соответствующего свойства Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name ProductName # Хотя это применяется преимущественно для доступа к реестру, это также можно применять и к # файловой системе. Тем не менее, такой подход слишком громоздкий Get-ItemProperty -Path $(Get-Command -Name pwsh).Source -Name LastWriteTime
-
Создание новых элементов рабоатет аналогично файловой системе. Обратите внимеание, что создаются ключи реестра, не значения:
# Вы можете применять New-Item для создания новых ключей New-Item -Path HKCU:\Software -Name MyProduct
-
Для работы со значениями прмиеняются cmdlet
ItemProperty
. Для того чтобы увидеть как создаются новые значения и изменяются имеющиеся, воспользуйтесь следующим образцом кода:<# Для создания новых значений применяйте New-ItemProperty. Значениями для PropertyType могут быть: String (REG_SZ): Стандартная строка ExpandString (REG_EXPAND_SZ): Строка с автоматическим расширением переменной среды Binary (REG_BINARY): Двоичные данные DWord (REG_DWORD): 32bit ldjbxyjt xbckj MultiString (REG_MULTI_SZ): Массив строк QWord (REG_QWORD): 64bit двоичное число #> New-ItemProperty -Path HKCU:\Software\MyProduct -Name Version -Value '0.9.9-rc1' -PropertyType String New-ItemProperty -Path HKCU:\Software\MyProduct -Name SourceCode -Value $([Text.Encoding]::Unicode.GetBytes('Write-Host "Cool, isnt it?"')) -PropertyType Binary # Проверьте это ;) [scriptblock]::Create($([Text.Encoding]::Unicode.GetString($(Get-ItemPropertyValue -Path HKCU:\Software\MyProduct -Name SourceCode)))).Invoke() # Измените некий элемент Set-ItemProperty -Path HKCU:\Software\MyProduct -Name SourceCode -Value $([Text.Encoding]::Unicode.GetBytes('Stop-Computer -WhatIf')) [Text.Encoding]::Unicode.GetString($(Get-ItemPropertyValue -Path HKCU:\Software\MyProduct -Name SourceCode))
-
Удалять элементы просто. Для того чтобы снова удалить ваш ключ попробуйте следующий образец кода:
# Имеющийся по умолчанию cmdlet удаления работает также Remove-Item -Path HKCU:\Software\MyProduct -Verbose
-
Имейте в виду, что имеющийся поставщик реестра не способен отображать удалённые реестры - чтобы иметь возможность выполнять подобное вам требуется применять .NET.
# Нет возможности применять права доступа Get-PSProvider -PSProvider Registry # Отображение локального улья работает отлично New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT Get-ChildItem -Path HKCR: Remove-PSDrive -Name HKCR
Как и поставщик файловой системы, поставщик реестра реализует определённые cmdlet поставщика. Это делает возможным для вас
применение взаимодействовать с реестром из своих сценариев. Для обхода проблемы с невозможностью отображения улья удалённого
реестра можно применять cmdlet Invoke-Command
.
В то время как ваша файловая система работает с файлами и папками, имеющийся реестр работает с ключами и значениями. Такой
реестр работает с ключами и значениями. Данная структура может выглядеть похожей на файловую систему, однако реализуется слегка
иначе. Для доступа к к файлам вам это может проделать любой cmdlet Item
. Для доступа
к значениям реестра cmdlet Item
несомненно рабоатют, однако для выборки только
содержимого некого значения вам потребуется применять cmdlet ItemProperty
.
Хотя Invoke-Command
через управление веб службой и удалённое управление Windows
должны быть предпочтительным способом доступа к ключам удалённого реестра, могут возникать проблемы с упорядочением и
иные сложности. Применяя .NET вы также можете просто делать доступ к улью удалённого реестра через
DCOM
(Distributed Component Object Model
)
и RPC
(Remote Procedure Calls
) из своего
локального сеанса. Для начала рассмотрите такой образец кода:
$remoteKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', 'MyHost', 'Registry64')
$remoteUserKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('CurrentUser', 'MyHost', 'Registry64')
Наши предыдущие строки открывают два удалённых улья, HKLM и HKCU. В наших последующих строках вы можете получить значения имён в неком ключе и получить некое определённое значение из названного ключа:
$remoteKey.OpenSubKey('SOFTWARE\Microsoft\Windows NT\CurrentVersion').GetValueNames()
$remoteKey.OpenSubKey('SOFTWARE\Microsoft\Windows NT\CurrentVersion').GetValue('ProductName')
Для создания удалённых подчинённых ключей и удалённых значений вы можете воспользоваться приводимыми ниже строками.
Обратите внимание на Булево значение параметра для нашего метода OpenSubKey
. Для
доступа на запись требуется открыть ключ, так как по умолчанию он доступен лишь для чтения:
$remoteUserKey.OpenSubKey('SOFTWARE',$true).CreateSubKey('MyProduct').SetValue('Version','1.2.3.4', 'String')
$remoteUserKey.OpenSubKey('SOFTWARE\MyProduct').GetValue('Version')
$remoteUserKey.DeleteSubKeyTree('Software\MyProduct')
Хотя это и не предписано как обязательное действие, вам следует всегда высвобождать ссылки на объекты .NET чтобы процесс сборки мусора мог очищать ресурсы:
$remoteKey.Dispose()
$remoteUserKey.Dispose()
Как и поставщик реестра, поставщик сертификатов является привязанным исключительно к Windows. Он позволяет вам выполнять доступ к хранилищам сертификатов для учётых записей компьютера илм пользователяпричём вам доступны некоторые дополнительные методы фильтрации.
Как и для поставщика реестра, есть ряд вопросов для рассмотрения.
Установите в неком хосте Windows PowerShell Core и выполните такую последовательность шагов:
-
Для перечисления хранилищ сертификатов и самих сертификатов внутри хранилища воспользуйтесь таким cmdlet поставщика:
# Ещё один поставщик, применяемый исключительно для Windows, делающий возможным доступ к локальным хранилищам сертификатов Get-PSProvider -PSProvider Certificate # И вновь применим установленный по умолчанию cmdlets # Перечисли все хранилища сертификатов Get-ChildItem -Path Cert:\CurrentUser # Перечилим все сертификаты конкретного персонального хранилища пользователя Get-ChildItem -Path Cert:\CurrentUser\my
-
ДРасширяющие параметры cmdlet
Get-ChildItem
позволяют прмменять дополнительнные фильтры:# Те параметры, которые предлагаются в Windows PowerShell его потсавщиком сертификатов очень интересны, # будут доступны дополнительные параметры, такие как -EKU -SslServerAuthentication Get-ChildItem -Path Cert:\CurrentUser\my -CodeSigningCert
-
В PowerShell Core могут использоваться не все параметры
Get-ChildItem
, с которыми вы знакомы в PowerShell Windows.Where-Object
продолжает оставаться вашим другом:$certificate = Get-ChildItem -Path Cert:\CurrentUser\my | Select-Object -First 1 # Фильтруем по значениям OID. Если невозможно разрешить OID, вместо дружественного названия применяйте ID пречисления объектов! # Значение OID является более надёжным и не выступает предметом локализации $certificate.EnhancedKeyUsageList # в качестве примера поиска по всем сертификатам аутентификации клиентов Get-ChildItem -Path cert:\currentuser\my | Where-Object -FilterScript {$_.EnhancedKeyUsageList.ObjectId -eq '1.3.6.1.5.5.7.3.2'} # Не менее важно; фильтрация сертификатов когда доступны частные ключи, т.е. для документов с цифровой подписью Get-ChildItem -path Cert:\CurrentUser\my | Where-Object -Property HasPrivateKey | Format-table -Property Subject,Thumbprint,@{Label='EKU'; Expression = {$_.EnhancedKeyUsageList.FriendlyName -join ','}} $certificate.HasPrivateKey
-
Не все cmdlet
Item
реализованы для имеющегося поставщика сертификатов:# Хотя cmdlet New и Set не реализованы для сертификатов, Remove может применяться для некой тщательной очистки Get-ChildItem -Path Cert:\CurrentUser\my | Where-Object -Property NotAfter -lt $([datetime]::Today) | Remove-Item -WhatIf
-
Однако вы всё ещё можете создавать новые хранилища сертификатов:
# New-item может использоваться для новыххранилищ - но это редко применяется New-Item -Path Cert:\LocalMachine\NewStore Remove-Item -Path Cert:\LocalMachine\NewStore
Как это имеет место и в случае с прочими поставщиками, имеющиеся cmdlet реализуют свою собственную функциональность и
делают возможным доступ к сертификатам в вашем персональном хранилище. Хотя получаемая функциональность и достаточно
ограничена, cmdlet Get-ChildItem
и Get-Item
полезны для фильтрации сертификатов и удаляют сертификаты с истекшим сроком если они не удалены автоматически.
Также весьма полезным (и увлекательным) может быть и создание вашего собственного поставщика. Соответствующий
модуль SHiPS PowerShell позволяет вам устанавливать соответствие для любых имеющихся структур для доступа к ним в
качестве некого устройства. Для выборки данных на данный момент SHiPS поддерживает лишь
Get-Item
и Get-ChildItem
.
SHiPS, например, применяется в PowerShell для Оболочки Azure Cloud, а также в популярном модуле автоматизации AutomatedLab.
Установите и запустите PowerShell Core, а затем выполните такие шаги:
-
Для получения возможности разработки своего собственного поставщика, вам требуется вначале установить из галереи SHiPS. В качестве альтернативы вы можете последовать рекомендациям в GitHub для компиляции своего собственного ядра .NET:
# Установите SHiPS Install-Module -Name SHiPS -Force -Scope CurrentUser
-
SHiPS основывается на классах PowerShell. Прежде всего вы можете пожелать добавить свой собственный контейнер корня:
# Поставщики SHiPS основываются на классах PowerShell. # Для создания некого нового каталога, либо контейнера, ваш класс должен # наследоваться из класса ShipsDirectory class MyContainerType : Microsoft.PowerShell.SHiPS.SHiPSDirectory { # Ваш новый контейнер теперь должен реализовывать по крайней мере функцию GetChildItem(). # Чтобы в действительности возвращать дочергие элементы, ваш контейнер должен быть текущим! MyContainerType([string]$name): base($name) { } [object[]] GetChildItem() { $obj = @() $obj += [MyFirstLeafType]::new(); return $obj; } }
-
Добавьте другой контейнер, который вложен в ваш корень, также как и один или два листа:
# Листья являются дочерними элементами ваших контейнеров, которые больше сами по себе не могут содержать никаких дополнительных элементов. # Тем не менее, контейнеры всё ещё могут содержать контейнеры. class MyFirstContainerType : Microsoft.PowerShell.SHiPS.SHiPSDirectory { MyFirstContainerType([string]$name): base($name) { } } class MyFirstLeafType : Microsoft.PowerShell.SHiPS.SHiPSLeaf { MyFirstLeafType () : base ("MyFirstLeafType") { } [string]$LeafProperty = 'Value' [int]$LeafLength = 42 [datetime]$LeafDate = $(Get-Date) }
-
Наконец, сохраните определение своего поставщика в качестве модуля и импортируйте его:
# Рассмотоите данный образец кода psedit .\ShipsProvider.psm1 # Для монтирования устройства вашего поставщика вы можете просто воспользоваться New-PSDrive Import-Module -Name .\ShipsProvider -Force
Замечание psedit
является удобной доступной в коде Visual Studio функцией для открытия файлов в вашем редакторе! Если вы работаете с другим IDE, для просмотра кода вам может потребоваться другая команда. Попробуйте вместо этогоGet-Content
. -
С помощью SHiPS и вашего импортированного модуля вы можете смонтировать собственное устройство. Обратите внимание, что значение корня вашего устройства полагается на значение класса root, который содержит вашу иерархию:
New-PSDrive -Name MyOwnDrive -PSProvider SHiPS -Root "ShipsProvider#MyContainerType"
-
Изучите его с помощью cmdlet
item
:# Теперь вы можете использоввать имеющиеся cmdlet item по своиему усмотрению Get-ChildItem -Path MyOwnDrive:\MyFirstContainerType Get-ChildItem -Path MyOwnDrive: -Recurse
-
Обратите внимание на то как отображаются ваши листья и контейнеры:
SHiPS позволяет вам отображать доступные только для чтения структуры данных по вашему выбору, ограничиваясь лишь
вашими навыками программирования. Хотя вы и не можете применять такие cmdlet как Remove-Item
,
вы можете определять свои собственные методы объекта или выставлять функции, которые вызывают внутренним образом cmdlet
или методы .NET. Ваш собственный поставщик также может возвращать объеткы .NET из имеющихся классов, напрмиер объекты
System.IO.FileInfo
, возвращаемые поставщиком определённой файловой системы.
Ваши данные всё ещё могут обрабатываться cmdlet Object
, в конвейерах и в
противном случае обрабатываются PowerShell. Просто имейте в виду, что для выборки необходимых данных могут применяться
только Get-Item
и Get-ChildItem
, а также
что все прочие cmdlet provider-aware
просто не реализованы.
Если вы уверенно себя чувствуете, вы можете добавлять своему поставщику динамические параметры. Помните параметр
File
для поставщика FileSystem
или
CodeSigningCertificate
для поставщика
Certificate
? При помощи SHiPS вы запросто можете добавлять свои собственные
параметры:
class MyDynamicParameter
{
[Parameter()]
[switch]
$DispenseCandy
}
class MyContainerType : Microsoft.PowerShell.Ships.SHiPSDirectory
{
...
[object] GetChildItemDynamicParameters()
{
return [MyDynamicParameter]::new()
}
...
}
New-PSDrive -Name MyOwnDrive -PSProvider SHiPS -Root ShipsProviderAdvanced#MyContainerType
Get-ChildItem MyOwnDrive: -Recurse -DispenseCandy
Это не ограничивается тольао параметрами переключателей, но может быть и всеми прочими типами параметра, о которых
вы только можете подумать. Это также включает также и параметры валидации, позволяющие вам, например, задавать параметр
ValidateSet
для Get-ChildItem
и
вашего собственного поставщика.
-
Для получения дополнительных сведений относительно SHiPS обращайтесь к https://github.com/powershell/ships.
-
чтобы узнать болше о классах ознакомьтесь с книгой David das Neves и Jan-Hendrik Peters PowerShell Core 6.0 или с официальной документацией.
Хотя он и не совсем относится к таким потокам как потоки вывода и ошибок, которые мы наблюдали до этого, существует особый
вид потока, с которым достаточно интересно поиграть: альтернативный поток данных NTFS. Этот рецепт относится к особым
форматированным NTFS томам и покажет вам зачем прежде всего имеется cmdlet Unblock-File
.
Установите и запустите PowerShell Core, а затем выполните такое шаги:
-
Выгрузите некий файл для упражнений:
# Чтобы начать выгрузите любой файл в отформатированный NTFS том. # Данное лабораторное занятие предполагает что вы сохранили выгруженное в $home\Downloads $downloadRoot = "~\Downloads" # Выгрузите любой файл, например, некий выпуск популярной инфраструктуры даборатории автоматизации AutomatedLab start https://github.com/AutomatedLab/AutomatedLab/releases/download/v5.1.0.153/AutomatedLab.msi
-
Опробуйте файл
Get-Item
. Вы обратили внимание на кое- какую неравномерноость? Давайте посмотрим:# На первый взгляд этот файл кажется нормальным Get-Item $downloadRoot\AutomatedLab.msi
-
Давайте попробуем
Get-Item
снова, на этот раз с параметром-Stream
. В чём теперь состоит разница? Давайте рассмотрим:Get-Item $downloadRoot\AutomatedLab.msi -Stream *
Предыдущая строка кода отображает вам то что вы можете видеть в диалоге свойств самой файловой системы. Этот поток с названием
Zone.Identifier
отвечает за создание такого сообщения в вашем UI!
Замечание Все файлы NTFS содержат некий поток данных и дополнительные потоки, такие как значение идентификатора зоны для вызргузок файла.
-
Потоки могут обрабатываться cmdlet-ами
Item
иContent
:# Потоки могут обрабатываться cmdlet-ами Content- # Сам поток данных, конечно же, возвращается по умолчанию Get-Content -Path C:\windows\logs\cbs\cbs.log -Stream ':$DATA' # Хначение идентификатора зоны определяет выгружается ли данный файл из Интернета или из иной зоны # Из них вы можете выявлять все виды информации, например значение URL адреса в данном примере. # На момент написания, значение HostUrl располагалось в хранилище S3 Amazon и прменялось в качестве # подписи доступа для установления даты истечения срока этой ссылки Get-Content -Path $downloadRoot\AutomatedLab.msi -Stream Zone.Identifier
-
Вы не только можете считывать их, вы также можете устанавливать свои собственные:
# Давайте теперь испробуем другие cmdlet содержимого... Set-Content -Path .\TestFile -Value 'A simple file' $bytes = [Text.Encoding]::Unicode.GetBytes('Write-Host "Virus deployed..." -Fore Red') $base64script = [Convert]::ToBase64String($bytes) # Теперь внутри не привлекающего внимания файла мы скрыли некий сценарий Set-Content -Path .\TestFile -Stream Payload -Value $base64script # И естественно мы можем выполнить его следующей бесподобной командой в одну строку [scriptblock]::Create($([Text.Encoding]::Unicode.GetString($([Convert]::FromBase64String($(Get-Content .\TestFile -Stream Payload)))))).Invoke()
-
Вы также имеете возможность очищать содержимое потока также как и удалять потоки:
# Также вы можете вручную очищать определённый поток Clear-Content -Path .\TestFile -Stream Payload # И вы имеете возможнгость удалить этот поток целиком Remove-Item -Stream Payload -Path .\TestFile
-
Для потока идентификатора зоны cmdlet
Unblock-File
сделает более простым удаление:# Сам cmdlet Unblock-File делает в точности то же самое со значением Zone.Identifier Unblock-File -Path $downloadRoot\AutomatedLab.msi Get-Item -Path $downloadRoot\AutomatedLab.msi -Stream *
Альтернативные потоки данных NTFS, как подразумевает их название, выступают потоками, в которых могут храниться данные. Поскольку это позволяет развёртывать вредоносные программы, похожие на те, которые мы применяли в данном рецепте, такие потоки применяются чаще чемы вы полагаете.
Значение идентификатора зоны является самым простым и наиболее доступный пример, поскольку он обычно записывается при выгрузке файла обычным способом в браузере. Значения индивидуальных потоков таковы:
Идентификатор зоны | Отображаемое название зоны |
---|---|
|
My computer |
|
Local intranet |
|
Trusted sites |
|
Internet |
|
Restricted sites |
Такие компоненты Windows как Диспетвер ресурсов Файлового сервера или продукты подобные DropBox также применяют
альтернативные потоки данных для сохранения информации. В случае DropBox такой поток именуется
com.dropbox.attributes
, который, видимо, применяется для хранения данных о машине,
с которой был синхронизирован этот файл, как это показано ниже:
Я надеюсь, что разжёг ваше любопытство, а потому давайте посмотрим на свои файлы чтобы обнаружить что зарыто в глубине ваших дисков:
Get-ChildItem -Path ~ -Recurse -File | Where-Object -FilterScript {
$($_ | Get-Item -Stream *).Stream -notmatch ':\$DATA|Zone\.Identifier'
}