Глава 2. Введение в PowerShell 7
Содержание
- Глава 2. Введение в PowerShell 7
Данная глава рассматривает следующие рецепты:
-
Изучение новых операторов
-
Изучение параллельной обработки при помощи
ForEach-Object
-
Изучение улучшений в
ForEach
иForEach-Object
. -
Улучшения в
Test-Connection
-
Применение
Select-String
-
Изучение просмотра ошибок и
Get-Error
-
Изучение экспериментальных функциональных возможностей
В Главе 1, Установка и конфигурирование PowerShell 7 вы установили и настроили PowerShell 7, причём совместно с VS Code и новым шрифтом. В этой главе мы взглянем на PowerShell 7 и то чем он отличается от Windows PowerShell. Все рецепты в этой главе иллюстрируют некоторые из наиболее важных функциональных возможностей, которые поступают с PowerShell 7.
Теперь, когда PowerShell является кросс- платформенным, он обладает новой, расширенной аудиторией, которая обладает за плечами оболочками Linux, такими как Bash. В PowerShell 7 команда PowerShell добавила некоторые новые операторы, которые улучшили соответствие с прочими оболочками и сделали жизнь для ИТ профессионалов чуточку проще.
После перехода на открытый исходный код сам код PowerShell стал открытым для инспекции его сообществом. Многие талантливые разработчики
получили возможность выполнять улучшения для производительности и функциональности. Одним из примеров является то, как PowerShell выполняет
итерации при помощи ForEach
и ForEach-Object
. В Windows PowerShell
имевшийся синтаксис элемента ForEach
и команды ForEach-Object
позволял вам обрабатывать коллекции объектов. В Windows PowerShell каждая итерация по некой коллекции была последовательной, что могло иметь
результатом длинные времена исполнения сценария. PowerShell 7 ввёл в команду ForEach-Object
некое улучшение,
которое сделало для вас возможным параллельное выполнение итераций. Такой пересмотр привёл к снижению общих накладных расходов применения таких
популярных функциональных возможностей, тем самым ускоряя производительность сценариев.
Другим улучшением является пересмотр Test-Connection
, команды, которую вы применяете для проверки сетевого
соединения с удалённой системой. В PowerShell 7 Test-Connection
не только делает больше, но и быстрее чем
в Windows PowerShell.
Отчёты об ошибках в Windows PowerShell были исключительными: ясные и обычно имеющими практическое применение сообщения об ошибках с подробностями того где именно произошла ошибка. В PowerShell 7 вы теперь получаете, по умолчанию, лаконичное представление о некой ошибке без всего дополнительного текста, который часто не имел ценности. Как и всегда, вы можете вернуться к меньшей краткости, если пожелаете. В рецепте Изучение представления ошибки и Get-Error вы обнаружите насколько лучше в PowerShell 7 стали отчёты об ошибках (по сравнению с Windows PowerShell).
В заключительном рецепте этой главы мы взглянем на некоторые экспериментальные функциональные возможности, которые можно разрешить в PowerShell 7.
Операторы являются символами или сочетаниями нажатий клавиш, которые PowerShell распознаёт и придаёт им некое значение. PowerShell
применяет оператор +
имея в виду добавление, причём либо арифметическое сложение, либо добавление/
конкатенацию строк. Большинство популярных операторов было определено в Windows PowerShell v1.
PowerShell 7 реализовал некоторые новые операторы, включая следующие:
-
Операторы цепочек конвейеров:
||
и&&
-
Оператор объединения с null:
??
-
Присваивающий оператор при условии null:
??=
-
Экспериментальные операторы доступа к участнику при условии null:
?.
и?[]
-
Оператор обработки в фоновом режиме:
&
-
Трёхместный (тернарный) оператор:
? <if-true> : <if-false>
-
:
&
В данном рецепте вы рассмотрите образцы таких операторов.
Этот рецепт пользуется SRV1
, хостом Windows Server 2022.
Вы установили и настроили PowerShell 7 и VS Code. Все последующие рецепты данной книги вы исполняете либо в консоли PowerShell 7, либо в VS Code.
-
Воспользуемся PowerShell 7 для традиционной проверки результатов
Write-Output 'Something that succeeds' if ($?) {Write-Output 'It worked'}
-
Проверим результаты при помощи оператора конвейера
&&
Write-Output 'Something that succeeds' && Write-Output 'It worked'
-
Воспользуемся цепочным оператором конвейера
||
Write-Output 'Something that succeeds' || Write-Output 'You do not see this message'
-
Определим простую функцию
function Install-CascadiaPLFont{ Write-Host 'Installing Cascadia PL font...' }
-
Воспользуемся оператором
||
:$OldErrorAction = $ErrorActionPreference $ErrorActionPreference = 'SilentlyContinue' Get-ChildItem -Path C:\FOO\CASCADIAPL.TTF || Install-CascadiaPLFont $ErrorActionPreference = $OldErrorAction
-
Создадим функцию для обработки
null
:Function Test-NCO { if ($args -eq '42') { Return 'Test-NCO returned a result' } }
-
Проверим результаты
null
традиционно:$Result1 = Test-NCO # no parameter if ($null -eq $Result1) { 'Function returned no value' } else { $Result1 } $Result2 = Test-NCO 42 # using a parameter if ($null -eq $Result2) { 'Function returned no value' } else { $Result2 }
-
Проверим применение оператора объединения с null
??
:$Result3 = Test-NCO $Result3 ?? 'Function returned no value' $Result4 = Test-NCO 42 $Result4 ?? 'This is not output, but result is'
-
Продемонстрируем null оператор присваивания по условию null:
$Result5 = Test-NCO $Result5 ?? 'Result is null' $Result5 ??= Test-NCO 42 $Result5
-
Традиционное исполнение метода с объектом
null
:$BitService.Stop()
-
Применение оператора с условием null:
${BitService}?.Stop()
-
Проверка доступа к имени свойства null:
$x = $null ${x}?.Propname $x = @{Propname=42} ${x}?.Propname
-
Проверка доступа к участнику массива объекта null:
$y = $null ${y}?[0] $y = 1,2,3 ${y}?[0]
-
Применение оператора фоновой обработки
&
:Get-CimClass -ClassName Win32_Bios &
-
Ожидание завершения выполнения задания:
$JobId = (Get-Job | Select-Object -Last 1).Id Wait-Job -id $JobId
-
Просмотр вывода
$Results = Receive-Job -Id $JobId $Results | Format-Table
-
Создание объекта без помощи тернарного оператора:
$A = 42; $B = (42,4242) | Get-Random $RandomTest = ($true, $false) | Get-Random if ($A -eq $B) { $Property1 = $true } else { $Property1 = $false } if ($RandomTest) { $Property2 = "Hello" } else { $Property2 = "Goodbye" } [PSCustomObject]@{ "Property1" = $Property1 "Property2" = $Property2 }
-
Создание объекта с применением тернарного оператора:
[PSCustomObject]@{ "Property1" = (($A -eq $B) ? $true : $false) "Property2" = (($RandomTest) ? "Hello" : "Goodbye") }
На Шаге 1 вы осуществляете вывод, который успешно завершается. Затем вы проверяете значение
$?
чтобы убедиться что предыдущий шаг был выполнен успешно. Вот что мы видим:
На Шаге 2 вы пользуетесь оператором &&
чтобы проверить, что предыдущая команда завершилась без ошибок. Вот как это выглядит:
Оператор цепочки, ||
сообщает PowerShell необходимость выполнения команды после данного оператора,
если предыдущая команда отказала (по действию, это противоположно &&
).
На Шаге 3 мы видим этот оператор в действии с выводом, подобным такому:
На Шаге 4 вы определяете некую функцию. Определение функции не производит вывод. Данная функция пишет вывод для имитации установки шрифта PL Cascadia Code.
На Шаге 5 вы проверяете имеется ли необходимый шрифт TTF и, если нет, вы вызываете функцию
Install-CascadiaPLFont
для имитации установки этого шрифта. Объединяя в конвейер вывод
Get-ChildItem
с Out-Null
, вы избегаете реального вывода от
Get-ChildItem
и, когда необходимый файл отсутствует, вы можете вызвать функцию
Install-CascadiaPLFont
. Вывод этого фрагмента кода выглядит следующим образом:
Для иллюстрации обработки результатов null из функции, на Шаге 6 вы создаёте функцию, которая либо ничего не возвращает (когда вы вызываете функцию без параметров), или некое строковое значение (если вы вызываете её с определённым параметром). Данная функция иллюстрирует как вы можете обрабатывать некую функцию, возвращающую null. Этот шаг не производит вывода.
На Шаге 7 вы иллюстрируете традиционную обработку возвращающей null функции. Вы вызываете эту функцию, причём сначала без параметра, что приводит возврату без результата, а затем со значением, что и приводит к возврату значения. Затем вы проверяете вернула ли функция значение в каждом из случаев, что выглядит так:
Когда вы пользуетесь оператором соединения с null (??
) между двумя операндами, этот оператор
возвращает значение операнда слева если оно не null
; в противном случае он вычисляет операнд справа и
возвращает его результат. На Шаге 8 вы вызываете функцию Test-NCO
и проверяете будет ли функция возвращать некое значение, что выглядит следующим образом:
Для присвоения переменной некого значения, когда эта переменная в настоящее время null
, вы пользуетесь
оператором присвоения по условию null, ??=
, что вы наблюдаете на Шаге 9,
вывод которого таков:
Одна из распространённых проблем, часто возникающая в различных форумах поддержки PowerShell, это когда вы пытаетесь активировать некий
метод у объекта, которым выступает null
. Вы можете воспользоваться неким выражением или командой чтобы
попытаться вернуть некое значение (например, всех пользователей AD из офиса Marin County), а это возвращает значение
null
. На Шаге 10 вы активируете метод
Stop()
в объекте $BitService
. Поскольку вы пока не назначили
значения $BitService
, вы наблюдаете соответствующий результат (ошибку, вы не можете
вызвать метод
в выражении со значением null
). Наш традиционный
метод отображает ошибку подобно следующей:
Применяя оператор с условием null вы можете выполнять метод Stop()
, когда переменная
$BitService
не null, но пропускать вызов этого метода, если переменная null. По существу, то что вы
делаете на Шаге 11, так это вызываете метод Stop()
если
соответствующая переменная не null и не делаете ничего в противном случае. Поскольку эта переменная ещё не обладает значением, данный шаг
не делает ничего (и не производит никакого вывода).
Когда переменная null, будь то по причине некой ошибки в вашем сценарии, или потому что команда возвращает null вместо некого реального значения, доступ к имени свойства также может вызывать ошибки. Вывод на Шаге 12 выглядит следующим образом:
Вы также можете сталкиваться с проблемами нулевых объектов когда вы пытаетесь получить доступ к некому участнику массива или объекта, который может существовать, а может и нет. На Шаге 13 вы пробуете получить доступ к некому участнику массива в не существующем массиве, за которым следует имеющийся массив. Вывод данного шага выглядит так:
На Шаге 14 вы изучаете применение оператора фонового процесса,
&
. Основная мысль состоит в том, что вы добавляете этот символ в конец некой команды или сценария и
PowerShell исполняет такой код в фоновом режиме. Вот вывод данного шага:
На Шаге 15 вы дожидаетесь завершения того задания, которое вы создали на Шаге 14, что выглядит следующим образом:
После завершения этого задания, на Шаге 16 вы получаете и отображаете вывод самого задания, который отображается так:
На Шаге 17 вы создаёте некий объект при помощи традиционного подхода. Данный шаг создаёт случайные значения для двух свойств. Затем вы создаёте объект применяя полученные значения для этих двух свойств. Вот вывод этого шага:
На Шаге 18 вы пользуетесь новым трёхместным (тернарным) оператором PowerShell 7. Этот оператор проверяет условие и исполняет различный код в зависимости от того истинный или ложный полученный результат. Это очень похоже на то, что вы наблюдали на Шаге 17, но требует гораздо меньшего числа строк. Вот как выглядит вывод этого шага:
На Шаге 4 создаваемая вами функция имитирует установку необходимого шрифта, если такой шрифт отсутствует.
Сам файл шрифта, CASCADIAPL.TTF
это TrueType файл шрифта для шрифта Powerline Cascadia Code. Для
получения дополнительных сведений по этому шрифту, обращайтесь к https://www.hanselman.com/blog/PatchingTheNewCascadiaCodeToIncludePowerlineGlyphsAndOtherNerdFontsForTheWindowsTerminal.aspx.
На Шаге 5 вы имитируете установку необходимого шрифта если он отсутствует. Когда вы проверяете
имеется ли в настоящее время установленным соответствующий файл TTF, настройка по умолчанию для $ErrorActionPreference
(Continue
) означает, что вы наблюдаете сообщение об ошибке если такой файл отсутствует. По умолчанию,
когда Get-ChildItem
выполняет проверку того имеется ли такой файл, он вырабатывает сообщение об ошибке
в случае отсутствия файла. Одним из подходов для избежания такого сообщения об ошибке выступает настройка
$ErrorActionPreference
в SilentlyContinue
и, после того как вы
убедились что файл шрифта теперь имеется, установка обратно устанавливаемого по умолчанию значения. Синтаксис слегка путанный, если вы не знакомы
с ним; это может быть ещё одним случаем, когда отказ от таких операторов и применение вместо них подхода Windows PowerShell может упростить чтение
и понимание сценария.
На Шаге 11 и на Шаге 12 вы выполняете попытку осуществления доступа
к свойству из некого объекта. Основное предположение здесь состоит в том, что вы желаете активировать соответствующий метод или доступ к значению
свойства только когда такой объект имеется и не заботиться ни о чём в противном случае. Таким образом, если $BitService
обладает неким свойством, вы вызываете метод Stop()
, в противном случае сам код позаботится о том, чтобы
соответствующий сценарий не вырабатывал ошибок. Такой подход великолепен для работе в командной строке, однако в промышленных сценариях этот
подход способен маскировать прочие лежащие в основе проблемы. Как и в случае со всеми функциональными возможностями PowerShell, вам придётся
применять обработку null с тщательностью и вниманием.
На Шаге 14 предписываете PowerShell выполнять команду как фоновое задание путём добавления в конец
соответствующей команды символа &
. Применение такого оператора более непосредственное нежели
способ активации соответствующей команды в качестве задания через вызов Invoke-Command
и определения
такой команды при помощи параметров -ScriptBlock
или
-Script
.
На Шаге 15 и на Шаге 16 вы пользуетесь
Get-Job
, Wait-Job
и Receive-Job
для ожидания того, чтобы ваше последнее задание выполнилось и получило вывод. Одним из недостатков отказа от применения
Start-Job
для фонового задания является то, что вы не можете определять название задания. Это подразумевает
применение показанной на Шаге 15 техники для получения самого задания и результатов задания для созданного
на Шаге 14 задания. Применение такой техники, тем самым, более полезно для применения из командной
строки, нежели в неком промышленном сценарии.
На Шаге 17 вы создаёте объект с применением более старого синтаксиса Windows PowerShell, в то время как на Шаге 18 вы пользуетесь трёхместным оператором. Как и в случае прочих операторов, применяйте его внимательно, а когда вы задействуете такие операторы в промышленном коде, убедитесь что вы задокументировали то, что вы выполняете.
В данном рецепте вы рассмотрели новые операторы, добавленные в PowerShell 7. Большинство из них предоставляют более краткий способ выполнения некоторых действий, или прочего, в особенности из командной строки.
Часто бывают ситуации когда вы желаете запустить несколько команд параллельно. Например, у вас может быть список имён компьютеров и для
каждого из этих компьютеров вы хотите на этом компьютере запустить некий сценарий. Возможно, вы пожелаете проверить состояние и применение
ресурсов различными службами на каждом из компьютеров. В таком случае вы можете воспользоваться Get-Content
для получения массива названий компьютеров, а затем применять либо ForEach
, либо
ForEach-Object
для запуска необходимого сценария на таком компьютере. Когда имеется 10 компьютеров
и такому сценарию требуется 10 минут, общее время исполнения будет более 100 минут.
Для Windows PowerShell единственными встроенными методами параллельного исполнения сценариев состояло в применении заданий в фоновом режиме или применение рабочих процессов. При заданиях в фоновом режиме вы можете создать некий набор заданий, причём каждое из них запускает некий сценарий на отдельном компьютере. В таком случае PowerShell запускает каждое задание в отдельном процессе, что предоставляет изоляцию между всеми заданиями, но является ресурсо ёмким. Для Windows PowerShell V4 команда Windows PowerShell добавила рабочие процессы, которые также позволяют вам запускать блоки сценариев параллельно. Однако, рабочие процессы не переносятся в PowerShell 7. Как и прочие функции, которые большее не доступны в PowerShell 7, ы можете продолжать применять Windows PowerShell для выполнения рабочих процессов и постепенно преобразовывать их по мере необходимости.
Альтернативой фоновым заданием выступает применение модуля ThreadJob
, который вы можете выгрузить из
галереи PowerShell. За дополнительными сведениями относительно этого модуля обратитесь к его странице репозитория
https://github.com/PaulHigin/PSThreadJob.
Для PowerShell 7 команда PowerShell добавила некий вариант для команды ForEach-Object
чтобы позволить
вам параллельно запускать блоки сценария. Этот вариант упрощает исполнение блоков сценариев в сценариях, в особенности сценариев длительного
исполнения, причём параллельно и избегая потребности в модулях сторонних производителей или необходимости ведения дел со сложными рабочими
процессами.
Данный рецепт демонстрирует традиционное параллельное исполнение операций, с применением заданий в фоновом режиме и при помощи
ForEach-Object -Parallel
.
Вы можете выполнять этот рецепт в SRV1
после того как вы установили PowerShell 7 и, не обязательно,
VS Code.
-
Имитируем блок сценария длительного исполнения:
$SB1 = { 1..3 | ForEach-Object { "In iteration $_" Start-Sleep -Seconds 5 } } Invoke-Command -ScriptBlock $SB1
-
Измеряем время исполнения
Measure-Command -Expression $SB1
-
Преобразовываем в код, который применяет задания
$SB2 = { 1..3 | ForEach-Object { Start-Job -ScriptBlock {param($X) "Iteration $X " ; Start-Sleep -Seconds 5} -ArgumentList $_ } Get-Job | Wait-Job | Receive-Job -Keep }
-
Активируем этот блок сценария
Invoke-Command -ScriptBlock $SB2
-
Определяем блок сценария при помощи
ForEach-Object -Parallel
$SB3 = { 1..3 | ForEach-Object -Parallel { "In iteration $_" Start-Sleep -Seconds 5 } }
-
Выполняем этот блок сценария
Invoke-Command -ScriptBlock $SB3
-
Измеряем время выполнения этого блока сценария
Measure-Command -Expression $SB3
-
Создание и исполнение двух коротких блоков сценариев
$SB4 = { 1..3 | ForEach-Object { "In iteration $_" } } Invoke-Command -ScriptBlock $SB4 $SB5 = { 1..3 | ForEach-Object -Parallel { "In iteration $_" } } Invoke-Command -ScriptBlock $SB5
-
Измеряем время исполнения для обоих блоков сценариев
Measure-Command -Expression $SB4 Measure-Command -Expression $SB5
-
Code
>
На Шаге 1 вы создали и затем активировали блок сценария. Этот блок сценария имитирует то как вы можете
исполнять некие блоки сценариев с длительным временем выполнения традиционно при помощи командлета
ForEach-Object
со следующим выводом:
На Шаге 2 вы определяете как долго PowerShell выполнял этот блок сценария с выводом подобным следующему:
На Шаге 3 вы переписываете свой блок кода $SB1
на применение
заданий PowerShell в фоновом режиме. Этот блок сценария запускает задачу с длительным временем исполнения, которое применяет задания и затем
дожидается отображения вывода от каждого из заданий. Эта концепция состоит в том, что вместо последовательного выполнения каждой итерации,
все задания выполняются параллельно. Определение таких функций не создаёт вывода.
На Шаге 4 вы активируете этот блок сценария для просмотра результатов, что выглядит так:
На Шаге 5 вы удаляете все имеющиеся задания и затем повторно исполняете свой обновлённый блок сценария. Этот шаг позволяет вам определить время выполнения всего выражения. Вывод на этом шаге отображается следующим образом:
Рисунок 2-19
Удаление всех имеющихся заданий и измерение времени исполнения соответствующего блока сценария
На Шаге 6 вы создаёте другой блок сценария с применением конструкции
ForEach-Object -Parallel
PowerShell 7. После создания этого блока сценария PowerShell не создаёт
никакого вывода.
На Шаге 7 вы исполняете этот блок сценария, что выглядит так:
Рисунок 2-20
Удаление всех имеющихся заданий и измерение времени исполнения соответствующего блока сценария
На Шаге 8 вы измеряете время исполнения этого блока сценария с применением функциональной
возможности ForEach-Object -Parallel
, что отображается следующим образом:
На Шаге 9 вы определяете, а затем активируете два блока сценария и это выглядит так:
На завершающем шаге этого рецепта, Шаге 10, вы измеряете значение времени исполнения этих двух блоков сценариев:
На Шаге 1 и на Шаге 2 вы активируете много раз задачу с
длительным временем исполнения. Как вы уже видели на Рисунке 17,
исполнение этих блоков сценария, по одному за раз, занимает примерно 1 секунд. На Шаге 5 вы видите,
что переписывание задачи с длительным временем исполнения на задания PowerShell в фоновом режиме, снижает время исполнения до 6.83 секунд.
Наконец, на Шаге 8 вы измеряете истекшее время выполнения при применении
ForEach-Object -Parallel
, которое теперь менее 5 секунд.
Как показывает этот рецепт, когда у вас имеются независимые блоки сценария, вы можете исполнять их параллельно для снижения общего
времени выполнения, в данном случае с более чеи 15 секунд до всего лишь около 5 секунд. И этот выигрыш был бы ещё больше если бы вы выполняли
цикл более трёх раз. Последовательное выполнение цикла 10 раз отняло бы более 50 секунд, по сравнению с чуть более 5 секундами для
ForEach-Object -Parallel
.
Однако, существует ограничение по умолчанию в пять блоков сценария, которые PowerShell может исполнять одновременно. Для того чтобы разрешать
больше или меньше чем по умолчанию, вы можете применять параметр -ThrottleLimit
. Один момент,
который следует отметить: когда вы пытаетесь выполнять больше параллельных блоков сценариев, чем у вас имеется ядер процессора, PowerShell
просто применяет очередь ядра процессора. Это всё отнимает время и завершается ростом общего времени выполнения. Хорошая новость состоит в том,
что всё это обрабатывает PowerShell, поэтому если, допустим, 1000 параллельных блоков сценария в системе с 12 ядрами процессора, PowerShell
отработает настолько быстро, насколько позволит ваш компьютер хоста.
Также не лишним будет отметить, что имеются некие накладные расходы, вовлечённые в ForEach-Object
-Parallel
. Под своим капотом этой команде приходится настраивать, а затем и управлять исполнением отдельных потоков. Когда
блок сценария очень короткий, вы можете обнаружить, что такие вовлекаемые в получение результатов накладные расходы замедляют время
исполнения. В данном случае значение времени выполнения уходит с 2.9мс до 83.7мс. Собственно критическая точка здесь состоит в том, что
эта конструкция полезна для нетривиальных блоков сценариев (или сценариев), которые работают параллельно. Вы черпаете выгоду до тех пор, пока
не задействуете имеющееся число ядер.
Также следует отметить тот момент, что когда вы пользуетесь синтаксисом ForEach-Object {script}
,
вы применяете позиционный параметр (-Process
). С другой стороны, когда вы применяете параметр
-Parallel
, значением этого параметра выступает число блоков сценария, которое вы желаете чтобы
PowerShell исполнял параллельно.
Пользователи Windows PowerShell хорошо осведомлены как о применении оператора ForEach
, так и об
использовании командлета ForEach-Object
. В своих сценариях вы можете применять оба эти метода для
обработки имеющихся коллекций, таких как все рассматриваемые пользователи в конкретной группе Active Directory или все аудио файлы в неком
совместно в файловом ресурсе. В PowerShell 7 оба этих метода итераций намного быстрее.
Применение любого из механизмов ForEach
это быстрый и простой способ обработки некой коллекции.
Одним из недостатков, которые некоторые из профессионалов ИТ могли заметить, состоит в том, что накладные расходы каждого
ForEach
в Windows PowerShell растут вместе с размером своей коллекции. При небольших размерах коллекции
вы скорее всего не замечаете разницы. По мере роста размера этой коллекции в дело вступают накладные расходы.
Вы можете выполнять этот рецепт в SRV1
после того как вы установили PowerShell 7 и создали файл профиля
консоли. Исполняйте этот рецепт в консоли с повышенными правами.
-
Создайте удалённое подключение к локальному хосту при помощи Windows PowerShell
New-PSSession -UseWindowsPowerShell -Name 'WPS'
-
Получите удалённый сеанс
$Session = Get-PSSession -Name 'WPS'
-
Проверьте номер версии PowerShell в полученном удалённом сеансе
Invoke-Command -Session $Session -ScriptBlock {$PSVersionTable}
-
Определите блок сценария длительного времени исполнения с применением
ForEach-Object
$SB1 = { $Array = (1..10000000) (Measure-Command { $Array | ForEach-Object {$_}}).TotalSeconds }
-
Выполните этот блок сценария локально
[gc]::Collect() $TimeInP7 = Invoke-Command -ScriptBlock $SB1 "Foreach-Object in PowerShell 7.1: [{0:n4}] seconds" -f $TimeInP7
-
Выполните этот блок сценария в PowerShell 5.1
[gc]::Collect() $TimeInWP = Invoke-Command -ScriptBlock $SB1 -Session $Session "ForEach-Object in Windows PowerShell 5.1: [{0:n4}] seconds" -f $TimeInWP
-
Определите другой блок сценария длительного времени исполнения с применением
ForEach
$SB2 = { $Array = (1..10000000) (Measure-Command { ForEach ($Member in $Array) {$Member}}).TotalSeconds }
-
Выполните его локально в PowerShell 7
[gc]::Collect() $TimeInP72 = Invoke-Command -ScriptBlock $SB2 "Foreach in PowerShell 7.1: [{0:n4}] seconds" -f $TimeInP72
-
Запустите его вWindows PowerShell 5.1
[gc]::Collect() $TimeInWP2 = Invoke-Command -ScriptBlock $SB2 -Session $Session "Foreach in Windows PowerShell 5.1: [{0:n4}] seconds" -f $TimeInWP2
На Шаге 1 мы пользуемся New-PSSession
для создания удалённого
сеанса с применением конечной точки Windows PowerShell. Этот шаг предоставляет такой вывод:
На Шаге 2 вы получаете соответствующий объект сеанса, предоставляющий тот сеанс, который вы создали на предыдущем шаге. Это не производит никакого вывода.
На Шаге 3 вы получаете номер версии PowerShell, который применяет удалённые сеанс для обработки команд, а именно Windows PowerShell 5.1. Получаемый на этом шаге выглядит так:
На Шаге 4 вы создаёте блок сценария, $SB1
, который применяет
командлет ForEach-Object
для выполнения итераций по большой коллекции. Данный шаг не выводит ничего.
На Шаге 5 вы активируете блок сценария $SB1
в локальном сеансе.
Этот шаг исполняет ваш блок сценария в PowerShell 7. Вывод этого шага выглядит следующим образом:
На Шаге 6 вы исполняете $SB1
в Windows PowerShell 5.1,
что предоставляет вывод подобный такому:
Затем, Шаге 7, вы создаёте блок сценария, который применяет элемент синтаксиса
ForEach
, не выдавая вывода. После этого, на Шаге 8,
вы исполняете второй блок сценария в PowerShell 7, что предоставляет вывод, подобный приводимому ниже:
На своём заключительном Шаге 9 вы запускаете $SB2
в созданном
ранее удалённом сеансе (иными словами, в Windows PowerShell 5.1), что снабжает вас таким выводом:
На Шаге 1 вы неявным образом создаёте удалённый сеанс с локальным хостом при помощи перемещения процесса, что намного быстрее чем традиционный удалённый сеанс с применением WinRM.
На Шагах 5, 6, 8 и 9 вы принуждаете .NET к сборке мусора. Именно эти шаги вы можете применять для минимизации снижения производительности при выполнении блока сценария в удалённом сеансе (Windows PowerShell) и уменьшить любое воздействие сборок мусора во время исполнения тестов данного рецепта.
Из получаемых выводов вы видите, что выполнение ForEach-Object
в PowerShell 7 намного быстрее нежели
выполнение ForEach
PowerShell 7. Обработка больших коллекций объектов вообще намного быстрее в
PowerShell 7.
Общее улучшение обработки цикла, которое вы наблюдаете в данном рецепте, сочетается с применением ForEach-Object
-Parallel
, которое вы наблюдали в Изучении параллельной обработки при помощи
ForEach-Object, предоставляя исключительную причину переключаться на PowerShell 7 для большинства операций.
Значение производительности итераций о большим коллекциям это сложный вопрос. На этот предмет вы можете прочесть исключительную заметку
https://powershell.one/tricks/performance/pipeline. Эта статья также адресована более подробному рассмотрению величины
производительности с применением конвейера по сравнению с использованием ForEach
для итераций по наборам.
В Windows PowerShell вы можете применять командлет Test-Connection
в качестве замены для команды
консоли Win32 ping.exe
. Одни из преимуществ применения этого командлета является то, что этот
командлет возвращает объекты, которые могут более просто применяться при составлении сценариев. Для выделения неких сведений из вывода
ping.exe
вы можете пользоваться взломом строк и регулярными выражениями, но это достаточный объём
дополнительной работы и результаты в сценариях, которые сложнее читать.
В Windows PowerShell 5.1 команда Test-Connection
пользуется WMI. Эта команда возвращает некий объект
с типом System.Management.ManagementObject#root\cimv2\Win32_PingStatus
. Для PowerShell 7.1 эта команда
больше не зависит от WMI и возвращает объекты с типом Microsoft.PowerShell.Commands.TestConnectionCommand+PingStatus
.
В результате такого изменения типа объекта, имена свойств, возвращаемых PowerShell 7.1 отличаются от свойств, возвращаемых в Windows PowerShell.
Сценарии, применяющие некоторые свойства могут работать в PowerShell 7 некорректно без соответствующей регулировки, но это не должно быть
общей проблемой.
Вы можете выполнять этот рецепт в SRV1
после того как вы установили PowerShell 7 и создали файл профиля
консоли.
-
Воспользуйтесь
Test-Connection
с параметром-Target
Test-Connection -TargetName www.packt.com -Count 1
-
Примените
Test-Connection
с адресом IPv4Test-Connection -TargetName www.packt.com -Count 1 -IPv4
-
Разрешите адрес получателя при помощи
Resolve-DnsName
$IPs = (Resolve-DnsName -Name Dns.Google -Type A).IPAddress $IPs | Test-Connection -Count 1 -ResolveDestination
-
Разрешите своего получателя и выполните
traceroute
Test-Connection -TargetName 8.8.8.8 -ResolveDestination -Traceroute | Where-Object Ping -eq 1
-
Примените бесконечный
ping
и остановку при помощиCtrl-C
Test-Connection -TargetName www.reskit.net -Repeat
-
Проверьте скорость
Test-Connection
в PowerShell 7Measure-Command -Expression {test-connection 8.8.8.8 -count 1}
-
Проверьте скорость
Test-Connection
в Windows PowerShell$Session = New-PSSession -UseWindowsPowerShell Invoke-Command -Session $Session -Scriptblock { Measure-Command -Expression { Test-Connection -ComputerName 8.8.8.8 -Count 1} }
На Шаге 1 вы проверяете соединение между SRV1
и вебсайтом
нашего издателя. Вот вывод этой команды:
Когда ваш компьютер работает с адресами IPv6, Test-Connection
предпочитает применять по умолчанию IPv6,
как это показано на Шаге 1. Когда вы желаете проверять именно соединение IPv4, вам требуется определять в
явном виде переключатель -IPv4
, как это показано на Шаге 2 :
На Шаге 3 вы пользуетесь командлетом Resolve-DnsName
с целью
определения IPv4 адреса (адресов) для Dns.Google
. Эта площадка является бесплатной службой DNS Google,
которую Google предлагает через два хорошо знакомых IPv4 адреса (8.8.8.8
и
8.8.4.4
), что вы и можете наблюдать в выводе на этом шаге:
Приложение консоли Win32 tracert.exe
позволяет вам отслеживать весь маршрут между хостом из вашей
системы и неким внешним хостом. На Шаге 4 мы пользуемся
Test-Connection
для отслеживания всего маршрута между SRV1
и
компьютером по адресу 8.8.8.8
. Вот как выглядит этот вывод:
При помощи приложения консоли Win32 ping.exe
вы можете определить параметр
-t
для нескончаемого пинга к своему целевому хосту, который вы можете прекратить нажатием
Ctrl-C
. В PowerShell 7 при помощи Test-Connection
вы
можете применять параметр -Repeat
для достижения того же вывода ( и остановки проверки по
Ctrl-C
). Вы наблюдаете этот вывод на Шаге 5:
На Шаге 6 и на Шаге 7 вы сравниваете скорость
Test-Connection
в Windows PowerShell и в PowerShell 7. Выполнение
Test-Connection
в PowerShell 7 выглядит так:
В Windows PowerShell 5.1 получаемый вывод аналогичен (хотя и медленнее) и отображается следующим образом:
Из получаемого на Шаге 1 вывода мы можете наблюдать, что получаемые результаты форматируются иначе по сравнению с Windows PowerShell, причём с улучшениями предоставляемого вывода.
Шаг 2 показывает как PowerShell 7 способен применять переключатель
-IPv4
для использования -IPv4 в явном виде. Аналогично, если вы желаете воспользоваться именно -IPv6,
вы можете применить переключатель -IPv6
. Никакой из этих переключателей не был доступен в Windows
PowerShell.
На Шаге 3 вы определяете IP адреса для хоста Dns.Google
(8.8.8.8 и 8.8.4.4), к которым затем успешно выполняется ping. Обратите внимание, что данный шаг выполняет разрешение IP адресов в имена
хостов осуществляет ping для каждого из адресов. Google исполняет бесплатную службу DNS, доступную в интернете всем. Дополнительные
сведения о ней вы можете найти на https://developers.google.com/speed/public-dns.
На Шаге 6 и на Шаге 7 вы сопоставляли скорость команды
Test-Connection
между Windows PowerShell 5.1 and PowerShell 7.1. Как вы можете наблюдать из полученных
выводов, команда в PowerShell 7 значительно быстрее.
Входящая в состав Windows PowerShell команда Select-String
была улучшена в PowerShell 7.
Вы пользуетесь этой командой для поиска строк либо внутри объектов в конвейерах, либо в текстовых файлах. Данная команда концептуально
аналогична команде Linux grep
. В PowerShell 7 команда PowerShell добавила некоторые исключительные
новые функциональные возможности для этой изумительной команды, которые мы и рассмотрим в данном рецепте.
Вы можете выполнять этот рецепт в SRV1
после того как вы установили PowerShell 7 и Visual Studio Code,
а также создали файл профиля консоли.
-
Получите некий текстовый файл для работы с ним
$Source = 'https://www.gutenberg.org/files/1661/1661-0.txt' $Destination = 'C:\Foo\Sherlock.txt' Start-BitsTransfer -Source $Source -Destination $Destination
-
Получите содержимое этой книги
$Contents = Get-Content -Path $Destination
-
Проверьте длину The Adventures of Sherlock Holmes
"The book is {0} lines long" -f $Contents.Length
-
Поиск "Watson" в содержимом этой книги
$Match1 = $Contents | Select-String -Pattern 'Watson' "Watson is found {0} times" -f $Match1.Count
-
Просмотр нескольких самых первых совпадений
$Match1 | Select-Object -First 5
-
Поиск "Dr. Watson" при помощи регулярного выражения
$Contents | Select-String -Pattern 'Dr\. Watson'
-
Поиск "Dr. Watson" при помощи простого совпадения
$Contents | Select-String -Pattern 'Dr. Watson' -SimpleMatch
-
Просмотр содержимого вывода при поиске в файле
Get-ChildItem -Path $Destination | Select-String -Pattern 'Dr\. Watson'
В данном рецепте мы рассмотрим как вы можете применять командлет Select-String
, включаемый в
PowerShell 7. Для исследования этого командлета вам сперва требуется выгрузить некий текстовый файл.
На Шаге 1 для выгрузки текста книги, The Adventures of Sherlock
Holmes, сэра Артура Конан Дойля, с вебсайта проекта Gutenberg вы применяете команду
Start-BitsTransfer
. Этот шаг не имеет вывода.
На Шаге 2 вы получаете весь текст из этой книги, который вы сохраняете в переменной
$Contents
. Этот шаг также не производит вывод. На Шаге 3
вы выдаёте отчёт об общей длине этой книги, который выглядит следующим образом:
На Шаге 4 вы осуществляете поиск по содержимому этой книги для нахождения вхождений "Watson", преданного компаньона Шерлока Холмса. Это имеет результатом 81 вхождение и выглядит так:
На Шаге 5 для просмотра первых пяти раз когда в содержимом книги обнаружен термин "Watson",
вы пользуетесь командлетом Select-Object
, что отображено ниже:
При помощи Select-String
вы можете задать некое регулярное выражение для соответствия искомому
содержимому. На Шаге 6 вы определяете шаблон регулярного выражения для поиска строки
"Dr. Watson". Получаемый вывод выглядит примерно так:
В качестве альтернативы применению регулярного выражения для выполнения поиска, Select-String
также
принимает простое соответствие, как это отображает вывод на Шаге 7:
На наших предыдущих шагах вы применяли Select-String
для поиска содержимого некой переменной.
Другая ценная функциональная возможность Select-String
состоит в способности поиска в неком файле
или даже во множестве файлов. Вы можете обнаружить это на Шаге 8, вывод которого приводится ниже:
На Шаге 1 и Шаге 2 вы выгружаете файл из проекта Gutenberg, бесплатной интернет библиотеки электронных книг. Эта площадка содержит большое число бесплатных книг в различных форматах, включая и основной текстовый. Для поиска дополнительных бесплатных книг посетите его домашнюю страницу https://www.gutenberg.org/, а для получения дополнительных сведений о самом проекте https://www.gutenberg.org/about/.
В качестве существенного улучшения Select-String
в PowerShell 7 отметим выделение найденных строк,
что вы можете наблюдать на Шаге 5, 6,
7 и 8. Это делает ещё более простым для употребления
Select-String
при просмотре вывода. Кроме того, возможность поиска по множеству файлов, как это показано
на Шаге 8 , превращает Select-String
в ещё более полезную.
С самого своего начала Windows PowerShell выполнил великое задание по отображению результатов ошибок: большой ком красного текста на чёрном экране, который содержал полные сведения относительно того что пошло не так. Это было потрясающе, но многих новых пользователей это слегка отталкивало - слишком много сведений, часть которых в большинстве случаев не слишком полезны.
PowerShell 7 теперь предлагает более компактное представление об ошибках, уменьшающее объём текста и улучшающее формат вывода. Получаемый
результат короче и более читаем. А в тех редких случаях, когда это может потребоваться, вы можете Get-Error
для получения полных подробностей без необходимости синтаксического разбора через $Error[0]
.
Вы можете выполнять этот рецепт в SRV1
после того как вы установили PowerShell 7 и/ или Visual Studio
Code, а также создали файл профиля консоли.
-
Создайте простой сценарий
$SCRIPT = @' # divide by zero 42/0 '@ $SCRIPTFILENAME = 'C:\Foo\ZeroDivError.ps1' $SCRIPT | Out-File -Path $SCRIPTFILENAME
-
Выполните этот сценарий и просмотрите установленное по умолчанию представление об ошибке
& $SCRIPTFILENAME
-
Выполните ту же самую строку в своей консоли
42/0
-
Просмотрите содержимое переменной
$ErrorView
$ErrorView
-
Просмотрите все потенциальные значения переменной
$ErrorView
$Type = $ErrorView.GetType().FullName [System.Enum]::GetNames($Type)
-
Установите
$ErrorView
в'NormalView'
и воссоздайте свою ошибку$ErrorView = 'NormalView' & $SCRIPTFILENAME
Установите
$ErrorView
в'CategoryView'
и воссоздайте свою ошибку$ErrorView = 'CategoryView' & $SCRIPTFILENAME
-
Установите
$ErrorView
в значение по умолчанию$ErrorView = 'ConciseView'
На Шаге 1 вы создаёте некий сценарий, который (умышленно) содержит ошибку деления на ноль. Этот шаг создаёт файл, но не производит вывода.
На Шаге 2 вы выполняете этот сценарий из VS Code и просматриваете получаемую в результате ошибку, выглядящую следующим образом:
На Шаге 3 вы воссоздаёте ошибку деления на ноль в командной строке. на этом шаге вывод выглядит так:
PowerShell 7 пользуется переменной $ErrorView
для содержания названия того представления об ошибках,
которое надлежит применять PowerShell для отображения ошибок. На Шаге 4 вы просматриваете текущее
значение этой переменной что отображается так:
Переменная $ErrorView
может принимать одно из трёх значений, как вы это можете наблюдать из вывода на
Шаге 5
На Шаге 6 вы устанавливаете значение $ErrorView
для отображения
ошибок с применением вывода, вырабатываемого Windows PowerShell и воссоздаёте саму ошибку, которая теперь выглядит так:
На Шаге 7 вы устанавливаете $ErrorView
для отображения
сообщения об ошибке при помощи CategoryView
и далее воссоздаёте ту же ошибку. Получаемый на этом шаге
вывод отображает представление о категории ошибки:
На Шаге 8 вы сбрасываете значение $ErrorView
в установленное
по умолчанию значение. Этот шаг не производит вывода.
Краткое представление об ошибке, которое вы наблюдаете на Шаге 2 содержит все сведения стандартного представления, которые вы обнаруживаете в выводе на Шаге 7, за исключением пропуска сведений о категории этой ошибки. А когда вы активируете эту ошибку непосредственно в командной строке, как это показано на Шаге 3, вы видите лишь сообщение об ошибке, что намного проще для наших глаз.
На Шаге 7 вы просматриваете сведения по категории своей ошибки, что не является особенно полезным.
На Шаге 8 вы сбрасываете значение $ErrorView
. В зависимости
от того что вы делаете, этот шаг может не быть необходимым. Вы просто можете покинуть PowerShell (или VS Code) и когда вы запустите свой
PowerShell снова, он сбросит это значение в установленное по умолчанию (ConciseView
). А если вы
предпочтёт обычное представление категорий ошибок, вы всегда можете установить значение для $ErrorView
в своём файле профиля.
Хотя это и не отражено в данном рецепте, вы можете применять командлет Get-Error
для отображения полных
сведений относительно конкретной ошибки. Для большинства профессионалов ИТ, те базовые сведения об ошибке, которые предоставляются PowerShell 7
более чем остаточные ( и выступают великолепным улучшением по сравнению с выводом сообщений об ошибках в Windows PowerShell).
На протяжении разработки PowerShell Core и позднее, в PowerShell 7, команда PowerShell по ходу дела добавляла новые функциональные возможности. Некоторые из этих новых свойств способны, по крайней мере в теории, способны вызывать крушение имеющихся сценариев, и потому носят название "экспериментальных". По умолчанию, PowerShell не включает такие свойства. Как показывается в данном рецепте, вам надлежит разрешать их в явном виде. Такой подход к экспериментальным свойствам позволяет вам проверять эти новые функциональные возможности и снабжать команду PowerShell обратной связью. В случае когда вы обнаружите некое свойство, которое нарушает вам некий сценарий, запрещайте его. После того как вы включили (отключили) некую экспериментальную функциональную возможность, вам требуется перезапустить PowerShell 7.
В целом, экспериментальные свойства не имеют тенденции применяться в промышленных приложениях, так как такие экспериментальные функциональные возможности, по своей архитектуре, могут приводить к нарушениям. Кроме того, такие экспериментальные свойства не поддерживаются официально. При этом, до сих пор, эти функциональные возможности были стабильными и надёжными.
В данном рецепте вы рассмотрите те экспериментальные свойства, которые доступны в PowerShell 7.1 в том виде, как он выпущен. Если вы применяете более позднюю версию (например, предварительный выпуск PowerShell 7.2), вы можете обнаружить иные экспериментальные функциональные возможности, подробнее в https://docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_experimental_features.
Вы можете выполнять этот рецепт в SRV1
после того как вы установили PowerShell 7 и/ или Visual Studio
Code, а также создали файл профиля консоли.
-
Исследуем имеющиеся экспериментальные свойства
Get-ExperimentalFeature -Name * | Format-Table Name, Enabled, Description -Wrap
-
Изучаем результат "команда не найдена" без каких бы то ни было доступных экспериментальных свойств
Foo
-
Включаем одну из экспериментальных функциональных возможностей в качестве текущего пользователя
Get-ExperimentalFeature -Name * | Select-Object -First 1 | Enable-ExperimentalFeature -Scope CurrentUser -Verbose
-
Включаем одну функциональную возможность для всех пользователей
Get-ExperimentalFeature -Name * | Select-Object -Skip 1 -First 1 | Enable-ExperimentalFeature -Scope AllUsers -Verbose
-
Запускаем новую консоль PowerShell.
Если вы пользуетесь VS Code, для запуска нового терминала нажмите
Ctrl + Shift + `
. Когда вы применяете консоль PowerShell 7, запустите новую копию консоли. -
Исследуйте имеющиеся новые экспериментальные свойства
Get-ExperimentalFeature
-
Изучите вывод предлагаемый свойством "команда не найдена"
Foo
На Шаге 1 для выявления всех доступных экспериментальных функциональных возможностей и их текущего
состояния вы применяете командлет Get-ExperimentalFeature
, который (по умолчанию) выглядит примерно
так:
Для проверки экспериментальных свойств на Шаге 2 вы исполняете не существующую команду, с подобным выводом:
На Шаге 3 вы включаете для своего текущего пользователя самое первое свойство, экспериментальную
функциональную возможность PSCommandNotFoundSuggestion
, что отображается так:
На Шаге 4 вы включаете вторую экспериментальную функциональную возможность,
PSCultureInvariantReplaceOperator
, что выглядит как показано ниже:
На Шаге 5 вы запускаете новую версию PowerShell. Этот шаг не производит как такового вывода.
На Шаге 6 вы изучаете состояние экспериментальных свойств, причём никакие, за исключением двух, не доступны, что показано ниже:
На Шаге 7 вы повторно исполняете неизвестную команду для просмотра предложений "команда не найдена", которое теперь отображается так:
В данном рецепте вы включили две экспериментальные функциональные возможности и исследовали одну (предложения "команда не найдена"). В большинстве случаев для вас должно быть безопасным включать все экспериментальные свойства, однако всегда куда безопасней включать их одну за другой и тщательно проверять ваши сценарии.