Практические команды PowerShell — расширяем набор маленьких инструментов для повседневной работы

· · PowerShell, Windows, Командная строка, Автоматизация, Повышение эффективности, Использование существующих активов

1. Что нужно понять в первую очередь

Освоив основы PowerShell, дальше стоит расширять набор «маленьких инструментов, которые используются часто» — именно это делает работу практичной.

Под маленькими инструментами здесь понимаются не короткие псевдонимы. В этой статье вместо сокращений вроде ls и cat используются полные имена команд: Get-ChildItem, Get-Content, Select-String.

Опираясь на это, мы сделаем так, чтобы следующие задачи выполнялись одной или несколькими строками:

Посчитать количество
Проверить общий размер
Убрать дубликаты
Агрегировать по категориям
Извлечь только нужные значения
Сравнить два набора результатов
Посмотреть строки вокруг найденной в логе строки
Отправить результат в буфер обмена
Сохранить вывод, одновременно показывая его на экране
Проверить переменные окружения и сеть

PowerShell полезен даже без написания длинных скриптов. Начните с расширения набора «часто используемых строительных блоков», чтобы постепенно комбинировать их в повседневной работе.

Важнее не запоминать команды наизусть, а уметь вспомнить: «в такой ситуации подходит вот этот блок».

Код, встречающийся в этой статье, опубликован на GitHub как готовый к запуску набор примеров, организованный по темам в соответствии со структурой глав (скрипты-рецепты и тесты Pester).

powershell-practical-command-recipes - komurasoft-blog-samples (GitHub)

2. Подход в этой статье

Три исходные предпосылки:

  1. Писать команды полными именами
  2. Строить работу в основном вокруг команд для чтения
  3. Изменяющие команды всегда сочетать с проверкой объекта и -WhatIf

Например, вместо того чтобы сразу учить команду удаления файлов, безопаснее сначала усвоить такой порядок:

Посмотреть объекты
  → отфильтровать по условию
  → посчитать количество
  → при необходимости сохранить в CSV
  → проверить план изменений
  → выполнить

В этой статье в центре внимания не сильные операции вроде удаления и остановки, а исследование, агрегация, сравнение и вывод.

3. Карта часто используемых команд

Основные команды, которые понадобятся в этот раз.

Что нужно сделать Команда
Посчитать количество или сумму Measure-Object
Убрать дубликаты Sort-Object -Unique
Агрегировать по категориям Group-Object
Вывести только нужные столбцы Select-Object
Обработать каждый элемент ForEach-Object
Искать строки Select-String
Сравнить два набора результатов Compare-Object
Вычислить хеш Get-FileHash
Использовать буфер обмена Set-Clipboard / Get-Clipboard
Вывести и на экран, и в файл Tee-Object
Измерить время выполнения Measure-Command
Посмотреть детали ошибки Get-Error
Проверить сетевую доступность Test-Connection / Test-NetConnection

Не обязательно учить всё сразу. Рекомендуем запоминать команды в паре с целью: «чтобы посчитать — Measure-Object», «чтобы сгруппировать — Group-Object», «чтобы сравнить — Compare-Object».

4. Подсчёт количества — Measure-Object

Самая частая операция — подсчёт количества.

Посчитать число файлов в текущей папке.

Get-ChildItem . -File | Measure-Object

Чтобы учесть и подпапки.

Get-ChildItem . -File -Recurse | Measure-Object

Count в результате Measure-Object — это и есть количество.

Чтобы увидеть суммарный размер файлов, агрегируйте Length.

Get-ChildItem . -File -Recurse |
  Measure-Object -Property Length -Sum

Однако в таком виде значение выражено в байтах и не очень удобно для восприятия, поэтому переведём его в гигабайты.

$size = Get-ChildItem . -File -Recurse |
  Measure-Object -Property Length -Sum

[math]::Round($size.Sum / 1GB, 2)

Это удобно, когда нужно примерно оценить общий размер папки.

5. Заодно смотрим максимум и среднее

Measure-Object умеет выводить не только сумму, но и максимум со средним значением.

Get-ChildItem . -File |
  Measure-Object -Property Length -Sum -Average -Maximum -Minimum

Это пригодится, чтобы увидеть разброс размеров файлов.

Также можно просуммировать накопленное процессорное время процессов. Свойство CPU у Get-Process — это не текущая загрузка, а число секунд процессорного времени, использованного с момента запуска процесса.

Get-Process |
  Measure-Object -Property CPU -Sum

Однако это значение накопительное, а не мгновенная загрузка CPU, поэтому оно зависит от момента запуска команды и времени работы процессов.

