Вы можете задействовать сценарии для автоматизации всех процессов управления системой SQL Server, поскольку это позволяет убедиться, что задачи выполняются стабильно и с приложением минимальных усилий. И в самом деле, ведь не хотите же вы использовать графический интерфейс среды SQL Server Management Studio, SSMS, для резервного копирования содержимого всех баз данных своей сети — или я ошибаюсь?
Конечно, вы можете включать в текст сценария имена конкретных серверов и баз данных, которыми хотите управлять, но такое решение резко ограничит полезность данного сценария. Передавая сценариям аргументы командной строки, вы получаете возможность использовать один и тот же сценарий при работе с множеством серверов и баз данных — и знать при этом, что сценарий будет функционировать безупречно.
В большинстве сценариев, с которыми мне приходится иметь дело, я передаю имя сервера (точнее сказать, экземпляра SQL Server, но поскольку при работе с интерфейсом SQL Server Management Objects (SMO) полагается оперировать объектом Server, я использую термин «server» для обозначения этого экземпляра). Блок param я размещаю на месте первого исполняемого модуля, который PowerShell видит в моем сценарии, и поэтому оболочка воспринимает переменные, определяемые мною, как аргументы командной строки, так что в моих сценариях я определяю сервер, к которому необходимо подключиться, следующим образом:
param { [string] $inst = $null }
Итак, если мой сценарий называется Get-DatabaseOptions.ps1, я запущу его для работы с сервером WS12SQL01, используя при этом следующий вызов из командной строки:
PS C:\Demos>./Get-DatabaseOptions.ps1 WS12SQL01
Таким образом, в начале выполнения сценария переменной $inst будет присвоено значение 'WS12SQL01', и это значение будет использоваться с целью подключения к данному серверу для его обработки. Этот механизм работает безупречно, но у нас нет гарантии, что вызывающая программа передаст требуемый параметр, а если установить первоначальное значение равным NULL, оно будет использоваться в случаях, когда параметр не указан, что приведет к ошибке.
А что произойдет, если вы передадите программе второй аргумент? Ответ — по умолчанию PowerShell возвратит дополнительные аргументы в коллекции $args, и вы при желании сможете использовать их в своем сценарии.
PS C:\Demos>. \Get-DatabaseOptions.ps1 WS12SQL01 AdventureWorks $inst = WS12SQL01 $args = AdventureWorks
Поскольку согласно моему замыслу сценарий должен был принимать только один аргумент, я решил использовать директиву CmdletBinding(), которая сообщает оболочке PowerShell, что допускается применение только заданных аргументов (наряду с прочими объектами, описание которых выходит за рамки данной статьи).
CmdletBinding()] param ( [string] $inst = $null )
А если я попытаюсь включить параметр AdventureWorks, PowerShell выдаст сообщение об ошибке, как на рисунке 1.
Рисунок 1. Сообщение об ошибке |
Непосредственно перед определением переменной $inst я могу добавить следующую строку, в которой будет содержаться запрос на ввод аргумента:
[Parameter(Mandatory=$true)]
Теперь сценарий не будет выполняться до тех пор, пока я не введу аргумент. И действительно, программа возвращает следующее сообщение:
cmdlet Get-DatabaseOptions.ps1 at command pipeline position 1
Supply values for the following parameters:
inst:
Сценарий дает пользователю возможность ввести параметр и затем продолжает выполняться с использованием предложенного значения.
Важно отметить, что, хотя я сосредоточил внимание на реакции блока param на аргументы командной строки, эта реакция остается аналогичной и для функций.
Одно из полезных свойств команд PowerShell состоит в возможности включения в ту или иную команду аргумента -WhatIf. В результате оболочка оценивает команду и ее аргументы, как если бы она была запущена на выполнение, однако фактически эта команда не выполняется. Приведу пример. Запустив команду
Get-Service | Stop-Service
мы можем получить нежелательные результаты, но если мы дополним эту команду аргументом -WhatIf, то сможем увидеть, каковы будут эти результаты, причем далее PowerShell подготовит отчет о каждой службе и сообщит нам, что действие этой службы приостанавливается, см. рисунок 2.
Рисунок 2. Состояние служб |
Еще одна возможность, реализованная в директиве CmdletBinding(), позволяет пользователю включить ту же возможность в свой сценарий. Чтобы активировать эту функцию, введите в скобки директивы CmdletBinding() параметр SupportsShouldProcess=$True. Встроенная переменная $PSCmdlet содержит сведения о стеке вызовов и наделена свойством ShouldProcess, значение которого можно проверить. Если данному свойству назначено значение $False, в дело вступает аргумент -WhatIf, и вы можете не допустить фактического выполнения определенных в сценарии действий. Поскольку код, в котором условия имеют значение $True, представляется более ясным, наш код будет выглядеть следующим образом:
[CmdletBinding(SupportsShouldProcess=$True)] param ( [Parameter(Mandatory=$true)] [string] $inst = $null ) if ($PSCmdlet.ShouldProcess(«$inst»,«Return Database Options»)) { write-output «Do stuff on server `$inst = $inst» }
Если мы включим в команду аргумент -Whatif, PowerShell возвратит следующие данные.
What if: Performing operation «Return Database Options» on Target «WS12SQL01».
Если аргумент -Whatif не используется, мы получаем обычные результаты обработки.
PS C:\Demos>. \Get-DatabaseOptions.ps1 WS12SQL01 Do stuff on server $inst = WS12SQL01
Давайте рассмотрим ситуацию чуть более подробно. Предположим, что вы, как правило, выполняете свои сценарии по очереди на ряде серверов. Я упоминал о возможностях конвейера. Отмечу, что вы можете воспользоваться им для дальнейшей автоматизации процессов. PowerShell обеспечивает возможность выполнения процессов инициализации, итеративных процессов и процессов упаковки с помощью блоков Begin {}, Process {} и End {}. Блок Begin {} выполняется однократно, блок Process {} выполняется по одному разу для каждого переданного по конвейеру значения, а блок End {} — однократно в ситуации, когда конвейер пуст. Пользователю нет нужды производить какие-либо операции в начале или в конце конвейера, но вы можете с помощью блока Process {} активировать упомянутую функцию и, получив набор серверов из конвейера, выполнить свой сценарий применительно к каждому из них.
Прежде всего, вам следует дополнить блок param еще одним компонентом — аргументом ValueFromPipeline. Дополнение вносится непосредственно в раздел определения Parameter.
param ( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [string] $inst = $null )
Теперь введите логику в блок сценария Process {} — и можете приступать к работе.
Process { if ($PSCmdlet.ShouldProcess(«$inst»,«Return Database Options»)) { foreach ($svr in $inst){ write-output «Do stuff on server `$inst = $inst» } } }
Вы можете запускать сценарий так же, как и раньше.
PS C:\Demos>. \Get-DatabaseOptions.ps1 WS12SQL01 Do stuff on server $inst = WS12SQL01
Но если вы создадите коллекцию имен серверов, можете по конвейеру передать их сценарию, и он корректно обработает серверы и в этом случае, см. рисунок 3.
Рисунок 3. Обработка нескольких серверов |
Сценарии, которые вы можете с легкостью составлять в среде PowerShell, помогут вам быстро решать ваши задачи. Но когда вы наберетесь опыта и захотите, чтобы с вашими сценариями могли работать коллеги, воспользуйтесь возможностями реализованных в PowerShell аргументов командной строки. Тем самым вы окажете неоценимую услугу сотрудникам, которые будут работать с вашими сценариями.