Всем добрый день.
Речь пойдет о создании своих собственных скриптов на PowerShell. Прежде всего параметризованных скриптов.
Простые PowerShell скрипты
Но для начала разберемся с тем, что такое скрипт. Вот пример:
1 |
Get-ADComputer -Filter * | Where-Object -FilterScript {$_.DistinguishedName -like "*OU=Marketing,*"} | Select-Object -ExpandProperty Name | Out-File c:\test.txt |
Данный код, если его запустить, запишет в файл c:\test.txt список компьютеров AD, находящихся в подразделении Marketing.
Вы можете написать свою команду или последовательность команд сохранить их в текстовом файле с расширением ps1. И этот файл и будет скриптом PowerShell или сценарием PowerShell. После этого в командной строке PowerShell вы можете выполнить данный скрипт и PowerShell выполнит команды, написанные в нем. Однако необходимо помнить про ExecutionPolicy.
Вернемся к нашему примеру, давайте я сохраню его в файле Get-Comp.ps1. Как вы думаете, как часто мне в таком виде придется запускать скрипт? Лично я думаю не часто. Причина простая — в этом коде нет универсальности. Т.е. если мне понадобиться вывести в другой файл компьютеры из другого подразделения, тогда увы я вынужден буду изменить данный скрипт. И поверьте мне эта процедура не так проста как кажется, во-первых, изменяемые данные могут находиться в любой части скрипта и найти их может быть не так просто, во-вторых, обычно, для редактирования сложных скриптов используется специальный редактор PowerShell ISE, который запускается не так быстро как хотелось бы, в-третьих, вам, возможно, каждый раз придется подписывать ваш скрипт заново.
Поэтому вам необходимо выделить в вашем скрипте значения, которые могут изменяться при каждом запуске, и заменить их переменными. Например, так:
1 |
Get-ADComputer -Filter * | Where-Object -FilterScript {$_.DistinguishedName -like "*OU=$OrganizationalUnit,*"} | Select-Object -ExpandProperty Name | Out-File $File |
Конечно, после этого вы можете легко написать что-то вроде этого:
1 2 3 |
$OrganizationalUnit='Marketing' $File='c:\Test.txt' Get-ADComputer -Filter * | Where-Object -FilterScript {$_.DistinguishedName -like "*OU=$OrganizationalUnit,*"} | Select-Object -ExpandProperty Name | Out-File $File |
Однако, это совершенно не избавит вас от необходимости редактировать скрипт перед запуском. И я так делал. И поверьте, моя лень написать две лишних строчки стоила мне очень много времени, которые я потратил на десятки изменений файла. Поэтому, если вы начали так делать, доведите процесс до конца — напишите параметризированный скрипт.
Параметризированный скрипт
Все что нам понадобится это дописать ключевое слово param и поставить запятые между объявлением переменных:
1 2 3 4 5 6 |
Param ( $OrganizationalUnit='Marketing', $File='c:\Test.txt' ) Get-ADComputer -Filter * | Where-Object -FilterScript {$_.DistinguishedName -like "*OU=$OrganizationalUnit,*"} | Select-Object -ExpandProperty Name | Out-File $File |
После этого в командной строке я могу выполнить данный скрипт следующим образом:
1 2 3 4 |
PS E:\> .\Get-Comp.ps1 PS E:\> .\Get-Comp.ps1 -File 'E:\Test.txt' PS E:\> .\Get-Comp.ps1 -OrganizationalUnit 'Sales' PS E:\> .\Get-Comp.ps1 -OrganizationalUnit 'Sales' -File 'E:\Test.txt' |
Первая строчка запишет список компьютеров в подразделении Marketing в файл c:\test.txt. Вторая строчка запишет список компьютеров в подразделении Marketing в файл e:\test.txt. Третья строчка запишет список компьютеров в подразделении Sales в файл c:\test.txt. Четвертая строчка запишет список компьютеров в подразделении Sales в файл e:\test.txt. Другими словами, если параметр не указан, его значение возьмется из указанных в файле.
Проблема в том, что можно и так:
1 |
PS E:\> .\Get-Comp.ps1 -OU 'Sales' -TextFile 'E:\Test.txt' |
В этом случае PowerShell не обнаружит ни каких ошибок, данные параметры проигнорирует, а значения параметров возьмет из файла. Как долго ваш коллега будет думать почему не создался файл e:\test.txt?
Чтобы обеспечить проверку параметров, возможность использовать общие параметры (-Verbose, -ErrorAction) и другие дополнительные возможности по работе с параметрами, необходимо в начало нашего скрипта поставить [CmdletBinding()]
1 2 3 4 5 6 7 |
[CmdletBinding()] Param ( $OrganizationalUnit='Marketing', $File='c:\Test.txt' ) Get-ADComputer -Filter * | Where-Object -FilterScript {$_.DistinguishedName -like "*OU=$OrganizationalUnit,*"} | Select-Object -ExpandProperty Name | Out-File $File |
Теперь пример с неверным указанием параметров не сработает и вернет ошибку.
В общем-то на этом все. Если вы будете использовать функционал описанный выше ваш скрипт можно назвать параметризованным.
Ниже я приведу несколько советов, особо не описывая их.
Несколько полезных советов
1. Указание типа значения параметра. Полезно для контроля ввода:
1 2 3 4 5 |
Param ( [string]$OrganizationalUnit='Marketing', [string]$File='c:\Test.txt', [int]$count=8 ) |
2. Указание какие параметры являются обязательными (нужно их указывать при запуске или нет). По умолчанию все параметры являются опциональными, т.е. их можно не указывать. Если вы указали, что параметр является обязательным, то его значение по умолчанию можно не указывать.
1 2 3 4 5 6 7 |
Param ( [parameter(Mandatory=$true)] [string]$OrganizationalUnit, [string]$File='c:\Test.txt', [parameter(Mandatory=$true)] [int]$Сount=8 ) |
3. Указания сообщения для ввода. Если не указать обязательный параметр при запуске скрипта, то PowerShell предложит его ввести. Можно указать текст приглашения для ввода данного параметра.
1 2 3 4 5 6 7 |
Param ( [parameter(Mandatory=$true,HelpMessage="Enter name of organizational unit")] [string]$OrganizationalUnit, [string]$File='c:\Test.txt', [parameter(Mandatory=$true,HelpMessage="Enter computers count")] [int]$Сount=8 ) |
4. Отключение позиционных параметров. По умолчанию все параметры позиционные, т.е. вы можете не указывать при запуске имя параметра, а просто указать его значение. При этом PowerShell будет определять к какому параметру соотносится каждый аргумент по порядковому номеру аргумента. Например, так:
1 |
PS E:\> .\Get-Comp.ps1 'Sales' 'E:\Test.txt' |
Однако лучше эту возможность выключить и «ручками» указать какие параметры должны быть позиционными и на каком месте должен стоять аргумент для данного параметра.
1 2 3 4 5 6 7 8 |
[CmdletBinding(PositionalBinding=$false)] Param ( [parameter(Mandatory=$true,Position=0)] [string]$OrganizationalUnit, [string]$File='c:\Test.txt', [parameter(Position=1)] [int]$Сount=8 ) |
5. Переключатели. Если вам необходимо использовать параметры типа boolean, то лучше вместо них использовать переключатели. Если параметр указан, то его значение истина, если опущен тогда ложь.
1 2 3 4 5 6 7 |
Param ( [parameter(Mandatory=$true)] [string]$OrganizationalUnit, [string]$File='c:\Test.txt', [parameter(Mandatory=$false)] [Switch]$OnlyEnabled ) |
6. Ввод значений с конвейера. Реализовать ввод значений с конвейера (PipeLine) можно двумя путями через значение (ValueFromPipeline=$true) или через имя свойства (ValueFromPipelineByPropertyName=$true).
1 2 3 4 5 |
Param ( [parameter(Mandatory=$true,ValueFromPipeline=$true)] [string]$OrganizationalUnit, [string]$File='c:\Test.txt' ) |
7. Проверка значений. Мы можем проверять значения параметров. Проверку можно проводить разными способами, я приведу пример, который проверяет число на соответствие диапазону.
1 2 3 4 5 |
Param ( [parameter(Mandatory=$true)] [ValidateRange(0,10)] [Int]$Сount ) |
Дополнительно почитать
1. Get-Help about_Functions_Advanced_Parameters -Online
2. Get-Help about_Functions_CmdletBindingAttribute -Online
Классная статья. Помогла понять некоторые вещи типа CmdletBinding и Position.
Спасибо огромное за статью и ваши советы — действительно дорогого стоит.
Спасибо за статью. Напишите по возможности еще что-нибудь, из практических примеров как упростить написание скрипта.