Для целей исследования этого достаточно, но для строгого мониторинга нужны специализированные инструменты мониторинга и продуманная схема логирования.

6. Извлечение только значений — Select-Object -ExpandProperty

Select-Object часто используется для выбора столбцов.

Get-Process |
  Select-Object Name, Id, CPU

Это выводит «объекты со столбцами Name, Id и CPU».

Если же нужны только значения, используется -ExpandProperty.

Get-Process -Name notepad -ErrorAction SilentlyContinue |
  Select-Object -ExpandProperty Id

То же самое при извлечении только имён файлов.

Get-ChildItem . -File |
  Select-Object -ExpandProperty Name

Когда результат команды нужно передать другой команде или в текстовый файл, -ExpandProperty удобен.

Например, сохраним только список имён файлов.

Get-ChildItem . -File |
  Select-Object -ExpandProperty Name |
  Set-Content .\file-names.txt -Encoding UTF8

Select-Object Name и Select-Object -ExpandProperty Name похожи, но форма результата различается.

Select-Object Name
  → таблица со столбцом Name

Select-Object -ExpandProperty Name
  → сами значения Name

Если результат нужен в CSV, лучше оставить его как столбец, а если нужно передать в дальнейшую обработку строк — удобнее просто значения.

7. Удаление дубликатов — Sort-Object -Unique

Допустим, нужен список только расширений файлов.

Get-ChildItem . -File |
  Select-Object -ExpandProperty Extension |
  Sort-Object -Unique

При выполнении отображаются только уникальные расширения без повторов, например .txt, .log, .csv.

Точно так же можно посмотреть только уникальные имена процессов.

Get-Process |
  Select-Object -ExpandProperty ProcessName |
  Sort-Object -Unique

Пример извлечения только списка названий отделов из CSV со списком пользователей.

Import-Csv .\users.csv |
  Select-Object -ExpandProperty Department |
  Sort-Object -Unique

Когда «список загромождён множеством одинаковых значений», удобно сразу вспомнить про Sort-Object -Unique.

8. Агрегация по категориям — Group-Object

Когда нужно не просто убрать дубликаты, а увидеть «сколько чего», используется Group-Object.

Посчитаем число файлов по каждому расширению.

Get-ChildItem . -File |
  Group-Object -Property Extension -NoElement |
  Sort-Object -Property Count -Descending

Добавление -NoElement убирает содержимое каждой группы, оставляя только удобные для чтения счётчики.

Посчитаем по статусу службы.

Get-Service |
  Group-Object -Property Status -NoElement

Посчитаем по уровню записи в журнале событий.

Get-WinEvent -LogName System -MaxEvents 500 |
  Group-Object -Property LevelDisplayName -NoElement

Group-Object хорошо подходит для того, чтобы охватить общую картину в начале исследования.

Каких расширений больше всего
Служб в каком состоянии больше всего
Каких уровней ошибок больше всего
В каком отделе больше всего данных

Используйте эту команду, когда сначала хочется увидеть распределение, а не сразу погружаться в детали.

9. Создание нужных столбцов — добавление вычисляемых столбцов

Размер файла доступен через Length, но единица измерения — байты. Смотреть на число байт каждый раз утомительно, поэтому удобно создать столбец, переведённый в МБ или КБ.

Get-ChildItem . -File |
  Select-Object Name, @{Name="SizeKB"; Expression={ [math]::Round($_.Length / 1KB, 1) }}, LastWriteTime

Пример с учётом подпапок, показывающий сначала самые большие файлы.

Get-ChildItem . -File -Recurse |
  Sort-Object -Property Length -Descending |
  Select-Object -First 20 FullName, @{Name="SizeMB"; Expression={ [math]::Round($_.Length / 1MB, 2) }}, LastWriteTime

@{Name="..."; Expression={ ... }} — это синтаксис создания вычисляемого столбца. Сначала он выглядит немного длинным, но на практике используется постоянно.

Например, так можно вывести использование памяти процессом в МБ.

Get-Process |
  Sort-Object -Property WorkingSet -Descending |
  Select-Object -First 10 Name, Id, @{Name="MemoryMB"; Expression={ [math]::Round($_.WorkingSet / 1MB, 1) }}

В отчётах столбец вроде SizeMB гораздо понятнее исходного Length.

10. Просмотр в порядке новых, старых и больших

В PowerShell сортировка выполняется через Sort-Object.

Посмотрим недавно изменённые файлы.

Get-ChildItem . -File |
  Sort-Object -Property LastWriteTime -Descending |
  Select-Object -First 10 Name, LastWriteTime

Посмотрим старые файлы.

