Создание цикла for для консолидации сценария с помощью командной строки powershell. Powershell цикл for


Контроль выполнения кода при помощи оператора if PowerShell | Windows IT Pro/RE

Windows PowerShell предоставляет несколько способов выполнения кода, включая операторы if, for и while. Можно использовать эти три оператора для определения состояний и действий, которые выполняются, когда эти состояния встречаются. Вы даже можете точно установить те действия, которые осуществляются, когда состояние не появляется. .

Условные операторы

Условные операторы if содержат блок кода условия, который заключен в круглые скобки, и блок команд, заключенный в фигурные скобки. Блок условия задает условие, тогда как блок команд задает одно или несколько действий. Когда условие выполняется (в этом случае блок условия выдает значение true), PowerShell запускает блок команд. Когда блок условия выдает значение false, PowerShell игнорирует блок команд.

Например, следующий код инициализирует переменную $files, затем формулирует базовое условие оператора if:

$files = dir c:\archivedfiles\*.txt if ($files -ne $null) { "There are files in this folder." write-host }

Первая строка присваивает коллекцию текстовых файлов в $files. Предложение if использует переменную $files в своем блоке условия ($files -ne $null), для того чтобы определить, что переменная не должна быть нулевой. Когда есть текстовые файлы, блок условия выдает true, и вступает в дело блок команд, который выводит сообщение There are files in this folder. Когда блок условия выдает значение false, оператор if прекращает работу. В результате никакое сообщение не выводится на экран, если папка не содержит текстовых файлов.

Время от времени вам бывает нужно выполнить особое действие, когда блок условия выдает значение false. Вы можете это сделать, добавив еще оператор else. Этот оператор начинается с ключевого слова else, за которым следует его собственный блок команд. Дополнительный блок команд запускается, когда ни один из блоков условий оператора if не выдает значение true. Взгляните, например, на такой оператор if:

$files = dir c:\archivedfiles\*.doc if ($files -ne $null) { "There are Word" + "files in this folder." write-host } else { "No Word files in this folder." write-host }

