Часть 2: Конвейер (Pipeline), переменные, Get-Member, файл .ps1 и экспорт результатов
Дисклеймер. в редакторе пикабу нет редактора кода. Поэтому картинки и вырвиглазное форматирование. В конце статьи я дам ссылки на гитхаб . Там полноценная статья. Вы можете почитать тут, а примеры копировать из гитхаб
❗ Важно: Я пишу про PS7 (PowerShell 7). Он отличается от PS5 (PowerShell 5). Начиная с седьмой версии ps стал кросплатформенным. Из-за этого изменилось поведение некоторых команд.
В первой части мы установили ключевой принцип: PowerShell работает с объектами, а не с текстом. Этот пост посвящен некоторым важным инструментам PowerShell: научимся передавать объекты по конвейеру, анализировать их с помощью Get-Member, сохранять результаты в переменные и автоматизировать все это в файлах скриптов (.ps1) с экспортом результатов в удобные форматы.
1. Что такое конвейер (|)?
Конвейер в PowerShell это механизм передачи полноценных .NET объектов (а не просто текста) от одной команды к другой, где каждый следующий командлет получает структурированные объекты со всеми их свойствами и методами.
Символ | (вертикальная черта) — это оператор конвейера. Его задача — взять результат (вывод) команды, стоящей слева от него, и передать его на вход команде, стоящей справа.
Команда 1 (создает объекты) → | → Команда 2 (получает и обрабатывает объекты) → | → Команда 3 (получает обработанные объекты) → | ...
Классический UNIX-конвейер: Поток текста
В bash по конвейеру передается поток байтов, который обычно интерпретируется как текст.
Найти все процессы 'nginx' и посчитать их количество
> ps -ef | grep 'nginx' | wc -l
Здесь `ps` выводит текст, `grep` фильтрует этот текст, а `wc` считает строки. Каждая утилита ничего не знает о "процессах", она работает только со строками.
PowerShell-конвейер: Поток объектов
Пример: Давайте получим все процессы, отсортируем их по использованию CPU и выберем 5 самых "прожорливых".
> Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 5
Здесь Get-Process создает объекты процессов. Sort-Object получает эти объекты и сортирует их по свойству CPU. Select-Object получает отсортированные объекты и выбирает первые 5.
Вы наверняка заметили в команде слова, начинающиеся с дефиса (-): -Property, -Descending, -First. Это параметры. Параметры — это настройки, переключатели и инструкции для командлета. Они позволяют управлять тем, КАК команда будет выполнять свою работу. Без параметров команда работает в режиме по умолчанию, а с параметрами вы даете ей конкретные указания.
Основные типы параметров:
Параметр со значением: требует дополнительной информации.
-Property CPU: Мы говорим Sort-Object, по какому свойству сортировать. CPU — это значение параметра.
-First 5: Мы говорим Select-Object, сколько объектов выбрать. 5 — это значение параметра.
Параметр-переключатель (флаг): Не требует значения. Само его наличие в команде включает или выключает определенное поведение.
-Descending: Этот флаг говорит Sort-Object изменить порядок сортировки на обратный (от большего к меньшему). Ему не нужно дополнительное значение — он сам по себе инструкция.
> Get-Process -Name 'svchost' | Measure-Object
Эта команда отвечает на очень простой вопрос: "Сколько именно процессов с именем svchost.exe сейчас запущено в моей системе?"
Разбор по шагам
Шаг 1: Get-Process -Name 'svchost'
Эта часть команды обращается к операционной системе и просит найти все без исключения запущенные процессы, у которых имя исполняемого файла — svchost.exe. В отличие от процессов типа notepad (которых обычно один или два), процессов svchost в системе всегда много. Команда вернет массив (коллекцию) объектов, где каждый объект — это отдельный, полноценный процесс svchost со своим уникальным ID, использованием памяти и т.д. PowerShell нашел в системе, например, 90 процессов svchost и теперь держит в руках коллекцию из 90 объектов.
Шаг 2: | (Оператор конвейера)
Этот символ берет коллекцию из 90 объектов svchost, полученную на первом шаге, и начинает передавать их по одному на вход следующей команде.
Шаг 3: Measure-Object
Поскольку мы вызвали Measure-Object без параметров (таких как -Property, -Sum и т.д.), он выполняет свою операцию по умолчанию — просто считает количество "предметов", которые ему передали. Раз, два, три ... После того как все объекты посчитаны, Measure-Object создает свой собственный объект-результат, в котором есть свойство Count, равное итоговому числу.
Count: 90 — это и есть ответ на наш вопрос. Запущено 90 процессов svchost. Остальные поля пустые, потому что мы не просили Measure-Object выполнять более сложные вычисления.
Пример с svchost и параметрами
Давайте изменим нашу задачу. Теперь мы хотим не просто посчитать процессы svchost, а узнать, сколько всего оперативной памяти (в мегабайтах) они потребляют вместе.
Для этого нам понадобятся параметры:
-Property WorkingSet64: Эта инструкция говорит Measure-Object: "Из каждого объекта svchost, который к тебе придет, возьми числовое значение из свойства WorkingSet64 (это использование памяти в байтах)".
-Sum: Эта инструкция-флаг говорит: "Сложи все эти значения, которые ты взял из свойства WorkingSet64".
Наша новая команда будет выглядеть так:
> Get-Process -Name 'svchost' | Measure-Object -Property WorkingSet64 -Sum
Get-Process найдет количество объектов svchost.
Конвейер | передаст их в Measure-Object.
Но теперь Measure-Object работает по-новому:
Он берет первый объект svchost, смотрит его свойство .WorkingSet64 (например, 25000000 байт) и запоминает это число.
Берет второй объект, смотрит его .WorkingSet64 (например, 15000000 байт) и прибавляет к предыдущему.
...и так далее для всех объектов.
В итоге Measure-Object создаст объект-результат, но теперь он будет другим.
Count: 92: Количество объектов.
Sum: 1661890560: Это общая сумма всех значений WorkingSet64 в байтах.
Property: WorkingSet64: Это поле теперь тоже заполнено, оно информирует нас, какое именно свойство было использовано для вычислений.
2. Переменные (Обычные и специальная $_)
Переменная — это именованное хранилище в памяти, которое содержит какое-либо значение.
Этим значением может быть что угодно: текст, число, дата или, что самое важное для PowerShell, целый объект или даже коллекция объектов. Имя переменной в PowerShell всегда начинается со знака доллара ($). Примеры: $name, $counter, $processList.
Специальная переменная $_?
$_ — это сокращение для "текущий объект" или "вот эта штука". Представьте себе конвейер на заводе. По нему едут разные детали (объекты).
$_ — это та самая деталь, которая находится прямо сейчас перед вами (или перед роботом-обработчиком).
Источник (Get-Process) — высыпает на конвейер целую коробку с деталями (всеми процессами).
Конвейер (|) — заставляет эти детали двигаться по ленте по одной.
Обработчик (Where-Object или ForEach-Object) — это робот, который смотрит на каждую деталь.
Переменная $_ — это та самая деталь, которая сейчас находится в "руках" у робота.
Когда робот закончит с одной деталью, конвейер подает ему следующую, и $_ теперь будет указывать уже на нее.
Давайте посчитаем, сколько всего памяти используют процессы svchost, и выведем результат на монитор.
1. Выполняем команду и сохраняем ее сложный объект-результат в переменную $svchostMemory
> $svchostMemory = Get-Process -Name svchost | Measure-Object -Property WorkingSet64 -Sum
2. Теперь мы можем работать с сохраненным объектом. Достаем из него свойство Sum
> $memoryInMB = $svchostMemory.Sum / 1MB
3. Выводим результат на экран, используя новую переменную
> Write-Host "Все процессы svchost используют $memoryInMB МБ памяти."
Write-Host — это специализированный командлет, чья единственная задача — показать текст непосредственно пользователю в консоли.
Строка в двойных кавычках: "..." - текстовая строка, которую мы передаем командлету Write-Host в качестве аргумента. Почему двойные, а не одинарные кавычки?
В PowerShell есть два типа кавычек:
Одинарные ('...'): Создают буквальную строку. Все, что внутри них, воспринимается как обычный текст, без исключений.
Двойные ("..."): Создают расширяемую (или подстановочную) строку. PowerShell "сканирует" такую строку на предмет переменных (начинающихся с $) и подставляет на их место их значения.
$memoryInMB. Это переменная, в которую мы на предыдущем шаге нашего скрипта положили результат вычислений. Когда Write-Host получает строку в двойных кавычках, происходит процесс, называемый "подстановка переменных" (String Expansion):
PowerShell видит текст "Все процессы svchost используют ".
Затем он натыкается на конструкцию $memoryInMB. Он понимает, что это не просто текст, а переменная.
Он заглядывает в память, находит значение, хранящееся в $memoryInMB (например, 1585.52).
Он подставляет это значение прямо в строку.
Затем он добавляет оставшуюся часть текста: " МБ памяти.".
В итоге, в Write-Host передается уже готовая, собранная строка: "Все процессы svchost используют 1585.52 МБ памяти.".
Запустите блокнот!
Находим процесс Блокнота и сохраняем его в переменную $notepadProcess
> $notepadProcess = Get-Process -Name notepad
Обращаемся к свойству 'Id' этого объекта через точку и выводим его
> Write-Host "ID процесса 'Блокнот' равен: $($notepadProcess.Id)"
❗ Важно: Write-Host "ломает" конвейер. Текст, выведенный им, нельзя передать дальше по конвейеру для обработки. Он предназначен только для отображения.
3. Get-Member (Инспектор объектов)
Мы знаем, что по конвейеру "текут" объекты. Но как узнать, из чего они состоят? Какие у них есть свойства и какие действия (методы) с ними можно совершать?
командлет Get-Member (псевдоним: gm) главный инструмент для исследования. Прежде чем работать с объектом, пропустите его через Get-Member, чтобы увидеть все его возможности.
Давайте проанализируем объекты, которые создает Get-Process:
> Get-Process | Get-Member
Разберем каждую часть вывода Get-Member.
TypeName: System.Diagnostics.Process - Это полное, официальное "имя типа" объекта из библиотеки .NET. Это его "паспорт". Эта строка говорит вам, что все объекты, которые возвращает Get-Process, являются объектами типа System.Diagnostics.Process. Это гарантирует, что у них у всех будет одинаковый набор свойств и методов. Вы можете загуглить "System.Diagnostics.Process", чтобы найти официальную документацию Microsoft с еще более подробной информацией.
Колонка 1: Name
Это простое, человекочитаемое имя свойства, метода или другого "члена" объекта. Именно это имя вы будете использовать в своем коде для доступа к данным или выполнения действий.
Колонка 2: MemberType (Тип объекта)
Это самая важная для понимания колонка. Она классифицирует, чем является каждый объект. Это его "должность", которая говорит вам, КАК его использовать.
Property (Свойство): характеристика или порция данных, хранящаяся внутри объекта. Вы можете "прочитать" ее значение.
Примеры на скриншоте: BasePriority, HandleCount, ExitCode. Это просто данные, которые можно посмотреть.
Method (Метод): ДЕЙСТВИЕ, которое можно совершить с объектом. Методы всегда вызываются с круглыми скобками ().
Примеры на скриншоте: Kill, Refresh, WaitForExit. Вы бы написали $process.Kill() или $process.Refresh().
AliasProperty (Псевдоним свойства): дружелюбный псевдоним для другого, более длинного свойства. PowerShell добавляет их для удобства и краткости.
Примеры на скриншоте: WS — это короткий псевдоним для WorkingSet64. Name — для ProcessName. VM — для VirtualMemorySize64.
Event (Событие): УВЕДОМЛЕНИЕ о том, что что-то произошло, на которое можно "подписаться".
Пример на скриншоте: Exited. Ваш скрипт может "слушать" это событие, чтобы выполнить какое-то действие сразу после того, как процесс завершится.
CodeProperty и NoteProperty: специальные типы свойств, часто добавляемые самим PowerShell для удобства. CodeProperty вычисляет свое значение "на лету", а NoteProperty — это простое свойство-заметка, добавленное к объекту.
Колонка 3: Definition (Определение)
Это техническое определение или "подпись" члена. Она дает вам точные детали для его использования. Ее содержимое зависит от MemberType:
Для AliasProperty: Показывает, чему равен псевдоним. Это невероятно полезно!
Пример на скриншоте: WS = WorkingSet64. Вы сразу видите, что WS — это просто короткая запись для WorkingSet64.
Для Property: Показывает тип данных, который хранится в свойстве (например, int для целого числа, string для текста, datetime для даты и времени), и что можно с ним делать ({get;} — только читать, {get;set;} — читать и изменять).
Пример на скриншоте: int BasePriority {get;}. Это целочисленное свойство, которое можно только прочитать.
Для Method: Показывает, что метод возвращает (например, void — ничего, bool — true/false) и какие параметры (входные данные) он принимает в скобках.
Пример на скриншоте: void Kill(). Это значит, что метод Kill ничего не возвращает и может быть вызван без параметров. Также есть вторая версия void Kill(bool entireProcessTree), которая принимает логическое значение (true/false).
В виде таблицы
Пример: Работа с окнами процессов
1. Проблема:
"Я открыл много окон Блокнота. Как мне программно свернуть все, кроме главного, а затем закрыть только то, у которого в заголовке есть слово 'Untitled'?"
Откройте несколько экземпляров блокнота (Windows Notepad) на компьютере
2. Исследование с Get-Member:
Нам нужно найти свойства, связанные с окном и его заголовком.
> Get-Process -Name notepad | Get-Member
Анализ результата Get-Member:
Листая свойства, мы находим MainWindowTitle. Тип string. Отлично, это заголовок главного окна!
В методах мы видим CloseMainWindow(). Это более "мягкий" способ закрыть окно, чем Kill().
Также в методах есть WaitForInputIdle(). Звучит интересно, возможно, это поможет дождаться, пока процесс будет готов к взаимодействию.
Get-Member показал нам свойство MainWindowTitle, которое является ключом к решению задачи и позволяет взаимодействовать с процессами на основе состояния их окон, а не просто по имени.
3. Решение:
Теперь мы можем построить логику, основанную на заголовке окна.
Пример: Найти родительский процесс
1. Проблема:
"Иногда я вижу в системе много дочерних процессов chrome.exe. Как мне узнать, какой из них является главным, "родительским" процессом, который их всех запустил?"
2. Исследование с Get-Member:
Нам нужно найти что-то, что связывает один процесс с другим.
> Get-Process -Name chrome | Select-Object -First 1 | Get-Member
Анализ результата Get-Member:
Внимательно просматривая список, мы находим свойство типа CodeProperty с именем Parent.
Его определение (Definition) — System.Diagnostics.Process Parent{get=GetParentProcess;}. Это вычисляемое свойство, которое при обращении к нему возвращает объект родительского процесса.
3. Решение:
Теперь мы можем написать скрипт, который для каждого процесса chrome будет выводить информацию о его родителе.
Мы сразу видим, что процессы с ID 4756, 7936, 8268 и 9752 были запущены процессом с ID 14908. Также можно заметить интересный случай с процессом ID: 7252, у которого родительский процесс не определился (возможно, родитель уже успел завершиться к моменту проверки). Модификация скрипта с проверкой if ($parent) аккуратно обрабатывает этот случай, не вызывая ошибки. Get-Member помог нам обнаружить "скрытое" свойство Parent, которое предоставляет мощные возможности для анализа иерархии процессов.
4. Файл .ps1 (Создание скриптов)
Когда ваша цепочка команд становится полезной, вы захотите сохранить ее для многократного использования. Для этого и нужны скрипты — текстовые файлы с расширением .ps1.
Разрешение на запуск скриптов
По умолчанию в Windows запрещен запуск локальных скриптов. Чтобы это исправить для текущего пользователя, выполните один раз в PowerShell от имени администратора:
> Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Это безопасная настройка, которая разрешает запускать ваши собственные скрипты и скрипты, подписанные доверенным издателем.
Пример скрипта system_monitor.ps1
Создайте файл с таким именем и вставьте в него код ниже. Этот скрипт собирает информацию о системе и генерирует отчеты.
Примечание: функция Export-Results будет определена в следующем разделе как пример хорошей практики.
5. Экспорт результатов
Чистые данные — это хорошо, но часто их нужно представить в удобном для человека или другой программы виде. PowerShell предлагает множество командлетов для экспорта.
Дополнение к скрипту: функция экспорта
Давайте добавим в наш скрипт system_monitor.ps1 функцию, которая будет заниматься экспортом. Поместите этот код перед вызовом Export-Results.
код на github
Теперь наш скрипт не просто собирает данные, но и аккуратно сохраняет их в двух форматах: CSV для анализа и HTML для быстрого просмотра.
Заключение
Конвейер (|) — главный инструмент для объединения команд и обработки объектов.
Get-Member — анализ объектов, который показывает, из чего они состоят.
Переменные ($var, $_) позволяют сохранять данные и обращаться к текущему объекту в конвейере.
Файлы .ps1 превращают команды в переиспользуемые инструменты автоматизации.
Командлеты экспорта (Export-Csv, ConvertTo-Html) Экспортируют данные в соответствующем формате.
В следующей части мы применим эти знания для навигации и управления файловой системой, исследуя объекты System.IO.DirectoryInfo и System.IO.FileInfo.
Полезно? Подпишись.
Понравилось — ставь «+»
Удачи! 🚀