В первой части статьи (опубликованной в Windows IT Pro/RE № 10 за 2014 год) мы начали разговор о процессе преобразования полезной, но маловразумительной строки кода в нечто более изящное. Сегодня я завершу эту тему, а затем рассмотрю еще один несложный пример превращения однострочного фрагмента кода в сценарий. До сих пор мы имели дело вот с таким текстом:
get-aduser -filter * -properties * | foreach { set-aduser $_ -displayname ($_.givenname + «" + $_.sn)}
Я начал процесс его превращения в многострочный сценарий: разделил строку на две по символу конвейера (|), поместил результат выполнения первой строки (get-aduser -filter * -properties *) в переменную (в данном случае ей было присвоено имя $Allusers, но сгодилось бы и любое другое), а затем использовал переменную для заполнения конвейера с целью выполнения операции по правую сторону конвейера (foreach. ..}. В результате получился более удобочитаемый двустрочный сценарий:
$AllUsers= ( get-aduser -filter * -properties *) $AllUsers| foreach { set-aduser $_ -displayname ($_.givenname +» «+ $_.sn)}
Кроме того, в предыдущей статье я показал, что читатель может в любое время добавить в текст комментарий, для чего нужно ввести с клавиатуры символ»решетки«(#), и что PowerShell будет игнорировать все символы справа от»решетки«.
Теперь давайте разделим вторую строку на несколько частей. Главный компонент здесь — команда foreach-object, которая, кстати, имеет два более коротких имени — foreach или просто символ процента (%).
По сути синтаксис foreach таков:
foreach { command; command; command... }
При работе с оболочкой PowerShell практически во всех случаях, когда вам попадаются фигурные скобки (символы {}), можете исходить из того, что внутри этих скобок размещается одна или несколько команд PowerShell, отделяемых друг от друга точками с запятой; согласно терминологии PowerShell такой фрагмент кода именуется сценарным блоком (scriptblock). Обычно команды PowerShell несколько многословны, так что сценарные блоки foreach бывают длинноваты. Поэтому мы сокращаем их размеры. Как правило, мы будем просто начинать строку со слова foreach, размещать каждую команду на отдельной строке и затем в особой строке ставить закрывающую скобку. Кроме того, мы будем использовать отступы, чтобы читатели смогли без труда следить за текстом, составляющим сценарный блок. В результате мы получим такой сценарий, отформатированный в соответствии с нашими представлениями о прекрасном:
$AllUsers= ( get-aduser -filter * -properties *) $AllUsers| foreach { Set-aduser $_ -displayname ($_.givenname +» «+ $_.sn) }
Обратите внимание на то, как я работаю со скобками. В соответствии со стандартами PowerShell открывающая скобка должна располагаться на той же строке, что и оператор foreach, хотя вы можете разместить ее на другой строке, если за foreach следует обратная кавычка, как в следующем примере:
foreach ` { (commands) }
Кроме того, схема будет работать без переменной, так что сценарий может выглядеть следующим образом:
get-aduser -filter * -properties * | foreach { set-aduser $_ -displayname ($_.givenname +» «+ $_.sn) }
Кстати, зачем делать отступы? Когда сценарий настолько прост, как в нашем случае, можно обойтись и без них. Но если в сценарии один цикл foreach вложен в другой цикл foreach, при отсутствии отступов читателю будет трудно следить за выполнением сценария, как показывает пример:
get-something | foreach { get-somethingelse | foreach { Do something... Do something else... } Do yet another thing... }
Следовать моей формуле в точности необязательно, но в любом случае следует разработать такой стиль применения отступов, который в наибольшей степени соответствует вашим потребностям. Иначе отладка или расшифровка любого сценария, состоящего более чем из 10 строк, станет для вас делом неподъемным.
А вот еще один пример. Наверное, он тривиален, но, возможно, прольет свет на то, как функционирует PowerShell. Допустим, вы хотите ввести в PowerShell набор чисел и получить их сумму. Сценарий будет выглядеть примерно так:
2,77,13,92 | foreach {некоторое действие с переменной $_ pipeline}
Сценарий должен работать при использовании произвольного числа значений в конвейере, так что вам потребуется переменная для набора суммы. Присвоим ей имя $sum. Теперь каждый раз, когда вы будете прогонять цикл через конвейер, можете набирать сумму с помощью следующей команды:
$sum = $sum + $_ $_ — это значение конвейера, так что команда должна работать. Итак, первый прогон: 1,2,3 | foreach {$sum = $sum + $_}
Запустите сценарий на выполнение, и вы получите... А ничего вы не получите. Ведь вы не предписывали оболочке PowerShell отображать результат! Напомню, что если просто ввести имя переменной, PowerShell отобразит ее значение. Кроме того, на одной строке можно размещать более одной команды, разделяя их точкой с запятой. Итак, теперь вы можете протестировать такую версию:
1,2,3 | foreach {$sum = $sum + $_}; $sum
Выполните этот код, и вы получите результат 12. Почему 12? Потому что вы забыли»инициализировать«значение $sum.
Когда оболочка PowerShell в первый раз прочла команду $sum=$sum+$_, она исходила из того, что нужно создать некую числовую переменную, и по умолчанию начала со значения»нуль«. Но по завершении выполнения сценария переменная сохраняется. Так, в ходе первого прогона сценария переменная $sum получила значение»нуль«, но во второй раз она начала со своего предыдущего значения, то есть с 6. Давайте добавим на левой стороне еще одну команду, чтобы инициализировать переменную $sum:
$sum=0; 1,2,3 | foreach {$sum = $sum + $_}; $sum
Теперь все работает как надо, и в заключение давайте придадим сценарию более эстетичный вид:
$sum=0 1,2,3 | foreach { $sum = $sum + $_ } $sum
Думаю, вы согласитесь, что многострочный сценарий читается проще, чем сверхдлинный»однострочник", да и редактировать его легче. Итак, с проблемой преобразования однострочного кода в сценарий мы разобрались. Пойдем дальше – покажем, как оболочка PowerShell должна принимать решения.