Process Management
Note
Find, start, stop, and monitor running processes with Get-Process, Start-Process, and Stop-Process.
Overview
Whether you're killing a hung application, launching an external tool from a script, or watching for a runaway process eating CPU, PowerShell's process cmdlets give you the same control as Task Manager — scriptable and filterable. Get-Process reads, Start-Process launches, Stop-Process kills.
Basic Syntax
Get-Process -Name "notepad"
Start-Process -FilePath "notepad.exe"
Stop-Process -Name "notepad"
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5
Key Points
Get-Process -Namedoesn't need the.exeextensionStop-Processkills immediately — there's no graceful shutdown request, use-Forceonly when a normalStop-Processdoesn't workStart-Process -Waitblocks the script until the launched process exits- Process IDs (
-Id) are more precise than names when multiple instances are running
Finding Processes
# All processes
Get-Process
# By name (wildcards supported)
Get-Process -Name "chrome*"
# By process ID
Get-Process -Id 4832
# Top CPU consumers
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 Name, CPU, Id
# Top memory consumers (WorkingSet is in bytes)
Get-Process | Sort-Object WorkingSet -Descending |
Select-Object -First 10 Name, @{N="MemoryMB";E={[math]::Round($_.WorkingSet / 1MB, 1)}}
Output:
Handles NPM(K) PM(K) WS(K) CPU(s) Id ProcessName
------- ------ ----- ----- ------ -- -----------
845 41 89452 92104 12.45 4832 chrome
312 18 24012 31556 3.20 9944 notepad
Filtering by Resource Usage
# Processes using more than 500MB
Get-Process | Where-Object { $_.WorkingSet -gt 500MB }
# Processes that have been running more than an hour
Get-Process | Where-Object { $_.StartTime -lt (Get-Date).AddHours(-1) }
# Processes with no window (background/service-hosted processes)
Get-Process | Where-Object { $_.MainWindowTitle -eq "" }
Starting Processes
# Launch an application
Start-Process -FilePath "notepad.exe"
# Launch with arguments
Start-Process -FilePath "notepad.exe" -ArgumentList "C:\Temp\notes.txt"
# Launch and wait for it to close before continuing
Start-Process -FilePath "msiexec.exe" -ArgumentList "/i app.msi /qn" -Wait
# Launch minimized/hidden
Start-Process -FilePath "backup.exe" -WindowStyle Hidden
# Run elevated (triggers a UAC prompt)
Start-Process -FilePath "powershell.exe" -Verb RunAs
# Capture exit code from a launched process
$proc = Start-Process -FilePath "robocopy.exe" -ArgumentList "C:\Src C:\Dst /MIR" -Wait -PassThru
$proc.ExitCode
-Wait -PassThru Is How You Get an Exit Code
Start-Process normally returns nothing (or immediately hands control back to your script). Combine -Wait (block until it finishes) with -PassThru (return the process object) to capture .ExitCode — essential when a script needs to know whether the launched program actually succeeded.
Stopping Processes
# Stop by name
Stop-Process -Name "notepad"
# Stop by process ID (safer when multiple instances exist)
Stop-Process -Id 4832
# Force-kill a process that won't close normally
Stop-Process -Name "notepad" -Force
# Preview before killing anything
Stop-Process -Name "chrome" -WhatIf
# Stop all processes matching a pattern
Get-Process -Name "chrome*" | Stop-Process -Force
Stop-Process Doesn't Ask the App to Close
# BAD (for anything with unsaved work) - immediately kills the process
Stop-Process -Name "notepad" -Force
# BETTER - ask the process to close its main window first (gives it a chance to prompt "Save changes?")
(Get-Process -Name "notepad").CloseMainWindow()
# Fall back to Stop-Process only if CloseMainWindow() doesn't work
Stop-Process is the equivalent of pulling the power cord, not clicking the X. Use CloseMainWindow() first for interactive applications where data loss matters. Monitoring Processes
# Snapshot CPU/memory right now
Get-Process -Name "sqlservr" | Select-Object Name, CPU, WorkingSet, Threads
# Watch a process over time (simple polling loop)
1..10 | ForEach-Object {
Get-Process -Name "myapp" | Select-Object Name, CPU, WorkingSet
Start-Sleep -Seconds 5
}
# Count instances of a process across the system
(Get-Process -Name "chrome" -ErrorAction SilentlyContinue).Count
CPU Property Is Cumulative, Not Instantaneous
$process.CPU is total processor time consumed since the process started, in seconds — not a percentage of current usage. To get percentage-style CPU usage, sample the CPU value twice with a known delay and calculate the delta, or use Get-Counter against the \Process(*)\% Processor Time performance counter.
Important Parameters
| Parameter | Type | Description | Example |
|---|---|---|---|
-Name | String[] | Process name, no .exe, supports wildcards | Get-Process -Name "chrome*" |
-Id | Int[] | Process ID | Get-Process -Id 4832 |
-FilePath | String | Start-Process: executable to launch | Start-Process -FilePath "app.exe" |
-ArgumentList | String | Start-Process: command-line arguments | -ArgumentList "/silent" |
-Wait | Switch | Start-Process: block until it exits | Start-Process -Wait |
-PassThru | Switch | Start-Process: return the process object | Start-Process -PassThru |
-Verb | String | Start-Process: RunAs for elevation | Start-Process -Verb RunAs |
-Force | Switch | Stop-Process: kill without confirmation prompt | Stop-Process -Force |
Common Patterns
# Pattern 1: Kill a process only if it's running (avoid errors on missing process)
$proc = Get-Process -Name "hungapp" -ErrorAction SilentlyContinue
if ($proc) {
Stop-Process -InputObject $proc -Force
}
# Pattern 2: Run an external tool and check success
$result = Start-Process -FilePath "robocopy.exe" -ArgumentList "C:\Src C:\Dst /MIR" -Wait -PassThru
if ($result.ExitCode -ge 8) {
Write-Error "Robocopy failed with exit code $($result.ExitCode)"
}
# Pattern 3: Top resource consumers report
Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 Name, Id, CPU,
@{N="MemoryMB"; E={[math]::Round($_.WorkingSet / 1MB, 1)}}
Real-World Examples
Example: Restart a Hung Application
Scenario: Check if a specific application is unresponsive (not responding to the UI), and if so, kill and relaunch it.
$appName = "myapp"
$appPath = "C:\Program Files\MyApp\myapp.exe"
$process = Get-Process -Name $appName -ErrorAction SilentlyContinue
if ($process -and -not $process.Responding) {
Write-Output "$appName is not responding, restarting..."
Stop-Process -InputObject $process -Force
Start-Sleep -Seconds 2
Start-Process -FilePath $appPath
} elseif (-not $process) {
Write-Output "$appName is not running, starting it"
Start-Process -FilePath $appPath
} else {
Write-Output "$appName is running normally"
}
Explanation: .Responding is a boolean on the process object that reflects whether the app's UI thread is processing messages — the same check Task Manager uses to flag "(Not Responding)". Combining it with existence checks covers all three states: running fine, hung, and not running.
Tips & Tricks
Get the Owning Executable's Full Path
Useful for confirming exactly which binary (and from which folder) is actually running, especially when duplicate-named executables exist in different locations.Stopping the Wrong Process by Name
# BAD - kills EVERY process named "python", including ones you didn't start
Stop-Process -Name "python" -Force
# GOOD - target the specific instance by Id
Stop-Process -Id 8842 -Force
-Id when precision matters. Related Topics
- Services Management - Managing long-running background processes as services
- Event Logs - Finding out why a process crashed
- Scheduled Tasks - Running a process on a recurring schedule instead of manually