Get-ChildItem . -File |
  Sort-Object -Property LastWriteTime |
  Select-Object -First 10 Name, LastWriteTime

Посмотрим большие файлы.

Get-ChildItem . -File -Recurse |
  Sort-Object -Property Length -Descending |
  Select-Object -First 10 FullName, Length

Эти три варианта используются постоянно.

От новых к старым → LastWriteTime -Descending
От старых к новым → LastWriteTime
От больших к меньшим → Length -Descending

Это частые спутники при исследовании логов, очистке ненужных файлов и проверке использования диска.

11. Команды Format-* используйте только в конце, для отображения

Чтобы улучшить читаемость на экране, используются Format-Table и Format-List.

Get-Process |
  Sort-Object -Property WorkingSet -Descending |
  Select-Object -First 10 Name, Id, WorkingSet |
  Format-Table -AutoSize

Для просмотра деталей одного элемента удобен Format-List.

Get-Process -Id $PID |
  Format-List *

Однако Format-Table и Format-List — это «для отображения».

Безопаснее не использовать их перед выводом в CSV или перед обработкой следующей командой.

# Избегать
Get-Process |
  Format-Table Name, Id |
  Export-Csv .\process.csv -NoTypeInformation

Для вывода в CSV выбирайте столбцы через Select-Object.

# Правильно
Get-Process |
  Select-Object Name, Id |
  Export-Csv .\process.csv -NoTypeInformation -Encoding UTF8

Если сомневаетесь, рассуждайте так:

Только посмотреть на экране → Format-Table / Format-List
Использовать дальше        → Select-Object

12. Немного упрощаем поиск по логам — Select-String

Для поиска строк используется Select-String.

Select-String -Path .\logs\*.log -Pattern "ERROR"

Можно указать сразу несколько шаблонов.

Select-String -Path .\logs\*.log -Pattern "ERROR", "WARN", "FATAL"

Чтобы увидеть окружающие строки, используйте -Context.

Select-String -Path .\logs\*.log -Pattern "ERROR" -Context 2

Это дополнительно показывает две строки до и после найденной, что удобно, когда нужно понять контекст вокруг ошибки.

Чтобы искать буквальную строку, используйте -SimpleMatch.

Select-String -Path .\logs\*.log -Pattern "[ERROR]" -SimpleMatch