Когда блок условия (($files -ne $null) выдает значение false, блок команд else запускает и выводит на экран сообщение No Word files in this folder.

В этом примере оператор if рассматривает два варианта: содержит папка файлы Microsoft Word и не содержит их. Но, возможно, вы захотите, чтобы оператор обрабатывал больше, чем два сценария. Тогда вы можете добавить несколько операторов elseif (если нужно использовать много операторов elseif, требуется решить, использовать ли оператор переключения switch). Для каждого оператора elseif вы определяете блок условия и блок команд. Когда блок условия выдает true, запускается блок команд.

Например, код в листинге 1 использует операторы elseif, чтобы определить, как много файлов в папке. В этом коде один оператор if помещен в другой. Код начинается с присваивания коллекции файлов в $files. Затем внешний оператор if проверяет, не является ли $files пустой. Я произвожу такую проверку и не использую свойство Count, чтобы определить количество файлов, поскольку я должен предусмотреть случай, если в папке будет только один файл. Свойство Count доступно только тогда, когда в папке два или более файлов. Когда файлов более одного, PowerShell обращается с $files как с массивом, который поддерживает свойство Count. Когда есть только один файл, PowerShell обращается с $files как со скалярной величиной (то есть единственной), а это означает, что свойство Count недоступно.

Когда внешний оператор if обнаруживает, что $files пуста, запускается оператор else из фрагмента В. Блок команд этого оператора выводит сообщение No files in folder. Когда $files не пуста, запускается внутренний оператор if из фрагмента А. Блок условия внутреннего оператора if определяет три состояния: условие if и два условия elseif.

Условие if ($files.count -gt 10) определяет, что число текстовых файлов должно быть более десяти. Когда блок условия выдает true, блок команд выводит сообщение More than 10 files in folder.

Первый оператор elseif ($files.count -gt 7 и $files.count -le 10) определяет, что число текстовых файлов должно быть больше 7, но меньше или равно 10. Когда блок условия выдает true, блок команд выводит сообщение «5-7 files in folder».

Если ни один из блоков условий не выдает значение true, запускается блок команд оператора else. Он выводит сообщение Fewer than 5 files in folder.

Как видно из листинга 1, вы можете вставить оператор if в блок команд другого оператора if, но вы не ограничены только этими типами операторов. Можно, например, вставить оператор foreach в блок команд оператора if, как показано в листинге 2. Foreach-оператор запускается только тогда, когда блок условия оператора if выдает true. Другими словами, когда папка содержит текстовые файлы, оператор foreach будет выполнять цикл по этим текстовым файлам. Каждый раз, когда цикл повторяется, оператор foreach возвращает размер текстового файла, как показано на экране 1.

 

Экран 1. Результаты работы встроенного оператора foreach в операторе if

Оператор for

Я уже рассказывал о том, как реализовать оператор цикла foreach, чтобы произвести перебор коллекции. Оператор for запускает пошаговый цикл, который повторяется по всей коллекции, как только условие подтверждается. Подобно условным операторам if, операторы for включают блок условия и блок команд. Однако блок условия здесь гораздо сложнее.

Давайте посмотрим па простой пример. Следующий оператор for показывает значения, заданные для переменной $a:

for ($a = 1; $a -le 5; $a++) {$a}

Оператор начинается с ключевого слова for, за которым следует блок условия ($a = 1; $a -le 5; $a++). Блок условия оператора for состоит из трех частей, которые разделены точкой с запятой. Первая часть ($a = 1) инициализирует переменную $a значением 1. Переменная $a обеспечивает базовую, или начальную, величину для других элементов блока кода.

Вторая часть ($a -le 5) — это само условие. В данном случае значение в $a должно быть меньше 5, чтобы получить true. Третья часть ($a++) увеличивает $a на единицу в конце каждого цикла. В итоге оператор будет двигаться через коллекцию, пока значение $a меньше или равно 5. Когда $a равно 6, оператор for прекращает работу. На экране 2 показан простой вывод этого оператора.

 

Экран 2. Результаты работы оператора for при реализации цикла по коллекции

В некоторых случаях вы не можете знать количество элементов в коллекции. Код в листинге 3 показывает, как можно использовать оператор for, чтобы произвести итерацию по такой коллекции. Этот код начинается с присваивания коллекции текстовых файлов в переменной $files. Оператор if использует переменную $files и ее свойство Count ($files.count), чтобы проверить число файла. Как уже говорилось, если в коллекции только один файл, PowerShell вернет объект (например, объект System.IO.FileSystemInfo), у которого есть скалярное значение. Если файлов нет, PowerShell не возвращает объект. В любом случае свойство Count не существует. В результате вы получаете пустое значение, если пытаетесь вызвать свойство Count, поэтому можете использовать его как условие в операторе if.

Когда $files.count возвращает значение null, оператор else из фрагмента В проверяет, действительно ли там только один файл. Если файлов там нет, запускается оператор else из фрагмента С.

Когда $files.count не возвращает значение null (то есть PowerShell возвращает объект System.Array, таким образом, свойство Count существует), запускается код из фрагмента А, поскольку есть по меньшей мере два файла. Во фрагменте А код условия цикла внутреннего оператора for использует свойство Count для определения точного количества файлов (то есть элементов) в коллекции. Как только $i становится меньше, чем число элементов, условие выдает true.

Заметьте, что я инициализировал $i с нуля. Я использую нуль, поскольку коллекции, такие как $files, используют как базу для индексации нуль. В блоке команд я применял $i, чтобы установить, какой элемент извлечь из коллекции. Например, в течение первого шага цикла $i равна нулю. Это означает, что $files [$i] — такая же, как $files [0]. На втором шаге цикла $i равно 1, таким образом, значение становится $files [1] и т. д. На экране 3 показаны результаты работы кода, приведенного в листинге 3.

 

Экран 3. Получение списка текстовых файлов и их размеров

Еще следует обратить внимание на то, что вы можете задать имя и тип используемой в программе переменной и инициализировать базовую переменную до запуска оператора for. Например, в листинге 4 переменной $i задано имя и тип, и она инициализирована в строке, выделенной во фрагменте А. Очевидно, что код в этом случае читать легче.

Операторы while

Подобно оператору for, операторы while — это тип цикла, который производит итерации по коллекции. Оператор while, который состоит из блока условия и блока команд, продолжается, пока блок условия не выдаст значение true. Следующий код показывает простой пример того, как он работает:

$count = 0 while ($count -lt 5) { $count++ "The count is $count." }

Первая строка кода инициализирует $count нулем. Эта переменная используется как база или начальная величина для организации цикла. Блок условия ($count -lt 5) определяет, что $count должно быть меньше 5.

Когда блок условия выдает true, запускается блок команд. Первый оператор в блоке команд увеличивает $count на 1. Второй оператор выводит строку, которая показывает текущее значение $count, как мы видим в результатах:

The count is 1. The count is 2. The count is 3. The count is 4. The count is 5.

Заметим, что, когда $count равна 4 в блоке условия, блок команд увеличивает $count на 1 и выводит в итоге значение 5. И только на следующем шаге блок условия вернет false и тем самым остановит цикл while.

Иногда вы можете не знать количество элементов в коллекции, по которой происходит повторение цикла. В подобной ситуации можно использовать свойство Count для возвращения значения:

$proc = Get-Process $count = 0 while ($count -lt $proc.count) { "The" + $proc [$count].name + "process uses" + $proc [$count].handles + "handles." $count ++ }

Этот код вначале присваивает результаты команды Get-Process переменной $proc. Эта команда возвращает список процессов, запущенных на локальной системе. Блок условия в цикле while определяет, что величина в $сount должна быть меньше, чем общее количество процессов ($proc.count). На экране 4 показаны результаты запуска этого кода. Вы можете использовать переменную $proc, чтобы найти не только количество процессов, но и имя каждого процесса и количество дескрипторов.

 

Экран 4. Получение списка процессоров и числа их дескрипторов

В этой статье я рассказал об условных операторах if, for и while. Эти операторы, наряду с foreach- и командой ForEach-Object, дают нам возможность выбора инструментов для реализации управления исполнением кода. Вы можете использовать эти операторы и пытаться комбинировать их разными способами, например добавляя условные операторы if к операторам foreach.

Листинг 1. Оператор if, содержащий оператор elseif

$files = dir c:\archivedfiles\*.txt if ($files -ne $null) Начало фрагмента А {   if ($files.count -gt 10)   {     "More than 10 files in folder."     write-host   }   elseif ($files.count -gt 7 -and     $files.count -le 10)   {     "8-10 files in folder."     write-host   }   elseif ($files.count -gt 4 -and     $files.count -le 7)   {     "5-7 files in folder."     write-host   }   else   {     "Fewer than 5 files in folder."     write-host   } } Конец фрагмента A Начало фрагмента B else {   "No files in folder."   write-host } Конец фрагмента B

Листинг 2. Оператор if со встроенным оператором foreach

$files = dir c:\archivedfiles\*.txt if ($files -ne $null) {   write-host   foreach ($file in $files)   {     "The $file file is " +       $file.length + " bytes."   }   write-host } else {   "No files in this folder."   write-host }

Листинг 3. Оператор for

$files = dir c:\archivedfiles\*.txt if ($files.count -ne $null) Начало фрагмента A {   for ($i = 0; $i -lt $files.count;     $i++)   {     $files[$i].name + " = " +       $files[$i].length + " bytes"   }   write-host } Конец фрагмента A Начало фрагмента B elseif ($files -ne $null `     -and $files.count -eq $null) {   $files.name + " = " +     $files.length + " bytes"   write-host } Конец фрагмента B Начало фрагмента C else {   "No text files in this folder."   write-host } Конец фрагмента C

Листинг 4. Код для объявления и инициализации $i перед оператором for

$files = dir c:\archivedfiles\*.txt Начало фрагмента A $i = 0 Конец фрагмента A if ($files.count -ne $null) {   for ($i; $i -lt $files.count;     $i++)   {     $files[$i].name + " = " +       $files[$i].length + " bytes"   }   write-host } elseif ($files -ne $null `     -and $files.count -eq $null) {   $files.name + " = " +     $files.length + " bytes"   write-host } else {   "No text files in this folder."   write-host }

Роберт Шелдон ([email protected]) — технический консультант и автор книг по технологиям Microsoft Windows и базам данных

www.osp.ru

powershell - Как создать цикл для возврата результата в качестве нового параметра функции? (PowerShell)

Исходное описание в вашем вопросе, похоже, указывает на рекурсивный метод, так как рекурсивная функция может работать до тех пор, пока не будет выполнено условие (таким же образом, как цикл). В этом случае, вероятно, будет работать цикл while while или do/ while, но это также дает прекрасную возможность узнать о рекурсии для более поздних проблем или назначений (таких как сглаживание массивов или проблемы сортировки/поиска).

Простой рекурсивный метод

Замечание о коде: хорошая практика кодирования начинает изучать стандарты именования. В этом случае переменные, созданные внутри функций, не должны иметь то же имя, что и другие функции (если не требуется $ global), и одна функция букв редко бывает хорошей идеей - обе из них могут вызвать путаницу и затруднить анализ кода позже.

function CalculateF #changed from F { param ([int] $cur_of) #changed from $OF so no confusion with calling variable $cur_of if ($cur_of -gt 2) { CalculateF -cur_of ($cur_of/2) # calls itself with halved value } } $OF = 100 if (CalculateF -cur_of $OF -ge 2) #changed these function calls to call the parameter directly { CalculateF -cur_of $OF }

Эта функция запускается, печатает значение текущего OF (cur_of), проверяет, больше ли значение cur_of больше 2. Than CalculateF вызывает себя (это простая рекурсия) с cur_of/2. Или, если он не завершает эту функцию.

Для версии Loop

Поскольку @Kiran уже поставил версию с циклом do/while и функцией, я поставлю версию цикла for, которая не требует и не имеет смысла с функцией вообще. Поскольку цикл for может выполнять простые математические вычисления на итераторе не только + / - 1, тем самым:

$val = 100 for ($i=$val; $i -ge 2; $i /= 2) { # Sets iterator to value and loops through (dividing by 2 each time) [int] $i }

Примечание. Протестировано выше с помощью powershell -version 2./scriptname.ps1 для обеспечения отсутствия проблем с версированием

qaru.site

Обработка коллекций с помощью циклов foreach оболочки PowerShell | Windows IT Pro/RE

Чтобы максимально эффективно использовать мощный инструментарий Windows PowerShell, необходимо знать, как с помощью цикла foreach поочередно обращаться ко всем элементам коллекции, например строкового массива или списка служб Windows. В среде PowerShell реализовано два типа циклов foreach — инструкция foreach и команда ForEach-Object. Они позволяют добиваться аналогичных результатов, но отличаются друг от друга в некотором отношении. В предлагаемой статье я разъясню эти различия, а также покажу, как следует использовать инструкцию foreach и команду ForEach-Object.

Инструкция foreach

В ходе выполнения инструкции foreach оболочка PowerShell «перебирает» элементы коллекции. Для каждого элемента цикл проводится один раз; при этом выполняется блок инструкций, именуемый блоком сценария. Чтобы создать цикл foreach, нужно определить коллекцию, к элементам которой он будет обращаться, задать переменную, которая будет содержать все элементы данной коллекции, и составить блок, который будет выполняться при каждом шаге по элементам коллекции.

Поясним сказанное на примере. В следующей команде объявляется переменная $birds, затем она инициализируется со строковым массивом, после чего эта переменная используется в инструкции foreach:.

$birds = "owl", "crow", "robin", "wren", "jay" oreach ($bird in $birds) {    "$bird = " + $bird.length }

Инструкция foreach начинается с ключевого слова foreach, за которым следует пара скобок, заключающих три компонента ($bird in $birds). Первый компонент — это переменная цикла, которую мы определяем для использования в инструкции foreach. В данном случае переменная цикла имеет имя $bird, но пользователь может присвоить ей любое имя по своему усмотрению — лишь бы оно соответствовало соглашениям об именовании PowerShell. По мере того как цикл переходит от одного элемента коллекции к другому, переменная цикла всегда содержит текущую величину коллекции. Так, при первой итерации переменная $bird имеет значение owl, при второй — crow и т. д.

Второй компонент в скобках — ключевое слово in. Используйте его «как есть». Третий элемент — собственно коллекция, доступ к которой в данном случае осуществляется с помощью переменной $birds. Далее следует пара фигурных скобок. В них заключен блок сценария, выполняемый при каждом шаге цикла. В нашем примере этот блок содержит только одну инструкцию ("$bird = " + $bird.length), создающую простую строку, которая выводится на консоль. В данном случае переменная $bird получает величину из коллекции, а свойство Length считывает число символов этой величины.

Команда возвращает следующие результаты

owl = 3 crow = 4 robin = 5 wren = 4 jay = 3

В нашем примере блок сценария включает в себя только одну инструкцию, но мы можем использовать любое число инструкций. Вот блок сценария, содержащий три инструкции:

$count = 0 $birds = "owl", "crow", "robin", `    "wren", "jay" foreach ($bird in $birds) {    $count += 1    "$bird = " + $bird.length    Write-Host } "Total number of birds is $count."

Первая инструкция в этом блоке сценария увеличивает значение переменной $count на единицу. Переменная $count определяется в первой строке и используется для подсчета промежуточной суммы элементов коллекции. Вторая инструкция формирует строку и передает ее на консоль, как в предыдущем примере. А третья инструкция — это команда Write-Host, которая попросту добавляет к выходным данным пустую строку.

При выполнении каждой итерации цикла выполняются все три инструкции блока сценария. Но код, следующий за блоком сценария, выполняется только раз — по завершении последней итерации цикла. Этот код использует переменную $count в выходных результатах. В данном случае значение этой переменной составляет 5. Данное значение присвоено переменной в ходе последней итерации, как показано в следующих результатах

owl = 3 crow = 4 robin = 5 wren = 4 jay = 3 Total number of birds is 5.

Предшествующие команды сохраняют коллекцию (строковый массив) в переменной, но это не единственная возможность. Коллекцию можно определить непосредственно внутри инструкции foreach, причем определять можно и коллекцию, состоящую из объектов других типов, например:

foreach ($svc in Get-Service) {   $svc.name + ": " +   $svc.canstop.tostring ().toupper ()}

В данном фрагменте кода третий компонент в скобках — команда Get-Service. Эта команда возвращает коллекцию объектов, где один объект представляет отдельную службу на локальной системе. Объект service назначается переменной $svc при каждой итерации цикла. В цикле инструкция foreach использует переменную $svc для получения имени службы (с помощью свойства Name объекта service) и добавляет к ней двоеточие. Далее инструкция foreach использует переменную $svc для обращения к свойству CanStop объекта service, которое возвращает логическое значение, указывающее, можно ли остановить данную службу после ее запуска. Наконец, инструкция foreach вызывает методы ToString и ToUpper для форматирования полученного значения. Но перед тем как переводить строку в верхний регистр с помощью метода ToUpper, необходимо перевести значение свойства CanStop в строковый формат с помощью метода ToString, поскольку метод ToUpper применяется только к строковым значениям. Если вы не хотите переводить строку результата в верхний регистр, можете обойтись без вызова метода ToString, как и метода ToUpper. Результаты представлены на экране 1.

Отметим, что при ссылках на методы и свойства объектов их имена нечувствительны к регистру. К примеру, при вызове метода ToString можно использовать только строчные буквы (как в приведенных примерах), только прописные или как те, так и другие.

Всякий раз при определении коллекции в инструкции foreach мы, в сущности, реализуем конвейер. В приведенном выше примере конвейер формируется из выходных данных команды Get-Service. Но можно создавать и более сложные конвейеры, например такие:

foreach ($svc in Get-Service | where {$_.status -eq 'running' }) {    $svc.name + ": " +    $svc.canstop.tostring (). toupper () }

В этой команде выходные данные Get-Service передаются по конвейеру команде Where-Object (вызываемой с помощью псевдонима where), которая выбирает из значений, возвращаемых командой Get-Service, только те объекты service, свойство Status которых определяется как running. Для обращения к текущему значению в конвейере команда Where-Object использует встроенную переменную $_. На экране 2 представлены примерные результаты, возвращаемые с помощью данной команды.

Возможно, вы пришли к выводу, что заключать весь конвейер в скобки не всегда удобно. Вероятно, более целесообразно передавать объекты service переменной, а затем вызывать эту переменную в инструкции foreach:

$svcs = Get-Service | where {$_.status -eq 'running'} foreach ($svc in $svcs) {    $svc.name + ": " +    $svc.canstop.tostring ().   toupper () }

Нетрудно убедиться, что для вызова коллекции инструкция foreach использует переменные $svc. Эта команда возвращает те же результаты, что и предыдущая.

Команда ForEach-Object

Мы рассмотрели, как используется инструкция foreach для перебора элементов коллекции, но это еще не все. Наряду с данной инструкцией в оболочке PowerShell реализована команда ForEach-Object. И чтобы жизнь не казалась пользователям совсем уж безмятежной, слово foreach служит псевдонимом для обращения к упомянутой команде.

ForEach-Object получает коллекцию из конвейера и, подобно инструкции foreach, поочередно обращается ко всем ее элементам. Поясним сказанное на примере. Следующая команда возвращает те же результаты (показанные на экране 2), что были получены в результате выполнения двух предыдущих команд:

Get-Service | where {$_.status -eq 'running'} | foreach {    $_.name + ": " +    $_.canstop.tostring ().toupper () }

Эта инструкция начинается с передачи по конвейеру выходных данных Get-Service команде Where-Object. Далее коллекция, возвращенная командой Where-Object, передается по конвейеру команде ForEach-Object (для обращения к последней используется псевдоним foreach). Обратите внимание, что за псевдонимом foreach следует только блок сценария — никакого кода в скобках нет. Следствие этого различия между инструкцией foreach и командой ForEach-Object состоит в том, что необходимость определения переменной элементов отпадает. Вместо нее используется встроенная переменная $_. Во всех прочих отношениях оставшаяся часть блока сценария ничем не отличается от кода, использовавшегося в двух предыдущих примерах (отметим, что открывающую фигурную скобку следует размещать на той же строке, где указывается псевдоним foreach, иначе PowerShell будет рассматривать первую строку как завершенную инструкцию).

Но как же тогда оболочка PowerShell отличает ключевое слово foreach от псевдонима foreach? Если foreach стоит в начале инструкции, PowerShell рассматривает ее как ключевое слово и обрабатывает следующий за ним код как инструкцию foreach. Если же это слово указывается в другом месте, PowerShell рассматривает его как псевдоним команды ForEach-Object.

В среде PowerShell предусмотрен еще один псевдоним для ссылки на команду ForEach-Object; это знак процента (%). Так, инструкция

Get-Service | where {$_.status -eq 'running'} | % {    $_.name + ": "+    $_.canstop.tostring ().toupper () }

возвращает те же результаты, что и в предыдущем примере, хотя в ней вместо foreach используется символ %.

Различия

Итак, для получения одних и тех же результатов можно использовать как инструкцию foreach, так и команду ForEach-Object, но надо иметь в виду, что между ними существует несколько различий. Во-первых, как мы уже убедились, команда несколько проще, поскольку в случае ее применения не нужно создавать специальную переменную цикла — вместо нее используется встроенная переменная $_.

Второе отличие сводится к тому, каким образом среда PowerShell обрабатывает эти две инструкции. Когда PowerShell обрабатывает инструкцию PowerShell, она, перед тем как приступить к работе с отдельными величинами, генерирует всю коллекцию. Когда же оболочка обрабатывает команду ForEach-Object, она работает с каждым значением в момент соответствующего шага по конвейеру; таким образом, в каждый момент времени используются меньшие объемы памяти. В случаях, когда важно экономно расходовать ресурсы памяти, более целесообразно использовать команду.

Третье различие состоит в следующем. Если выходные данные команды ForEach-Object можно передать далее по конвейеру, то при использовании инструкции foreach такой возможности нет. В следующем фрагменте кода выходные данные команды ForEach-Object передаются команде Sort-Object:

Get-Service | where {$_.status -eq 'running'} | foreach {    $_.name + ": " +    $_.canstop.tostring ().toupper () } | sort -descending

Команда Sort-Object (вызываемая с использованием псевдонима sort) сортирует выходные данные конвейера в порядке убывания, как показано на экране 3.

Еще одно преимущество команды ForEach-Object по сравнению с инструкцией foreach состоит в том, что команда поддерживает три типа блоков сценария, как видно из следующего фрагмента кода:

Get-Service | where {$_.status -eq 'running'} | foreach {    $count = 0} {    $_.name + ": " +       $_.canstop.tostring ().toupper ()    $count ++} {Write-Host    "$count services are running."    Write-Host }

Первый блок сценария присваивает переменной $count значение 0. Эта переменная отслеживает число элементов в коллекции. Второй блок сценария получает значения свойств Name и CanStop для каждой службы и увеличивает значение переменной $count на 1. Третий блок сценария содержит указание распечатать сообщение, содержащее общее число служб, которое получено на основе последнего значения переменной $count.

При включении трех блоков сценария указанным выше способом PowerShell выполняет первый блок до первого выполнения цикла, второй блок выполняется по разу при каждом проходе, а третий блок — после выполнения последнего цикла. На экране 4 видно, как отображается общее число служб в соответствии с инструкциями последнего блока сценария.

На следующих уроках

Инструкция foreach и команда ForEach-Object — мощные инструменты для работы с коллекциями. Любой из них можно использовать для создания циклов, в ходе которых выполняется набор инструкций для каждого элемента коллекции. При составлении сценариев PowerShell придется часто пользоваться инструкциями foreach. И, как будет видно из следующих статей, вы сможете создавать гораздо более сложные команды, нежели те, что были показаны выше.

Роберт Шелдон ([email protected]) — технический консультант и автор книг по технологиям Microsoft Windows и базам данных

www.osp.ru

scripting - Использование переменной PowerShell из цикла For Each

Я пишу сценарий PowerShell, который пытается подключиться к списку серверов на порту 3389. Когда соединение выходит из строя, скрипт отправляет электронное письмо.

Линии (переменные), о которых идет речь, закомментированы в начале скрипта и будут работать только при ссылке в цикле. Не могли бы вы помочь мне понять, почему при отправке электронной почты из цикла, в то время как переменные $ subject и $ body находятся за пределами цикла, переменная $ server является только самым последним сервером в списке?

Мне кажется, что когда $ subject и $ body ссылаются на переменную $ server, она будет установлена на любой $ server, с которым в настоящее время работает цикл ForEach. Может быть, я вообще не понимаю правила написания сценариев?

Заранее спасибо!

# $subject = "$server is not accepting RDP connections!" # Why do these two lines have to be in the loop? # $body = " Hello, 'n 'n This is an automatically generated message indicating that an attempt to RDP to $server has failed. Please investigate ASAP. 'n 'n Thank you." $serversArray = @("host1","host2","host3") $port = 3389 $status = "" $emailFrom = "[email protected]" $emailTo = "[email protected]" # To address $relay = "smtp.example.com" $smtp=new-object Net.Mail.SmtpClient($relay) # For each loop that iterates through each server in the $serversArray array. This creates a generic socket connection and then calls it for the current server name. # If the socket connection returns True for the connected status, the connection is closed. # Otherwise, a failure email is sent. ForEach ($server in $serversArray) { $socket = new-object Net.Sockets.TcpClient $socket.Connect($server, $port) if ($socket.Connected) { $socket.Close() $status += "RDP port 3389 is open on $server 'n" } else { $subject = "$server is not accepting RDP connections!" # Everything works when this line is here, in the loop $body = " Hello, 'n 'n This is an automatically generated message indicating that an attempt to RDP to $server has failed. Please investigate ASAP. 'n 'n Thank you." # Everything works when this line is here, in the loop $smtp.Send($emailFrom, $emailTo, $subject, $body) $status += "RDP port 3389 is NOT open on $server 'n" } } echo $status источник поделиться

qaru.site

НОУ ИНТУИТ | Лекция | Сценарии Windows PowerShell

Аннотация: Описываются управляющие инструкции языка PowerShell (ветвления и циклы). Обсуждаются механизмы безопасности, применяемые при запуске сценариев PowerShell

Сценарии Windows PowerShell

До настоящего момента мы работали в оболочке PowerShell интерактивно, вводя команды и получая результат их выполнения. Перейдем теперь к рассмотрению сценариев, поддержка которых реализована в PowerShell в полной мере – от самых простых до очень сложных.

Сценарии PowerShell представляют собой текстовые файлы с расширением ps1, в которых записаны команды, операторы и другие конструкции языка PowerShell. В отличие от сценариев WSH и командных файлов интерпретатора Cmd.exe, сценарии PowerShell можно писать поэтапно, непосредственно в самой оболочке, перенося затем готовый код во внешний текстовый файл. Такой подход значительно упрощает изучение языка и отладку сценариев, так как можно сразу видеть результат выполнения отдельных частей сценария.

Как правило, при создании более-менее сложных сценариев возникает необходимость в управлении ходом выполнения программы. Для этого в языке PowerShell, как и в любом алгоритмическом языке, предусмотрены специальные инструкции.

Управляющие инструкции языка PowerShell

В языке PowerShell имеются элементы, позволяющие выполнить логическое сравнение и предпринять различные действия в зависимости от его результата, или дающие возможность повторять одну или несколько команд снова и снова.

Инструкция If … Elseif … Else

Логические сравнения лежат в основе практически всех алгоритмических языков программирования. В PowerShell при помощи инструкции If можно выполнять определенные блоки кода только в том случае, когда заданное условие имеет значение True (истина). Также можно задать одно или несколько дополнительных условий выполнения, если все предыдущие условия имели значение False. Наконец, можно задать дополнительный блок кода, который будет выполняться в том случае, если ни одно из условий не имеет значения True.

Синтаксис инструкции If в общем случае имеет следующий вид:

if (условие1) {блок_кода1} [elseif (условие2) {блок_кода2}] [else {блок_кода3}]

При выполнении инструкции If проверяется истинность условного выражения условие1.

Замечание

Условные выражения в PowerShell формируются, чаще всего, с помощью операторов сравнения и логических операторов, рассмотренных в лекции 17. Кроме того, важной особенностью языка является то, что в качестве условных выражений можно использовать конвейеры команд PowerShell.

Если условие1 имеет значение true, то выполняется блок_кода1, после чего PowerShell завершает выполнение инструкции If. Если условие1 имеет значение false, то PowerShell проверяет истинность условного выражения условие2. Если условие2 имеет значение true, то выполняется блок_кода2, после чего PowerShell завершает выполнение инструкции If. Если и условие1, и условие2 имеют значение false, то выполняется блок_кода3, а затем PowerShell завершает выполнение инструкции If.

Приведем пример использования инструкции If в интерактивном режиме работы. Запишем сначала в переменную $a число 10:

Сравним теперь значение переменной $a с числом 15 (в нашем примере используется командлет Write-Host, который выводит информацию на экран):

PS C:\> if ($a -eq 15) { >> Write-Host '"Значение $a равно 15' >> } >> else {Write-Host 'Значение $a не равно 15'} >> Значение $a не равно 15

Из данного примера также видно, что в оболочке PowerShell в интерактивном режиме можно выполнять инструкции, состоящие из нескольких строк (это может оказаться очень кстати при отладке сценариев).

Цикл While

В PowerShell поддерживаются несколько видов циклов. Самый простой из них – цикл While, в котором команды выполняются в до тех пор, пока проверяемое условие имеет значение True.

Инструкция While имеет следующий синтаксис:

while (условие){блок_команд}

При выполнении инструкции While оболочка PowerShell вычисляет раздел условие инструкции, прежде чем перейти к разделу блок_команд. Условие в инструкции принимает значения True или False. До тех пор, пока условие имеет значение True, PowerShell повторяет выполнение раздела блок_команд.

Замечание

Как и в инструкции If, в условном выражении цикла While может использоваться конвейер команд PowerShell.

Раздел блок_команд инструкции While содержит одну или несколько команд, которые выполняются каждый раз при входе и повторении цикла.

Например, следующая инструкция While отображает числа от 1 до 3, если переменная $val не была создана, или была создана и инициализирована значением 0.

while($val -ne 3) { $val++ Write-Host $val }

В данном примере условие (значение переменной $val не равно 3) имеет значение true, пока $val равно 0, 1 или 2. При каждом повторении цикла значение $val увеличивается на 1 с использованием унарного оператора увеличения значения ++ ( $val++ ). При последнем выполнении цикла значением $val становится 3. При этом проверяемое условие принимает значение false, и цикл завершается.

При интерактивной работе команды удобнее записывать в одной строке, например:

PS C:\> while($val -ne 3){$val++; Write-Host $val} 1 2 3while($val -ne 3){$val++; Write-Host $val}

Обратите внимание, что в качестве разделителя команд в PowerShell используется точка с запятой ( ; ).

Цикл Do … While

Цикл Do…While похож на цикл While, однако условие в нем проверяется не до блока команд, а после:

do{блок_команд}while (условие)

Например:

PS C:\> $val=0 PS C:\> do {$val++; Write-Host $val} while($val -ne 3) 1 2 3
Цикл For

Инструкция For в PowerShell реализует еще один тип циклов – цикл со счетчиком. Обычно цикл For применяется для итерации массива значений и выполнение действий на подмножестве этих значений. В PowerShell инструкция For используется не так часто, как в других языках программирования, так как коллекции объектов обычно удобнее обрабатывать с помощью инструкции Foreach. Однако, если необходимо знать, с каким именно элементом коллекции или массива мы работаем на данной итерации, то цикл For может помочь.

Синтаксис инструкции For:

for (инициация; условие; повторение){блок_команд}

Составные части цикла For имеют следующий смысл:

  • инициация — это одна или несколько разделяемых запятыми команд, выполняемых перед началом цикла. Данная часть цикла обычно используется для создания и присвоения начального значения переменной, которая будет затем основой для проверяемого условия в следующей части инструкции For.
  • условие — часть инструкции For, которая может принимать логическое значение true или false. PowerShell проводит оценку условия при каждом выполнении цикла For. Если результатом этой оценки является true, то выполняются команды в блоке блок_команд, после чего проводится новая оценка условия инструкции. Если результатом оценки условия вновь становится true, команды блока блок_команд выполняются вновь, и т.д., пока результатом проверки условия не станет false.
  • повторение — это одна или несколько разделяемых запятыми команд, выполняемых при каждом повторении цикла. Данная часть цикла обычно используется для изменения переменной, проверяемой внутри условия.
  • блок_команд — это набор из одной или нескольких команд, выполняющихся при вхождении в цикл или при повторении цикла. Содержимое блока блок_команд заключается в фигурные скобки.

Классический пример:

PS C:\> for ($i=0; $i -lt 5; $i++) { Write-Host $i } 0 1 2 3 4

www.intuit.ru

about_For

РАЗДЕЛ about_For КРАТКОЕ ОПИСАНИЕ Описывает команду языка, которую можно использовать для выполнения инструкций в зависимости от условия. ПОЛНОЕ ОПИСАНИЕ Инструкция For (называемая также циклом For) - это языковая конструкция, которая служит для создания цикла, в котором блок команд выполняется, пока заданное условие остается истинным. Типичным применением цикла For является перебор массива значений и выполнение действий на подмножестве этих значений. В большинстве случаев, если необходимо перебрать все значения в массиве, имеет смысл применять инструкцию Foreach. Синтаксис Ниже показан синтаксис инструкции For. for (<инициация>; <условие>; <повторение>) {<список_инструкций>} Заполнитель <инициация> представляет одну или несколько разделяемых запятыми команд, выполняемых перед началом цикла. Обычно элемент инструкции <инициация> используется для создания переменной и присвоения ей начального значения. Эта переменная затем будет использоваться для проверки условия в следующей части инструкции For. Заполнитель <условие> представляет часть инструкции For, которая принимает логическое значение true или false. Windows PowerShell вычисляет условие при каждом выполнении цикла For. Если результатом вычисления выражения является значение true, выполняется блок команд, а выражение вычисляется снова. Если условие по прежнему имеет значение true, команды в списке инструкций выполняются снова. Цикл повторяется, пока условие не примет значение false. Заполнитель <повторение> представляет одну или несколько команд, разделенных запятыми, которые выполняются при каждом повторении цикла. Обычно это используется для изменения переменной, проверяемой внутри части <условие> инструкции. Заполнитель <список_инструкций> представляет одну или несколько команд, выполняющихся при каждом входе в цикл или его повторении. Содержимое списка инструкций заключается в фигурные скобки. Примеры Как минимум, для инструкции For необходимы круглые скобки, заключающие блок <инициация>, <условие> и <повторение>, а также заключенная в фигурные скобки команда в блоке <список_инструкций>. Обратите внимание, что в приведенных ниже примерах намеренно приводится код за пределами инструкции For. В следующих примерах код помещен внутрь инструкции For. Например, следующая инструкция For будет постоянно отображать значение переменной $i до тех пор, пока команда не будет вручную прервана нажатием клавиш Ctrl+C. $i = 1 for (;;){Write-Host $i} В список инструкций можно добавить дополнительные команды, чтобы значение переменной $i увеличивалось на 1 при каждом выполнении цикла, как показано в следующем примере. for (;;){$i++; Write-Host $i} До тех пор, пока пользователь не прервет команду нажатием клавиш CTRL+C, эта инструкция будет постоянно отображать значение переменной $i, которое будет увеличиваться на 1 при каждом выполнении цикла. Вместо изменения значения переменной в списке инструкций цикла For можно воспользоваться блоком <повторение>, как показано ниже. $i=1 for (;;$i++){Write-Host $i} Эта инструкция по-прежнему будет повторяться бесконечно долго или до тех пор, пока пользователь не прервет команду нажатием клавиш CTRL+C. Задав соответствующее условие (с помощью блока <условие> инструкции For), можно завершить цикл For, когда условие примет значение false. В следующем примере цикл For выполняется, пока значение переменной $i меньше или равно 10. $i=1 for(;$i -le 10;$i++){Write-Host $i} Вместо создания и инициализации переменной за пределами инструкции For то же самое можно сделать внутри цикла For с помощью блока <инициация>. for($i=1; $i -le 10; $i++){Write-Host $i} В качестве разделителя в блоках <инициация>, <условие> и <повторение> инструкции For вместо точки с запятой можно использовать знаки возврата каретки. В следующем примере показана эта альтернативная форма инструкции For. for (<инициация> <условие> <повторение>){ <список_инструкций> } Такая альтернативная форма инструкции For используется в файлах скриптов Windows PowerShell и в командной строке Windows PowerShell. Но при вводе интерактивных команд с консоли удобнее использовать синтаксис инструкции For с точкой с запятой. Цикл For обладает большей гибкостью, чем цикл Foreach, поскольку он позволяет увеличивать значения в массиве или в коллекции при помощи шаблонов. В следующем примере переменная $i увеличивается на 2 в блоке <повторение> инструкции For. for ($i = 0; $i -ile 20; $i += 2) {Write-Host $i} СМ. ТАКЖЕ about_Comparison_Operators about_Foreach

winintro.ru

powershell - Создание цикла for для консолидации сценария с помощью командной строки powershell

Я работал над скриптом для установки продукта, который требует, чтобы несколько установщиков работали правильно. Некоторые требуют определенных функций для установки, в то время как другие являются необязательными. Мой скрипт управляется с помощью меню, что позволяет дополнительным установщикам и работает достаточно хорошо.

Большая часть кода используется повторно, и я хотел бы объединить его в один цикл for-loop, но мой PShell-foo еще не достиг уровня черного пояса. Я знаком с вводом CSV для переменных с переменной переменной, но думаю, что это можно сделать с помощью массива, сгенерированного в коде. Может ли один из вас обучить этот неофит?

Вот санированная версия текущего кода:

# Main Level Menu Choices switch (Read-Host $Main_Prompt) { 1 {$choice = 1} 2 {$choice = 2} 3 {$choice = 3} 4 {$choice = 4} 5 {$choice = 5} "q" {$choice = q} } if (($choice -eq "q"){ Write-host "Exiting!" exit } # Install 1 if (($choice -eq 1) -or ($choice -eq 5)){ Write-Host "Installing 1" $1Path = "$InstallSource\Folder1\file.msi" if (Test-Path $1Path) { $processInfo = New-Object System.Diagnostics.ProcessStartInfo "msiexec.exe" $processInfo.arguments = "/i '"$1Path'" /qr ARGUMENTS" $processMSI = New-Object System.Diagnostics.Process $processMSI.StartInfo = $processInfo $processMSI.Start() | Out-Null $processMSI.WaitforExit() if ($processMSI.ExitCode -ne 0) { Write-Host "The Installer generated error code:" $processMSI.ExitCode "Exiting!" exit } } Else { Write-host "Installer does not exist. Exiting!" exit } } # Install 2 if (($choice -eq 2) -or ($choice -eq 5)){ Write-Host "Installing 2" $2Path = "$InstallSource\Folder2\file.msi" $processInfo = New-Object System.Diagnostics.ProcessStartInfo "msiexec.exe" $processInfo.arguments = "/i '"$2Path'" /qr ARGUMENTS" $processMSI = New-Object System.Diagnostics.Process $processMSI.StartInfo = $processInfo # Write-host $ProcessInfo.arguments $processMSI.Start() | Out-Null $processMSI.WaitforExit() if ($processMSI.ExitCode -ne 0) { Write-Host "The Installer generated error code:" $processMSI.ExitCode "Exiting!" exit } } # Install 3 if (($choice -eq 3) -or ($choice -eq 5)){ Write-Host "Installing 3" $3Path = "$InstallSource\Folder3\file.exe" $processInfo = New-Object System.Diagnostics.ProcessStartInfo $3Path $processInfo.arguments = "/S /v'" ARGUMENTS'"" $processEXE = New-Object System.Diagnostics.Process $processEXE.StartInfo = $processInfo $processEXE.Start() | Out-Null $processEXE.WaitforExit() if ($processEXE.ExitCode -ne 0) { Write-Host "The Installer generated error code:" $processEXE.ExitCode "Exiting!" exit } } # Install 4 if (($choice -eq 4) -or ($choice -eq 5)){ Write-Host "Checking for the Desktop-Experience Feature" $DEStatus = Get-WindowsFeature Desktop-Experience if( $DEStatus.Installed -ne "True" ) { Write-Host "Installing Desktop-Experience Feature" Import-Module ServerManager Add-WindowsFeature -Name Desktop-Experience } Write-Host "Installing 4" $4Path = "$InstallSource\Folder4\file.exe" $processInfo = New-Object System.Diagnostics.ProcessStartInfo $4Path $processInfo.arguments = "ARGUMENTS" $processEXE = New-Object System.Diagnostics.Process $processEXE.StartInfo = $processInfo $processEXE.Start() | Out-Null $processEXE.WaitforExit() if ($processEXE.ExitCode -ne 0) { Write-Host "The Installer generated error code:" $processEXE.ExitCode "Exiting!" exit } # Install 5 Write-Host "Installing 5" $5Path = "$InstallSource\Folder5\file.exe" $processInfo = New-Object System.Diagnostics.ProcessStartInfo $5Path $processInfo.arguments = "ARGUMENTS" $processEXE = New-Object System.Diagnostics.Process $processEXE.StartInfo = $processInfo $processEXE.Start() | Out-Null $processEXE.WaitforExit() if ($processEXE.ExitCode -ne 0) { Write-Host "The Installer generated error code:" $processEXE.ExitCode "Exiting!" exit } } # Install 6 if (($choice -eq 3) -or ($choice -eq 5)){ Write-Host "Installing 6" $6Path = "$InstallSource\Folder6\file.exe" $processInfo = New-Object System.Diagnostics.ProcessStartInfo $6Path $processInfo.arguments = "ARGUMENTS" $processEXE = New-Object System.Diagnostics.Process $processEXE.StartInfo = $processInfo $processEXE.Start() | Out-Null $processEXE.WaitforExit() if ($processEXE.ExitCode -ne 0) { Write-Host "The Installer generated error code:" $processEXE.ExitCode "Exiting!" exit } } # Install 7 if (($choice -eq 4) -or ($choice -eq 5)){ Write-Host "Installing 7" $7Path = "$InstallSource\Folder7\file.exe" $processInfo = New-Object System.Diagnostics.ProcessStartInfo $7Path $processInfo.arguments = "ARGUMENTS" $processEXE = New-Object System.Diagnostics.Process $processEXE.StartInfo = $processInfo $processEXE.Start() | Out-Null $processEXE.WaitforExit() if ($processEXE.ExitCode -ne 0) { Write-Host "The Installer generated error code:" $processEXE.ExitCode "Exiting!" exit } }

EDIT: взяв код TheMadTechnician, я упростил его и запустил, чтобы посмотреть, что происходит. Вот тестовый код:

$App1 = "Write-host App1" $App2 = "Write-host App2" $App3 = "Write-host App3" $App4 = "Write-host App4" $App5 = "Write-host App5" $App6 = "Write-host App6" $App7 = "Write-host App7" # Main Level Menu Choices $Main_Prompt = ' [1] Choice1 [2] Choice2 [3] Choice3 [4] Choice4 [5] Choice5 [q] Quit ' switch (Read-Host $Main_Prompt) { {$_ -eq 1 -or $_ -eq 5} {$ToInstall += $App1} {$_ -eq 2 -or $_ -eq 5} {$ToInstall += $App2} {$_ -eq 3 -or $_ -eq 5} {$App3,$App4|%{If(!($ToInstall -contains $_)){$ToInstall += $_}}} {$_ -eq 4 -or $_ -eq 5} {$App5,$App6,$App7|%{If(!($ToInstall -contains $_)){$ToInstall += $_}}} {$_ -ieq "q"} {Write-host "Exiting!";Exit} } ForEach($App in ($ToInstall | Select -Unique)){ Invoke-Expression $App }

Вот результат (в блоке кода, потому что я еще не могу опубликовать изображения):

PS C:\temp> .\test.ps1 [1] Choice1 [2] Choice2 [3] Choice3 [4] Choice4 [5] Choice5 [q] Quit : 5 App1Write-host App2Write-host App3Write-host App4Write-host App5Write-host App6Write-host App7 PS C:\temp>

Похоже, что блок выполняет первую команду, а затем просто печатает остальную часть экрана. Я очень сильно из-за этого.

qaru.site