Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Как и многие другие языки, PowerShell имеет команды для управления потоком выполнения в скриптах. Одной из этих инструкций является оператор switch и в PowerShell, он предлагает функции, которые не найдены на других языках. Сегодня мы подробно рассмотрим работу с PowerShell switch.
Замечание
Оригинальная версия этой статьи появилась в блоге, написанном @KevinMarquette. Команда PowerShell благодарит Кевина за предоставление этого содержимого нам. Пожалуйста, ознакомьтесь с его блогом на PowerShellExplained.com.
Инструкция if
Одной из первых инструкций, которую вы изучаете, является инструкция if. Он позволяет выполнить блок скрипта, если инструкция имеет значение $true.
if ( Test-Path $Path )
{
Remove-Item $Path
}
Вы можете задействовать гораздо более сложную логику, используя операторы elseif и else. Ниже приведен пример, в котором у меня есть числовое значение для дня недели, и я хочу получить имя в виде строки.
$day = 3
if ( $day -eq 0 ) { $result = 'Sunday' }
elseif ( $day -eq 1 ) { $result = 'Monday' }
elseif ( $day -eq 2 ) { $result = 'Tuesday' }
elseif ( $day -eq 3 ) { $result = 'Wednesday' }
elseif ( $day -eq 4 ) { $result = 'Thursday' }
elseif ( $day -eq 5 ) { $result = 'Friday' }
elseif ( $day -eq 6 ) { $result = 'Saturday' }
$result
Wednesday
Оказывается, что это общий шаблон, и есть много способов справиться с этим. Один из них с switch.
Оператор «switch»
Инструкция switch позволяет предоставить переменную и список возможных значений. Если значение соответствует переменной, выполняется его блок инструкций.
$day = 3
switch ( $day )
{
0 { $result = 'Sunday' }
1 { $result = 'Monday' }
2 { $result = 'Tuesday' }
3 { $result = 'Wednesday' }
4 { $result = 'Thursday' }
5 { $result = 'Friday' }
6 { $result = 'Saturday' }
}
$result
'Wednesday'
В этом примере значение $day соответствует одному из числовых значений, затем $result присваивается правильное имя. Мы делаем только назначение переменной в этом примере, но в этих блоках скриптов можно выполнить любую powerShell.
Присваивание значения переменной
Мы можем написать последний пример другим способом.
$result = switch ( $day )
{
0 { 'Sunday' }
1 { 'Monday' }
2 { 'Tuesday' }
3 { 'Wednesday' }
4 { 'Thursday' }
5 { 'Friday' }
6 { 'Saturday' }
}
Мы помещаем значение в конвейер PowerShell и присваиваем ему значение $result. Это же можно сделать с помощью инструкций if и foreach.
По умолчанию
Мы можем использовать ключевое default слово для определения того, что должно произойти, если совпадения нет.
$result = switch ( $day )
{
0 { 'Sunday' }
# ...
6 { 'Saturday' }
default { 'Unknown' }
}
Здесь мы возвращаем значение Unknown в случае по умолчанию.
Строки
В последних примерах я сопоставлял числа, но вы также можете сопоставить строки.
$item = 'Role'
switch ( $item )
{
Component
{
'is a component'
}
Role
{
'is a role'
}
Location
{
'is a location'
}
}
is a role
Я решил не обертывать Component,Role и Location совпадения в кавычках здесь, чтобы подчеркнуть, что они необязательные.
switch рассматривает их как строку в большинстве случаев.
Массивы
Одной из крутых функций PowerShell switch является способ обработки массивов. Если вы предоставляете switch массив, он обрабатывает каждый элемент в этой коллекции.
$roles = @('WEB','Database')
switch ( $roles ) {
'Database' { 'Configure SQL' }
'WEB' { 'Configure IIS' }
'FileServer' { 'Configure Share' }
}
Configure IIS
Configure SQL
Если у вас есть повторяющиеся элементы в массиве, они сопоставляются несколько раз соответствующим разделом.
PSItem
Можно использовать $PSItem или $_, чтобы ссылаться на текущий элемент, который был обработан. Когда мы делаем простое совпадение, $PSItem — это значение, которое мы ищем. Я буду выполнять некоторые сложные сопоставления в следующем разделе, где эта переменная используется.
Параметры
Уникальная функция PowerShell switch заключается в том, что он имеет ряд параметров коммутатора, которые изменяют способ его выполнения.
-CaseSensitive
Совпадения по умолчанию не учитывает регистр. Если вам нужно учитывать регистр, можно использовать -CaseSensitive. Это можно использовать в сочетании с другими параметрами коммутатора.
-Подстановочный знак
Мы можем включить поддержку подстановочных знаков с помощью коммутатора -Wildcard . В данном случае используется та же логика подстановочных знаков, что и оператор -like, для выполнения каждого совпадения.
$Message = 'Warning, out of disk space'
switch -Wildcard ( $message )
{
'Error*'
{
Write-Error -Message $Message
}
'Warning*'
{
Write-Warning -Message $Message
}
default
{
Write-Information $message
}
}
WARNING: Warning, out of disk space
Здесь мы обрабатываем сообщение, а затем выводим его в разных потоках на основе содержимого.
-Regex
Оператор switch поддерживает совпадения регулярных выражений так же, как поддерживает и подстановочные знаки.
switch -Regex ( $message )
{
'^Error'
{
Write-Error -Message $Message
}
'^Warning'
{
Write-Warning -Message $Message
}
default
{
Write-Information $message
}
}
У меня есть больше примеров использования regex в другой статье я написал: Многие способы использования regex.
-File
Малоизвестная функция инструкции switch заключается в том, что он может обрабатывать файл с параметром -File . Вы используете -File путь к файлу вместо предоставления ему выражения переменной.
switch -Wildcard -File $path
{
'Error*'
{
Write-Error -Message $PSItem
}
'Warning*'
{
Write-Warning -Message $PSItem
}
default
{
Write-Output $PSItem
}
}
Он работает подобно обработке массивов. В этом примере я сочетаю его с подстановочными знаками и использую $PSItem. Этот процесс будет обрабатывать файл журнала и преобразовывать его в предупреждения и сообщения об ошибках в зависимости от совпадений с регулярными выражениями.
Дополнительные сведения
Теперь, когда вы знаете обо всех этих документированных функциях, мы можем использовать их в контексте более расширенной обработки.
Expressions
switch может быть не переменной, а выражением.
switch ( ( Get-Service | where Status -EQ 'running' ).Name ) {...}
Значение, к которому сводится выражение, используется для сопоставления.
Несколько совпадений
Возможно, вы уже заметили, но switch может соответствовать нескольким условиям. Это особенно верно при использовании совпадений -Wildcard или -Regex. Вы можете добавить одно и то же условие несколько раз, и все активируются.
switch ( 'Word' )
{
'word' { 'lower case word match' }
'Word' { 'mixed case word match' }
'WORD' { 'upper case word match' }
}
lower case word match
mixed case word match
upper case word match
Все три из этих заявлений уволены. Это показывает, что каждое условие проверяется по порядку. Это относится к массивам обработки, где каждый элемент проверяет каждое условие.
Продолжить
Как правило, это то место, где я бы представил оператор break, но лучше сначала узнать, как использовать continue. Как и цикл foreach , continue продолжает переход к следующему элементу в коллекции или завершает работу switch , если нет дополнительных элементов. Мы можем переписать последний пример с инструкциями continue, чтобы выполнить только одну инструкцию.
switch ( 'Word' )
{
'word'
{
'lower case word match'
continue
}
'Word'
{
'mixed case word match'
continue
}
'WORD'
{
'upper case word match'
continue
}
}
lower case word match
Вместо сопоставления всех трех элементов, первый элемент совпадает, и переключение продолжается к следующему значению. Так как для обработки не осталось никаких значений, переключатель завершает работу. В следующем примере показано, как подстановочный знак может соответствовать нескольким элементам.
switch -Wildcard -File $path
{
'*Error*'
{
Write-Error -Message $PSItem
continue
}
'*Warning*'
{
Write-Warning -Message $PSItem
continue
}
default
{
Write-Output $PSItem
}
}
Так как строка в входном файле может содержать слово Error и Warning, мы хотим, чтобы первый выполнялся, а затем продолжить обработку файла.
Перерыв
Оператор break завершает выполнение switch-конструкции. Это то же поведение, которое continue представляет для отдельных значений. Разница видна при обработке массива.
break останавливает всю обработку в коммутаторе и continue переходит к следующему элементу.
$Messages = @(
'Downloading update'
'Ran into errors downloading file'
'Error: out of disk space'
'Sending email'
'...'
)
switch -Wildcard ($Messages)
{
'Error*'
{
Write-Error -Message $PSItem
break
}
'*Error*'
{
Write-Warning -Message $PSItem
continue
}
'*Warning*'
{
Write-Warning -Message $PSItem
continue
}
default
{
Write-Output $PSItem
}
}
Downloading update
WARNING: Ran into errors downloading file
Write-Error -Message $PSItem : Error: out of disk space
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException
В этом случае, если мы ударим по любым строкам, начинающимся с Error , мы получаем ошибку и переключение останавливается.
Это то, break что это заявление делает для нас. Если мы находим Error не только в начале строки, но и внутри неё, мы напишем его как предупреждение. Мы делаем то же самое для Warning. Возможно, что строка может иметь как слово Error , так и Warning, но нам нужно только один для обработки. Это то, что continue заявление делает для нас.
Разрыв меток
Инструкция switch поддерживает break/continue метки так же, как foreach.
:filelist foreach($path in $logs)
{
:logFile switch -Wildcard -File $path
{
'Error*'
{
Write-Error -Message $PSItem
break filelist
}
'Warning*'
{
Write-Error -Message $PSItem
break logFile
}
default
{
Write-Output $PSItem
}
}
}
Лично я не люблю использовать метки перерывов, но хотел бы указать на них, потому что они могут сбить с толку, если вы никогда не видели их раньше. При наличии нескольких вложенных операторов switch , который может быть целевым объектом вашей break.
Enum
PowerShell 5.0 дал нам перечисления, и мы можем использовать их в коммутаторе.
enum Context {
Component
Role
Location
}
$item = [Context]::Role
switch ( $item )
{
Component
{
'is a component'
}
Role
{
'is a role'
}
Location
{
'is a location'
}
}
is a role
Если вы хотите сохранить все как строго типизированные перечисления, то их можно поместить в скобки.
switch ($item )
{
([Context]::Component)
{
'is a component'
}
([Context]::Role)
{
'is a role'
}
([Context]::Location)
{
'is a location'
}
}
Круглые скобки необходимы здесь, чтобы переключатель не рассматривал значение [Context]::Location как литеральную строку.
ScriptBlock
При необходимости можно использовать блок скрипта для выполнения оценки соответствия.
$age = 37
switch ( $age )
{
{$PSItem -le 18}
{
'child'
}
{$PSItem -gt 18}
{
'adult'
}
}
'adult'
Это повышает сложность и может сделать switch сложно читать. В большинстве случаев, когда вы будете использовать что-то подобное, лучше использовать if и elseif инструкции. Я бы рассмотрел использование этого, если бы у меня уже был большой коммутатор, и мне нужно было, чтобы два элемента попадали в один и тот же оценочный блок.
Одно, что я думаю, помогает с удобочитаемостью — разместить блок скрипта в скобках.
switch ( $age )
{
({$PSItem -le 18})
{
'child'
}
({$PSItem -gt 18})
{
'adult'
}
}
Он по-прежнему выполняется так же и дает лучший визуальный разрыв при быстром просмотре.
Regex $Matches
Нам нужно пересмотреть регрессию, чтобы коснуться чего-то, что не сразу очевидно. Использование регулярных выражений заполняет переменную $Matches. Я углубляюсь в использование $Matches подробнее, когда я рассказываю о различных способах использования regex. Ниже приведен краткий пример отображения в действии с именованными совпадениями.
$message = 'my ssn is 123-23-3456 and credit card: 1234-5678-1234-5678'
switch -Regex ($message)
{
'(?<SSN>\d\d\d-\d\d-\d\d\d\d)'
{
Write-Warning "message contains a SSN: $($Matches.SSN)"
}
'(?<CC>\d\d\d\d-\d\d\d\d-\d\d\d\d-\d\d\d\d)'
{
Write-Warning "message contains a credit card number: $($Matches.CC)"
}
'(?<Phone>\d\d\d-\d\d\d-\d\d\d\d)'
{
Write-Warning "message contains a phone number: $($Matches.Phone)"
}
}
WARNING: message may contain a SSN: 123-23-3456
WARNING: message may contain a credit card number: 1234-5678-1234-5678
$null
Можно установить $null значение, которое не должно быть значением по умолчанию.
$values = '', 5, $null
switch ( $values )
{
$null { "Value '$_' is `$null" }
{ '' -eq $_ } { "Value '$_' is an empty string" }
default { "Value [$_] isn't an empty string or `$null" }
}
Value '' is an empty string
Value [5] isn't an empty string or $null
Value '' is $null
При тестировании пустой строки в switch операторе важно использовать оператор сравнения, как показано в этом примере вместо необработанного значения ''. В выражении switch необработанное значение '' совпадает также с $null. Рассмотрим пример.
$values = '', 5, $null
switch ( $values )
{
$null { "Value '$_' is `$null" }
'' { "Value '$_' is an empty string" }
default { "Value [$_] isn't an empty string or `$null" }
}
Value '' is an empty string
Value [5] isn't an empty string or $null
Value '' is $null
Value '' is an empty string
Кроме того, будьте осторожны с пустыми возвратами из командлетов. Командлеты или конвейеры, у которых нет выходных данных, обрабатываются как пустой массив, который не соответствует ничему, включая default сценарий.
$file = Get-ChildItem NonExistantFile*
switch ( $file )
{
$null { '$file is $null' }
default { "`$file is type $($file.GetType().Name)" }
}
# No matches
Константное выражение
Ли Дайли отметил, что для оценки $true элементов можно использовать константное [bool] выражение.
Представьте, что у нас есть несколько логических проверок, которые должны быть выполнены.
$isVisible = $false
$isEnabled = $true
$isSecure = $true
switch ( $true )
{
$isEnabled
{
'Do-Action'
}
$isVisible
{
'Show-Animation'
}
$isSecure
{
'Enable-AdminMenu'
}
}
Do-Action
Enabled-AdminMenu
Это понятный способ оценить и принять меры по состоянию нескольких булевых полей. Прелесть в том, что вы можете иметь одно совпадение, чтобы изменить состояние не оценённого значения.
$isVisible = $false
$isEnabled = $true
$isAdmin = $false
switch ( $true )
{
$isEnabled
{
'Do-Action'
$isVisible = $true
}
$isVisible
{
'Show-Animation'
}
$isAdmin
{
'Enable-AdminMenu'
}
}
Do-Action
Show-Animation
В этом примере установка $isEnabled равным $true гарантирует, что $isVisible также будет установлен как $true.
Затем, когда $isVisible оценивается, вызывается его блок инструкций. Это немного контринтуитивно, но это умное использование механики.
$switch автоматическая переменная
Когда switch обрабатывает свои значения, он создает перечислитель и называет его $switch. Это автоматическая переменная, созданная PowerShell, и ее можно управлять напрямую.
$a = 1, 2, 3, 4
switch($a) {
1 { [void]$switch.MoveNext(); $switch.Current }
3 { [void]$switch.MoveNext(); $switch.Current }
}
Это дает результаты:
2
4
Перемещение перечислителя вперед приводит к тому, что следующий элемент не обрабатывается switch, но вы можете получить это значение напрямую. Я бы назвал это безумием.
Другие шаблоны
Хеш-таблицы
Одним из моих самых популярных постов был тот, который я сделал на хеш-таблицы. Одним из вариантов использования hashtable является таблица подстановки. Это альтернативный подход к общему шаблону, на который часто указывает оператор switch.
$day = 3
$lookup = @{
0 = 'Sunday'
1 = 'Monday'
2 = 'Tuesday'
3 = 'Wednesday'
4 = 'Thursday'
5 = 'Friday'
6 = 'Saturday'
}
$lookup[$day]
Wednesday
Если я использую switch только для поиска, я часто использую hashtable вместо этого.
Enum
PowerShell 5.0 представила этот enum параметр, и это также вариант в данном случае.
$day = 3
enum DayOfTheWeek {
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
}
[DayOfTheWeek]$day
Wednesday
Мы могли бы потратить целый день на изучение различных способов решения этой проблемы. Я просто хотел убедиться, что вы знали, что у вас есть варианты.
Окончательные слова
Оператор switch на первый взгляд прост, но он предлагает некоторые расширенные функции, которые большинство людей не осознают, что они доступны. Объединение этих возможностей делает её мощной. Надеюсь, вы узнали что-то, что вы раньше не поняли.
PowerShell