-Pattern в Select-String по умолчанию трактуется как регулярное выражение. Если нужно искать такие символы, как [ или ., буквально, добавление -SimpleMatch делает намерение понятнее.

Для поиска с учётом регистра.

Select-String -Path .\logs\*.log -Pattern "Error" -CaseSensitive

Результаты поиска тоже можно сохранить в CSV.

Select-String -Path .\logs\*.log -Pattern "ERROR" |
  Select-Object Path, LineNumber, Line |
  Export-Csv .\error-lines.csv -NoTypeInformation -Encoding UTF8

13. Замена строк

Для замены части строки используется -replace.

"server01.example.com" -replace "\.example\.com$", ""

При замене содержимого файла безопаснее не перезаписывать исходный файл сразу. Сначала выведем в отдельный файл.

(Get-Content .\appsettings.json -Raw) -replace "localhost", "db01" |
  Set-Content .\appsettings.preview.json -Encoding UTF8

После проверки при необходимости перенесите изменения в исходный файл.

В конфигурационных файлах и массовых заменах слишком большое число совпадений приводит к авариям. Безопаснее сначала проверить объекты через Select-String, а уже потом выполнять замену.

Select-String -Path .\appsettings.json -Pattern "localhost" -SimpleMatch

14. Сравнение двух результатов — Compare-Object

Compare-Object — команда для просмотра различий между двумя списками.

Например, сравним списки имён файлов до и после изменений.

Compare-Object `
  -ReferenceObject (Get-Content .\before.txt) `
  -DifferenceObject (Get-Content .\after.txt)

Значения SideIndicator такие:

Отображение Значение
<= Есть только слева, то есть в ReferenceObject
=> Есть только справа, то есть в DifferenceObject
== Есть в обоих. Отображается при указании -IncludeEqual

Пример сохранения и сравнения списков имён файлов из папок.

Get-ChildItem .\before -File |
  Select-Object -ExpandProperty Name |
  Set-Content .\before-list.txt -Encoding UTF8

Get-ChildItem .\after -File |
  Select-Object -ExpandProperty Name |
  Set-Content .\after-list.txt -Encoding UTF8

Compare-Object `
  -ReferenceObject (Get-Content .\before-list.txt) `
  -DifferenceObject (Get-Content .\after-list.txt)

Можно посмотреть и изменения в списке служб.

Get-Service |
  Select-Object Name, Status |
  Export-Csv .\services-before.csv -NoTypeInformation -Encoding UTF8

После изменения выведем ещё раз.

Get-Service |
  Select-Object Name, Status |
  Export-Csv .\services-after.csv -NoTypeInformation -Encoding UTF8

Сравним.

Compare-Object `
  -ReferenceObject (Import-Csv .\services-before.csv) `
  -DifferenceObject (Import-Csv .\services-after.csv) `
  -Property Name, Status

Если сохранять разницу «до/после» после каждой задачи, расследование и отчётность становятся проще.

15. Проверка хеша файла — Get-FileHash

Когда нужно убедиться, что загруженный или распространяемый файл совпадает с оригиналом, используются хеш-значения.

Get-FileHash .\installer.exe -Algorithm SHA256

Пример проверки всех файлов в папке сразу.

Get-ChildItem .\downloads -File |
  ForEach-Object { Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256 }

Можно сохранить результаты и в CSV.

Get-ChildItem .\downloads -File |
  ForEach-Object { Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256 } |
  Select-Object Path, Hash |
  Export-Csv .\hashes.csv -NoTypeInformation -Encoding UTF8

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

16. Отправка в буфер обмена — Set-Clipboard

Когда результат команды нужно вставить в письмо или чат, удобно отправить его в буфер обмена.

Get-Service |
  Select-Object Name, Status |
  ConvertTo-Csv -NoTypeInformation |
  Set-Clipboard

Чтобы посмотреть содержимое буфера обмена.

Get-Clipboard

Чтобы вставить в Excel с разделителем табуляции, задайте разделитель ConvertTo-Csv как табуляцию.

Get-Process |
  Select-Object Name, Id, CPU |
  ConvertTo-Csv -NoTypeInformation -Delimiter "`t" |
  Set-Clipboard

Если результат нужен только для временного обмена, это проще, чем каждый раз создавать файл. Но если нужен постоянный след, сохраните его в файл через Export-Csv.

17. Вывод и на экран, и в файл — Tee-Object

Когда нужно видеть результат на экране и одновременно сохранять его в файл, используется Tee-Object.

Get-Process |
  Sort-Object -Property WorkingSet -Descending |
  Select-Object -First 10 Name, Id, WorkingSet |
  Tee-Object -FilePath .\top-process.txt

Tee-Object направляет результат конвейера в двух направлениях.

Вывести на экран
Дополнительно сохранить в файл

Это подходит для сценария «смотреть и одновременно фиксировать» во время исследования. Однако если результат нужно позже открыть в Excel, удобнее Export-Csv.

Get-Process |
  Sort-Object -Property WorkingSet -Descending |
  Select-Object -First 10 Name, Id, WorkingSet |
  Export-Csv .\top-process.csv -NoTypeInformation -Encoding UTF8

Проще запомнить так: Tee-Object — для экрана, Export-Csv — для сохранения табличных данных.

18. Сохранение журнала работы — Start-Transcript

Чтобы зафиксировать последовательность действий, используется Start-Transcript.

Start-Transcript -Path .\work-log.txt

После этого записываются выполняемые команды и вывод на экран.

Завершение.

Stop-Transcript

В ходе исследовательской работы позже часто хочется проверить следующее:

Какие команды выполнялись
В какой папке велась работа
Какие получились результаты
Возникали ли ошибки

Использование Start-Transcript служит подстраховкой на случай, если вы забыли делать рабочие заметки. Однако будьте осторожны, если во время работы на экран выводится информация, которую нельзя записывать, например пароли или токены.

19. Чтение CSV и просмотр по условию

В бизнес-данных CSV встречается постоянно.

Например, предположим, что есть такой файл users.csv.

Name,Department,Enabled
Suzuki,Sales,true
Tanaka,Accounting,false
Sato,Sales,true

Загрузка.

Import-Csv .\users.csv

Посмотрим только людей из отдела Sales.

Import-Csv .\users.csv |
  Where-Object { $_.Department -eq "Sales" }

Выведем только активных пользователей в отдельный CSV.

Import-Csv .\users.csv |
  Where-Object { $_.Enabled -eq "true" } |
  Export-Csv .\enabled-users.csv -NoTypeInformation -Encoding UTF8

Значения из CSV чаще всего трактуются как строки, поэтому при сравнении true / false или чисел при необходимости учитывайте преобразование типов.

Например, чтобы увидеть только строки с баллом от 80.

Import-Csv .\scores.csv |
  Where-Object { [int]$_.Score -ge 80 }

20. Просмотр значений конфигурации JSON

JSON часто используется в конфигурационных файлах и веб-API.

$config = Get-Content .\settings.json -Raw | ConvertFrom-Json
$config

Можно посмотреть и конкретное значение.

$config.Database.Host

Пример изменения значения и сохранения в отдельный файл.

$config.Database.Host = "db01"
$config |
  ConvertTo-Json -Depth 10 |
  Set-Content .\settings.updated.json -Encoding UTF8

При работе с JSON часто используют Get-Content -Raw. С -Raw весь файл читается как одна строка, что понятнее при работе с многострочным JSON.

21. Просмотр переменных окружения

Переменные окружения часто нужны для настроек приложений и проверки путей.

Посмотрим список.

Get-ChildItem Env:

Посмотрим текущее имя пользователя.

$env:USERNAME

Посмотрим временную папку.

$env:TEMP

Разобьём PATH для удобства чтения.

$env:PATH -split ";"

Зададим переменную окружения только для текущего сеанса PowerShell.

$env:APP_MODE = "Development"

Эта настройка действует только внутри текущего открытого сеанса PowerShell.

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

[Environment]::SetEnvironmentVariable("APP_MODE", "Development", "User")

Это операция, изменяющая окружение, поэтому на корпоративных ПК и в продуктивных серверах перед выполнением проверьте область влияния.

22. Проверка профиля

В PowerShell есть профиль, который загружается при запуске.

Проверим его расположение.

$PROFILE

Проверим, существует ли он.

Test-Path $PROFILE

Для создания.

New-Item -ItemType File -Path $PROFILE -Force

Откроем в Блокноте.

notepad $PROFILE

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

23. Просмотр процессов

При исследовании процессов чаще всего сначала смотрят по убыванию использования памяти.

Get-Process |
  Sort-Object -Property WorkingSet -Descending |
  Select-Object -First 10 Name, Id, @{Name="MemoryMB"; Expression={ [math]::Round($_.WorkingSet / 1MB, 1) }}

Отфильтруем по конкретному имени.

Get-Process -Name notepad -ErrorAction SilentlyContinue

При остановке процесса не выполняйте сразу — сначала проверьте через -WhatIf.

Stop-Process -Name notepad -WhatIf

Если всё в порядке, выполните.

Stop-Process -Name notepad

В продуктивной среде остановка процесса может повлиять на бизнес-процессы. Перед остановкой проверьте объект, пользователей и способ восстановления.

24. Просмотр служб

Посмотрим список служб.

Get-Service

Если нужно посмотреть только остановленные службы.

Get-Service |
  Where-Object { $_.Status -eq "Stopped" }

Найдём службы с автоматическим запуском, но при этом остановленные.

Get-Service |
  Where-Object { $_.StartType -eq "Automatic" -and $_.Status -ne "Running" } |
  Select-Object Name, DisplayName, Status, StartType

При перезапуске службы тоже сначала проверьте объект.

Get-Service -Name "Spooler"

Прогоним.

Restart-Service -Name "Spooler" -WhatIf

Операции со службами оказывают большое влияние, поэтому перед выполнением проверьте время обслуживания и процедуру восстановления.

25. Просмотр журнала событий

При исследовании Windows часто приходится смотреть журнал событий.

Посмотрим последние 100 записей журнала System.

Get-WinEvent -LogName System -MaxEvents 100 |
  Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message

Отфильтруем только ошибки и предупреждения.

Get-WinEvent -LogName System -MaxEvents 500 |
  Where-Object { $_.LevelDisplayName -in @("Error", "Warning") } |
  Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message

Посмотрим только ошибки за последние 24 часа.

$start = (Get-Date).AddHours(-24)

Get-WinEvent -FilterHashtable @{ LogName = "System"; StartTime = $start } |
  Where-Object { $_.LevelDisplayName -eq "Error" } |
  Select-Object TimeCreated, ProviderName, Id, Message

Пример сохранения результатов исследования в CSV.

Get-WinEvent -LogName System -MaxEvents 500 |
  Where-Object { $_.LevelDisplayName -in @("Error", "Warning") } |
  Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message |
  Export-Csv .\system-events.csv -NoTypeInformation -Encoding UTF8

Журнал событий легко разрастается, поэтому важно ограничивать диапазон через -MaxEvents или указание периода.

26. Проверка сети

Для проверки доступности используется Test-Connection.

Test-Connection example.com -Count 4

Для проверки доступности TCP-порта в Windows удобен Test-NetConnection.

Test-NetConnection example.com -Port 443

Для проверки разрешения DNS.

Resolve-DnsName example.com

При исследовании сети разбивайте проверку так:

Разрешается ли имя
Достижим ли IP
Открыт ли нужный порт
Нет ли влияния прокси или межсетевого экрана

Не пытайтесь судить обо всём только средствами PowerShell — удобнее использовать его как отправную точку для разбора по частям.

27. Просмотр результатов из веба

Для получения веб-страниц используется Invoke-WebRequest.

Invoke-WebRequest https://example.com |
  Select-Object StatusCode, StatusDescription

Для работы с результатами JSON API удобен Invoke-RestMethod.

Invoke-RestMethod https://api.github.com/repos/PowerShell/PowerShell |
  Select-Object full_name, stargazers_count, forks_count

Результаты API чаще всего сразу можно обрабатывать как объекты PowerShell, что удобно сочетается с Select-Object и Where-Object.

При исследовании внутренних API тоже начинайте с запросов на чтение (GET).

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

28. Измерение времени выполнения — Measure-Command

Когда нужно узнать, сколько времени занимает обработка, используется Measure-Command.

Measure-Command {
  Get-ChildItem C:\Windows -File -Recurse -ErrorAction SilentlyContinue |
    Measure-Object
}

В результате есть TotalSeconds и TotalMilliseconds.

$result = Measure-Command {
  Get-ChildItem . -File -Recurse | Measure-Object
}

$result.TotalSeconds

Когда нужно ускорить скрипт, важно измерять время, а не полагаться на ощущения. Однако результат зависит от количества файлов, состояния диска и сети, поэтому не судите по одному запуску — проверьте несколько раз.

29. Выполнение долгих операций в фоне — Start-Job

Чтобы выполнить долгое исследование как отдельную задачу, используется Start-Job.

$job = Start-Job -ScriptBlock {
  Get-ChildItem C:\Windows -File -Recurse -ErrorAction SilentlyContinue |
    Measure-Object
}

Посмотрим состояние задачи.

Get-Job

Получим результат.

Receive-Job $job

Удалим ненужную задачу.

Remove-Job $job

Start-Job удобен, но не стоит использовать его повсеместно с самого начала. Сначала выполняйте всё обычным образом и переводите в задачи только медленные операции — так проще ориентироваться.

30. Использование истории команд

Чтобы посмотреть ранее выполненные команды, используется Get-History.

Get-History

Сделаем удобным просмотр только строк команд.

Get-History |
  Select-Object Id, CommandLine

Повторно выполним конкретную запись истории.

Invoke-History 12

История удобна, но с её помощью можно повторно выполнить и опасные команды. Перед повторным запуском изменяющих команд вроде Remove-Item или Stop-Process из истории проверьте их содержимое.

31. Просмотр деталей ошибки — Get-Error

В PowerShell 7 доступен Get-Error для подробного просмотра недавних ошибок.

Get-Error

Чтобы посмотреть только последнее сообщение об ошибке.

$Error[0].Exception.Message

При возникновении ошибки не ограничивайтесь просмотром красного текста на экране — проверьте следующее:

На какой команде произошёл сбой
На каком пути или объекте произошёл сбой
Не проблема ли это с правами доступа
Не отсутствует ли файл
Не проблема ли это сети или блокировки

Чтобы сохранить результаты исследования, сочетайте это с Start-Transcript и Export-Csv.

32. Часто используемые операторы сравнения

В Where-Object постоянно используются операторы сравнения.

Оператор Значение Пример
-eq Равно $_.Status -eq "Running"
-ne Не равно $_.Status -ne "Running"
-gt Больше $_.Length -gt 10MB
-ge Больше или равно $_.Length -ge 10MB
-lt Меньше $_.Length -lt 10MB
-le Меньше или равно $_.Length -le 10MB
-like Совпадение по маске $_.Name -like "*.log"
-match Совпадение по регулярному выражению $_.Name -match "^app"
-in Значение содержится в списке $_.Status -in @("Running", "Paused")
-contains Список содержит значение @("a", "b") -contains "a"

Примеры.

Get-ChildItem . -File |
  Where-Object { $_.Length -gt 10MB }
Get-Service |
  Where-Object { $_.Status -in @("Running", "Paused") }
Get-ChildItem . -File |
  Where-Object { $_.Name -match "\.log$" }

Поначалу вполне достаточно -eq, -ne, -gt, -lt и -like. Освоившись, используйте -match и -in — с ними условия писать проще.

33. Небольшие практические рецепты

Далее — примеры в форме, которую можно сразу использовать в работе.

Посмотреть количество файлов по расширениям

Get-ChildItem C:\Work -File -Recurse |
  Group-Object -Property Extension -NoElement |
  Sort-Object -Property Count -Descending

Используется, когда нужно примерно понять содержимое папки.

Посмотреть 20 самых больших файлов

Get-ChildItem C:\Work -File -Recurse |
  Sort-Object -Property Length -Descending |
  Select-Object -First 20 FullName, @{Name="SizeMB"; Expression={ [math]::Round($_.Length / 1MB, 2) }}

Часто применяется при проверке использования диска.

Посмотреть файлы, изменённые за последние 24 часа

$since = (Get-Date).AddHours(-24)

Get-ChildItem C:\Work -File -Recurse |
  Where-Object { $_.LastWriteTime -ge $since } |
  Select-Object FullName, Length, LastWriteTime

Полезно после инцидента, чтобы увидеть, какие файлы были изменены.

Собрать строки логов с ERROR в CSV

Select-String -Path C:\App\Logs\*.log -Pattern "ERROR" |
  Select-Object Path, LineNumber, Line |
  Export-Csv .\error-lines.csv -NoTypeInformation -Encoding UTF8

Упрощает обмен результатами исследования.

Посмотреть 2 строки до и после ERROR

Select-String -Path C:\App\Logs\*.log -Pattern "ERROR" -Context 2

Используется, когда нужен контекст вокруг причины.

Посмотреть процессы с наибольшим использованием памяти

Get-Process |
  Sort-Object -Property WorkingSet -Descending |
  Select-Object -First 10 Name, Id, @{Name="MemoryMB"; Expression={ [math]::Round($_.WorkingSet / 1MB, 1) }}

Посмотреть службы с автозапуском, но остановленные

Get-Service |
  Where-Object { $_.StartType -eq "Automatic" -and $_.Status -ne "Running" } |
  Select-Object Name, DisplayName, Status, StartType

Собрать ошибки журнала System в CSV

Get-WinEvent -LogName System -MaxEvents 500 |
  Where-Object { $_.LevelDisplayName -eq "Error" } |
  Select-Object TimeCreated, ProviderName, Id, Message |
  Export-Csv .\system-errors.csv -NoTypeInformation -Encoding UTF8

Отправить результат команды в буфер обмена

Get-Service |
  Select-Object Name, DisplayName, Status |
  ConvertTo-Csv -NoTypeInformation |
  Set-Clipboard

Удобно для вставки в письмо или чат.

Сравнить список файлов до и после работы

Get-ChildItem C:\Work -File |
  Select-Object -ExpandProperty Name |
  Set-Content .\before.txt -Encoding UTF8

Снимем список ещё раз после работы.

Get-ChildItem C:\Work -File |
  Select-Object -ExpandProperty Name |
  Set-Content .\after.txt -Encoding UTF8

Сравним.

Compare-Object (Get-Content .\before.txt) (Get-Content .\after.txt)

Посмотреть общий размер папки в ГБ

$size = Get-ChildItem C:\Work -File -Recurse |
  Measure-Object -Property Length -Sum

[math]::Round($size.Sum / 1GB, 2)

34. Осторожность при сочетании с изменяющими командами

Почти всё рассмотренное выше — команды для чтения, и с ними можно экспериментировать сравнительно свободно. А вот следующие команды — изменяющие.

Операция Команда
Копирование Copy-Item
Перемещение Move-Item
Удаление Remove-Item
Переименование Rename-Item
Остановка процесса Stop-Process
Перезапуск службы Restart-Service
Постоянная настройка переменной окружения [Environment]::SetEnvironmentVariable()

Изменяющие команды используйте в таком порядке:

1. Посмотреть объекты через команды Get-
2. Отфильтровать через Where-Object
3. Проверить список объектов через Select-Object
4. Посчитать количество через Measure-Object
5. При необходимости зафиксировать через Export-Csv
6. Прогнать через -WhatIf, если оно доступно
7. Выполнить

Например, при удалении старых файлов .tmp.

Сначала посмотрим объекты.

$limit = (Get-Date).AddDays(-30)

$targets = Get-ChildItem C:\Temp -Filter *.tmp -File -Recurse |
  Where-Object { $_.LastWriteTime -lt $limit }

$targets |
  Select-Object FullName, Length, LastWriteTime

Посчитаем количество.

$targets | Measure-Object

Зафиксируем в CSV.

$targets |
  Select-Object FullName, Length, LastWriteTime |
  Export-Csv .\delete-targets.csv -NoTypeInformation -Encoding UTF8

Проверим план удаления.

$targets | Remove-Item -WhatIf

Если всё в порядке, выполним.

$targets | Remove-Item

Простое соблюдение этого порядка значительно снижает число аварий.

35. Как запоминать

Пытаться запомнить команды по отдельности сложно, поэтому проще запоминать их по цели.

Цель Команда, которую нужно вспомнить
Посчитать Measure-Object
Просуммировать Measure-Object -Sum
Посмотреть по категориям Group-Object
Убрать дубликаты Sort-Object -Unique
Получить только значения Select-Object -ExpandProperty
Оформить столбцы Select-Object
Посмотреть по убыванию Sort-Object -Descending
Найти строку Select-String
Посмотреть контекст Select-String -Context
Сравнить два объекта Compare-Object
Сохранить след Export-Csv / Start-Transcript
Вывести и на экран, и в файл Tee-Object
Отправить в буфер обмена Set-Clipboard
Измерить время Measure-Command
Посмотреть детали ошибки Get-Error

36. Итог

PowerShell силён, когда вы умеете писать скрипты. Однако на практике чаще всего используются, как ни удивительно, сочетания небольших команд.

Посчитать
Отсортировать
Сгруппировать
Сравнить
Найти
Извлечь
Сохранить
Отправить в буфер обмена
Сохранить журнал работы

Уже одно умение делать это заметно облегчает повседневную работу. Не нужно с самого начала стремиться к сложной автоматизации. Сначала научитесь точно видеть ситуацию с помощью команд для чтения, а затем используйте изменяющие команды вроде Copy-Item, Move-Item, Remove-Item, Restart-Service только тогда, когда это действительно нужно.

Секрет безопасного использования PowerShell — вот такой порядок:

Смотреть → Считать → Оформлять → Фиксировать → Прогонять → Выполнять

Освоив эту схему, PowerShell перестаёт быть «чёрным экраном, куда вводят команды» и становится практичным инструментом, который поддерживает повседневное исследование, упорядочивание и отчётность.

Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.

Эти страницы показывают тему статьи в более широком контексте услуг и решений.

Статья напрямую связана со следующими услугами.

Частые вопросы

Вопросы, которые часто возникают при консультациях по теме статьи.

Как в PowerShell посчитать количество файлов и общий размер папки?
Передайте результат Get-ChildItem в Measure-Object: команда «Get-ChildItem . -File -Recurse | Measure-Object» даёт количество, а добавление «-Property Length -Sum» — суммарный размер в байтах, который удобно перевести в гигабайты через [math]::Round($size.Sum / 1GB, 2). Measure-Object также поддерживает -Average, -Maximum и -Minimum для оценки разброса размеров файлов. Для подсчёта по категориям — например, файлов по расширениям или служб по статусу — используйте Group-Object с -NoElement и сортировку по Count по убыванию.
В чём разница между Select-Object Name и Select-Object -ExpandProperty Name?
Select-Object Name возвращает таблицу объектов со столбцом Name, а Select-Object -ExpandProperty Name возвращает сами значения Name без обёртки. Это важно при передаче результата дальше: для вывода в CSV лучше подходит вариант со столбцом, а если значения нужно передать в обработку строк, записать в текстовый файл или в другую команду, удобнее «развёрнутые» значения без обёртки. Частый пример — сохранение списка только имён файлов через Set-Content.
Как сравнить два списка или состояние «до и после» в PowerShell?
Используйте Compare-Object с ReferenceObject (левая сторона, «до») и DifferenceObject (правая сторона, «после»). В результатах SideIndicator <= означает, что элемент есть только в reference, => — только в difference, а == (отображается при -IncludeEqual) — что элемент есть в обоих. Практичный приём — выгружать список имён файлов или состояние служб в файлы до и после изменения, а затем сравнивать их; для структурированных данных, например CSV со службами, указывайте -Property Name, Status, чтобы сравнивать конкретные столбцы. Если сохранять разницу «до/после» для каждой задачи, расследование и отчётность становятся заметно проще.
Как искать ошибки в файлах логов и смотреть окружающий контекст?
Используйте Select-String: команда «Select-String -Path .\logs\*.log -Pattern "ERROR"» ищет по файлам, причём можно указать сразу несколько шаблонов, например ERROR, WARN и FATAL. Добавьте -Context 2, чтобы вместе с найденной строкой показывались две строки до и после — это помогает понять, что привело к ошибке. Учтите, что -Pattern по умолчанию трактуется как регулярное выражение, поэтому для поиска буквальных символов вроде [ или . используйте -SimpleMatch. Результаты можно передать через Select-Object Path, LineNumber, Line в Export-Csv для дальнейшего обмена.

Об авторе

Страница с профилем автора статьи.

Го Комура

Представитель KomuraSoft LLC

Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.

Публичные ссылки

Вернуться в блог