Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Однострочники и скрипты PowerShell, которые часто приходится менять, являются хорошими кандидатами, чтобы превратиться в многоразовые функции.
Пишите функции, когда это возможно, так как они больше подходят для инструментальной задачи. Вы можете добавить функции в модуль скрипта, поместить этот модуль в расположение, определенное в $env:PSModulePath
, и вызвать функции без необходимости найти место сохранения функций. С помощью модуля PowerShellGet
Не переусложняйте вещи. Оставить его простым и использовать самый простой способ выполнения задачи. Избегайте псевдонимов и позиционных параметров в любом коде, который вы используете повторно. Форматирование кода для удобства чтения. Не хардкодьте значения; используйте параметры и переменные. Не создавайте ненужный код, даже если он ничего не повредит. Он добавляет ненужную сложность. Внимание к деталям имеет большое значение при написании кода на PowerShell.
Именование
При именовании функций в PowerShell используйте стиль Pascal с утвержденным глаголом и единственным существительным. Чтобы получить список утвержденных глаголов в PowerShell, выполните Get-Verb
. В следующем примере сортируются результаты Get-Verb
по свойству Verb.
Get-Verb | Sort-Object -Property Verb
Свойство Group дает представление о том, как должны использоваться глаголы.
Verb Group
---- -----
Add Common
Approve Lifecycle
Assert Lifecycle
Backup Data
Block Security
Checkpoint Data
Clear Common
Close Common
Compare Data
Complete Lifecycle
Compress Data
Confirm Lifecycle
Connect Communications
Convert Data
ConvertFrom Data
ConvertTo Data
Copy Common
Debug Diagnostic
Deny Lifecycle
Disable Lifecycle
Disconnect Communications
Dismount Data
Edit Data
Enable Lifecycle
Enter Common
Exit Common
Expand Data
Export Data
Find Common
Format Common
Get Common
Grant Security
Group Data
Hide Common
Import Data
Initialize Data
Install Lifecycle
Invoke Lifecycle
Join Common
Limit Data
Lock Common
Measure Diagnostic
Merge Data
Mount Data
Move Common
New Common
Open Common
Optimize Common
Out Data
Ping Diagnostic
Pop Common
Protect Security
Publish Data
Push Common
Read Communications
Receive Communications
Redo Common
Register Lifecycle
Remove Common
Rename Common
Repair Diagnostic
Request Lifecycle
Reset Common
Resize Common
Resolve Diagnostic
Restart Lifecycle
Restore Data
Resume Lifecycle
Revoke Security
Save Data
Search Common
Select Common
Send Communications
Set Common
Show Common
Skip Common
Split Common
Start Lifecycle
Step Common
Stop Lifecycle
Submit Lifecycle
Suspend Lifecycle
Switch Common
Sync Data
Test Diagnostic
Trace Diagnostic
Unblock Security
Undo Common
Uninstall Lifecycle
Unlock Common
Unprotect Security
Unpublish Data
Unregister Lifecycle
Update Data
Use Other
Wait Lifecycle
Watch Common
Write Communications
Важно использовать рекомендованный глагол для функций PowerShell. Модули, содержащие функции с неутвержденными глаголами, выдают предупреждение при импорте в сеанс PowerShell. Это предупреждение создает впечатление, что ваши функции выглядят непрофессионально. Неподтвержденные глаголы также ограничивают обнаруживаемость ваших функций.
Простая функция
Функция в PowerShell объявлена с ключевым словом function, за которым следует имя функции, а затем открывающая и закрывающая фигурная скобка ({ }
). Код, выполняемый функцией, содержится в фигурных скобках.
function Get-Version {
$PSVersionTable.PSVersion
}
Функция, показанная в следующем примере, является простым примером, который возвращает версию PowerShell.
Get-Version
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
При использовании универсального имени для функций, таких как Get-Version
, это может привести к конфликтам именования. Команды по умолчанию, которые будут добавлены в будущем, или команды, которые могут написать другие, могут вступить с ними в конфликт. Используйте префиксы для части имени функций, содержащей существительное, чтобы предотвратить конфликты имен. Например, <ApprovedVerb>-<Prefix><SingularNoun>
.
В следующем примере используется префикс PS
.
function Get-PSVersion {
$PSVersionTable.PSVersion
}
Кроме имени, эта функция идентична предыдущей.
Get-PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
Вы по-прежнему можете иметь конфликт имен даже при добавлении префикса в существительное. Мне нравится добавлять к именам моих функций префикс с моими инициалами. Разработайте стандарт и придерживайтесь его.
function Get-MrPSVersion {
$PSVersionTable.PSVersion
}
Эта функция не отличается от двух предыдущих, за исключением того, что она использует более уникальное имя для предотвращения конфликта имен с другими командами PowerShell.
Get-MrPSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
После загрузки в память вы можете увидеть функции на диске PSDrive Function.
Get-ChildItem -Path Function:\Get-*Version
CommandType Name Version
----------- ---- -------
Function Get-Version
Function Get-PSVersion
Function Get-MrPSVersion
Если вы хотите удалить эти функции из текущего сеанса, удалите их из функции PSDrive или закройте и снова откройте PowerShell.
Get-ChildItem -Path Function:\Get-*Version | Remove-Item
Убедитесь, что функции действительно удалены.
Get-ChildItem -Path Function:\Get-*Version
Если функции были загружены в составе модуля, можно выгрузить модуль, чтобы удалить их.
Remove-Module -Name <ModuleName>
Командлет Remove-Module
удаляет модули PowerShell из памяти в текущем сеансе PowerShell. Он не удаляет их из системы или диска.
Параметры
Не присваивайте статические значения. Вместо этого используйте параметры и переменные. При именовании параметров используйте то же имя, что и командлеты по умолчанию для имен параметров всякий раз, когда это возможно.
Обратите внимание, что в следующей функции я использовал ComputerName, а не computer, ServerNameили Host для имени параметра. Использование ComputerName стандартизирует имя параметра, приводя его в соответствие с именем и регистром параметра, как в командлетах по умолчанию.
function Test-MrParameter {
param (
$ComputerName
)
Write-Output $ComputerName
}
Следующая функция запрашивает все команды в системе и возвращает число с определенными именами параметров.
function Get-MrParameterCount {
param (
[string[]]$ParameterName
)
foreach ($Parameter in $ParameterName) {
$Results = Get-Command -ParameterName $Parameter -ErrorAction SilentlyContinue
[pscustomobject]@{
ParameterName = $Parameter
NumberOfCmdlets = $Results.Count
}
}
}
Как вы видите в следующих результатах, 39 команд с параметром ComputerName. Нет команд с такими параметрами, как computer, ServerName, Hostили Machine.
Get-MrParameterCount -ParameterName ComputerName, Computer, ServerName,
Host, Machine
ParameterName NumberOfCmdlets
------------- ---------------
ComputerName 39
Computer 0
ServerName 0
Host 0
Machine 0
Используйте тот же вариант для имен параметров, что и командлеты по умолчанию. Например, используйте ComputerName
, а не computername
. Эта схема именования способствует тому, чтобы люди, знакомые с PowerShell, могли обнаруживать ваши функции, и чтобы они выглядели и работали, как стандартные командлеты.
Оператор param
позволяет определить один или несколько параметров. Запятая (,
) разделяет определения параметров. Дополнительные сведения см. в разделе about_Functions_Advanced_Parameters.
Расширенные функции
Преобразование функции в расширенную функцию в PowerShell является простой. Одним из различий между функцией и расширенной функцией является то, что расширенные функции имеют общие параметры, которые автоматически добавляются. Общие параметры включают такие параметры, как подробность и отладка.
Начните с функции Test-MrParameter
, которая использовалась в предыдущем разделе.
function Test-MrParameter {
param (
$ComputerName
)
Write-Output $ComputerName
}
Существует несколько различных способов просмотра общих параметров. Один способ — просмотр синтаксиса с Get-Command
.
Get-Command -Name Test-MrParameter -Syntax
Обратите внимание, что функция Test-MrParameter
не имеет общих параметров.
Test-MrParameter [[-ComputerName] <Object>]
Другой способ — углубиться в свойства параметров Get-Command
.
(Get-Command -Name Test-MrParameter).Parameters.Keys
ComputerName
Добавьте атрибут CmdletBinding
, чтобы превратить функцию в расширенную функцию.
function Test-MrCmdletBinding {
[CmdletBinding()] # Turns a regular function into an advanced function
param (
$ComputerName
)
Write-Output $ComputerName
}
При указании CmdletBinding
общие параметры добавляются автоматически.
CmdletBinding
требуется блок param
, но блок param
может быть пустым.
Get-Command -Name Test-MrCmdletBinding -Syntax
Test-MrCmdletBinding [[-ComputerName] <Object>] [<CommonParameters>]
Углубленный анализ свойства параметров Get-Command
показывает фактические имена параметров, включая общие.
(Get-Command -Name Test-MrCmdletBinding).Parameters.Keys
ComputerName
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable
Поддержка SupportsShouldProcess
Атрибут SupportsShouldProcess
добавляет параметры управления рисками WhatIf и Confirm. Эти параметры необходимы только для команд, которые вносят изменения.
function Test-MrSupportsShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param (
$ComputerName
)
Write-Output $ComputerName
}
Обратите внимание, что теперь существуют параметры WhatIf и Confirm.
Get-Command -Name Test-MrSupportsShouldProcess -Syntax
Test-MrSupportsShouldProcess [[-ComputerName] <Object>] [-WhatIf] [-Confirm]
[<CommonParameters>]
Вы также можете использовать Get-Command
, чтобы вернуть список фактических имен параметров, включая общие, а также WhatIf и Confirm.
(Get-Command -Name Test-MrSupportsShouldProcess).Parameters.Keys
ComputerName
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable
WhatIf
Confirm
Проверка параметров
Проверьте входные данные на ранней стадии. Не позволяйте коду продолжать путь, если он не может завершиться без допустимых входных данных.
Всегда укажите тип данных для переменных, используемых для параметров. В следующем примере String указывается в качестве типа данных для параметра ComputerName. Эта валидация ограничивает возможность указания только одного имени компьютера для параметра ComputerName.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[string]$ComputerName
)
Write-Output $ComputerName
}
Ошибка возникает, если указано несколько имен компьютера.
Test-MrParameterValidation -ComputerName Server01, Server02
Test-MrParameterValidation : Cannot process argument transformation on
parameter 'ComputerName'. Cannot convert value to type System.String.
At line:1 char:42
+ Test-MrParameterValidation -ComputerName Server01, Server02
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Test-MrParameterValidation]
, ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Test-MrP
arameterValidation
Проблема с текущим определением заключается в том, что допустимо опустить значение параметра ComputerName, но для успешного выполнения функции требуется значение. В этом сценарии полезно использовать атрибут параметра Mandatory
.
Синтаксис, используемый в следующем примере, совместим с PowerShell версии 3.0 и выше.
[Parameter(Mandatory=$true)]
можно указать, чтобы сделать функцию совместимой с PowerShell версии 2.0 или более поздней.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$ComputerName
)
Write-Output $ComputerName
}
Теперь, когда требуется ComputerName, если он не указан, функция запрашивает его.
Test-MrParameterValidation
cmdlet Test-MrParameterValidation at command pipeline position 1
Supply values for the following parameters:
ComputerName:
Если вы хотите разрешить несколько значений для параметра ComputerName, используйте тип данных String, но добавьте квадратные скобки ([]
) в тип данных, чтобы разрешить массив строк.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string[]]$ComputerName
)
Write-Output $ComputerName
}
Может потребоваться указать значение по умолчанию для параметра ComputerName, если он не указан.
Проблема заключается в том, что значения по умолчанию нельзя использовать с обязательными параметрами. Вместо этого используйте атрибут проверки параметра ValidateNotNullOrEmpty
со значением по умолчанию.
Даже если задано значение по умолчанию, старайтесь не использовать статические значения. В следующем примере $env:COMPUTERNAME
используется в качестве значения по умолчанию, которое автоматически преобразуется в имя локального компьютера, если значение не указано.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
Write-Output $ComputerName
}
Подробный вывод
Встроенные комментарии полезны, если вы пишете сложный код, но пользователи не видят их, если они не смотрят на код.
Функция в следующем примере содержит встроенный комментарий в цикле foreach
. Хотя этот конкретный комментарий может быть несложным для поиска, представьте, что функция содержит сотни строк кода.
function Test-MrVerboseOutput {
[CmdletBinding()]
param (
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach ($Computer in $ComputerName) {
#Attempting to perform an action on $Computer <<-- Don't use
#inline comments like this, use write verbose instead.
Write-Output $Computer
}
}
Лучше использовать Write-Verbose
вместо встроенных комментариев.
function Test-MrVerboseOutput {
[CmdletBinding()]
param (
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach ($Computer in $ComputerName) {
Write-Verbose -Message "Attempting to perform an action on $Computer"
Write-Output $Computer
}
}
Подробные выходные данные не отображаются при вызове функции без параметра Verbose.
Test-MrVerboseOutput -ComputerName Server01, Server02
Подробные выходные данные отображаются при вызове функции с параметром Verbose.
Test-MrVerboseOutput -ComputerName Server01, Server02 -Verbose
Входные данные конвейера
Дополнительный код необходим, если требуется, чтобы функция принимала входные данные конвейера. Как упоминалось ранее в этой книге, команды могут принимать входные данные из конвейера по значению (по типу) или по имени свойства. Вы можете написать свои функции подобно встроенным командам, чтобы они принимали один или оба этих типа входных данных.
Чтобы принять входные конвейера по значению, укажите атрибут параметра ValueFromPipeline
для данного параметра. Вы можете принимать входные данные конвейера только по значению из одного параметра каждого типа данных. Если у вас есть два параметра, которые принимают строковые входные данные, только один из них может принимать входные данные конвейера по значению. Если вы указали по значению для обоих строковых параметров, входные данные не будут знать, к какой параметру следует привязать. Этот сценарий является еще одной причиной, по которой я называю этот тип входных данных конвейера по типу вместо по значению.
Входные данные конвейера обрабатываются по одному элементу за раз, аналогично тому, как это происходит в цикле foreach
.
Для обработки каждого элемента требуется блок process
, если функция принимает массив в качестве входных данных. Если функция принимает только одно значение в качестве входных данных, process
блок не нужен, но рекомендуется для согласованности.
function Test-MrPipelineInput {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline)]
[string[]]$ComputerName
)
process {
Write-Output $ComputerName
}
}
Прием входных данных конвейера по имени свойства аналогичен, за исключением того, что это указано с использованием атрибута параметра ValueFromPipelineByPropertyName
, и это может быть указано для любого количества параметров независимо от типа данных. Главное — это чтобы выходные данные команды, передаваемые в канал, имели имя свойства, соответствующее имени параметра или его псевдониму вашей функции.
function Test-MrPipelineInput {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
Write-Output $ComputerName
}
}
begin
и end
блоки являются необязательными.
begin
указывается перед блоком process
и используется для выполнения любой начальной работы перед получением элементов из конвейера. Значения, которые передаются через канал, недоступны в блоке begin
. Блок end
указан после блока process
и используется для очистки после обработки всех переданных элементов.
Обработка ошибок
Функция, показанная в следующем примере, создает необработанное исключение, если не удается связаться с компьютером.
function Test-MrErrorHandling {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
foreach ($Computer in $ComputerName) {
Test-WSMan -ComputerName $Computer
}
}
}
В PowerShell существует несколько различных способов обработки ошибок.
Try/Catch
является более современным способом обработки ошибок.
function Test-MrErrorHandling {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
foreach ($Computer in $ComputerName) {
try {
Test-WSMan -ComputerName $Computer
}
catch {
Write-Warning -Message "Unable to connect to Computer: $Computer"
}
}
}
}
Хотя функция, показанная в предыдущем примере, использует обработку ошибок, она создает необработанное исключение, так как команда не создает завершающееся сообщение об ошибке. Перехватываются только завершающие ошибки. Укажите параметр ErrorAction со значением Stop, чтобы превратить непрерывающуюся ошибку в завершающую.
function Test-MrErrorHandling {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
foreach ($Computer in $ComputerName) {
try {
Test-WSMan -ComputerName $Computer -ErrorAction Stop
}
catch {
Write-Warning -Message "Unable to connect to Computer: $Computer"
}
}
}
}
Не изменяйте глобальную $ErrorActionPreference
переменную, если это не обязательно. Если изменить ее в локальной области, она возвращает предыдущее значение при выходе из этой области.
Если вы используете что-то подобное .NET непосредственно из функции PowerShell, вы не можете указать параметр ErrorAction в самой команде. Перед вызовом метода .NET можно изменить переменную $ErrorActionPreference
.
Справка на основе комментариев
Добавление инструкций к функциям считается лучшей практикой. Справка помогает тем, с кем вы делитесь ей, понять, как ими пользоваться.
function Get-MrAutoStoppedService {
<#
.SYNOPSIS
Returns a list of services that are set to start automatically, are not
currently running, excluding the services that are set to delayed start.
.DESCRIPTION
Get-MrAutoStoppedService is a function that returns a list of services
from the specified remote computer(s) that are set to start
automatically, are not currently running, and it excludes the services
that are set to start automatically with a delayed startup.
.PARAMETER ComputerName
The remote computer(s) to check the status of the services on.
.PARAMETER Credential
Specifies a user account that has permission to perform this action. The
default is the current user.
.EXAMPLE
Get-MrAutoStoppedService -ComputerName 'Server1', 'Server2'
.EXAMPLE
'Server1', 'Server2' | Get-MrAutoStoppedService
.EXAMPLE
Get-MrAutoStoppedService -ComputerName 'Server1' -Credential (Get-Credential)
.INPUTS
String
.OUTPUTS
PSCustomObject
.NOTES
Author: Mike F. Robbins
Website: https://mikefrobbins.com
Twitter: @mikefrobbins
#>
[CmdletBinding()]
param (
)
#Function Body
}
При добавлении справки на основе комментариев к функциям можно получить справку для них так же, как и для встроенных команд по умолчанию.
Весь синтаксис для написания функции в PowerShell может показаться подавляющим для кого-то, кто начинает работу. Если вы не можете помнить синтаксис для чего-то, откройте второй экземпляр интегрированной среды сценариев PowerShell (ISE) на отдельном мониторе и просмотрите фрагмент кода "Командлет (расширенная функция) — завершить" при вводе кода для функций. Фрагменты кода можно получить в PowerShell ISE с помощью сочетания клавиш Ctrl + J.
Сводка
В этой главе вы узнали основы написания функций в PowerShell, в том числе как:
- Создание расширенных функций
- Использование проверки параметров
- Использование подробных выходных данных
- Поддержка входа конвейера
- Обработка ошибок
- Создать справку на основе комментариев
Обзор
- Как получить список утвержденных глаголов в PowerShell?
- Как превратить функцию PowerShell в расширенную функцию?
- Когда следует добавить параметры WhatIf и Confirm в функции PowerShell?
- Как превратить не завершающую выполнение ошибку в ошибку, приводящую к завершению?
- Зачем добавлять справочную информацию на основе комментариев в функции?
Ссылки
PowerShell