PowerShell Read and Write Files: Get-Content and Set-Content

PowerShell Read and Write Files: Get-Content and Set-Content

PowerShell Tips Editor 2 min read
PowerShell Read and Write Files: Get-Content and Set-Content

Config files, log files, CSV data, text templates — almost every real PowerShell script eventually needs to read a file or write one. Get-Content reads file text into PowerShell as an array of lines (or a single string with -Raw), and Set-Content writes content back. This guide covers reading, writing, appending, encoding, and processing large files efficiently — everything you need for text file automation.

Reading a File with Get-Content

By default, Get-Content returns each line of the file as a separate string in an array:

# Read all lines — returns an array of strings
$lines = Get-Content 'C:\Logs\app.log'

# Line count
$lines.Count

# Access specific lines by index
$lines[0]   # First line
$lines[-1]  # Last line

# Process each line in the pipeline
Get-Content 'C:\Logs\app.log' | Where-Object { $_ -like '*ERROR*' }
247
2026-05-04 08:00:01 INFO Application started
2026-05-04 09:14:55 INFO Shutdown complete
2026-05-04 08:15:33 ERROR Database connection failed

Because the output is an array of strings, you can immediately filter with Where-Object, sort, count, or process each line in a loop. Each element of the array is one line, without the newline character.

Reading as a Single String with -Raw

The -Raw flag reads the entire file as a single string, preserving all newlines:

# Without -Raw: array of lines
$lines = Get-Content 'C:\Config\appsettings.json'
$lines.GetType().Name    # Object[]

# With -Raw: single string
$content = Get-Content 'C:\Config\appsettings.json' -Raw
$content.GetType().Name  # String

# Use -Raw for JSON, regex, or any operation needing the whole file
$json = Get-Content 'C:\Config\appsettings.json' -Raw | ConvertFrom-Json
$json.Server
Object[]
String
localhost

Use -Raw when you need to process the file as a whole: parsing JSON, running a multi-line regex, or doing a whole-file text replacement. Without -Raw, a regex spanning two lines won’t match because each line is a separate string.

Writing a File with Set-Content

Set-Content writes content to a file, overwriting it completely if it exists:

# Write a single line
Set-Content -Path 'C:\Logs\status.txt' -Value 'All systems operational'

# Write multiple lines from an array
$report = @(
    "Report generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm')",
    "Server: $env:COMPUTERNAME",
    "Status: OK"
)
Set-Content -Path 'C:\Reports\daily.txt' -Value $report

# Write pipeline output to file
Get-Service | Where-Object { $_.Status -eq 'Stopped' } |
    ForEach-Object { $_.Name } |
    Set-Content -Path 'C:\Reports\stopped-services.txt'
# daily.txt contents:
# Report generated: 2026-05-04 09:15
# Server: WORKSTATION01
# Status: OK

Set-Content overwrites by default. Use Add-Content to append. If the file doesn’t exist, Set-Content creates it. If the parent directory doesn’t exist, it throws an error — create the directory first with New-Item.

Appending Lines with Add-Content

Add-Content adds content without erasing what’s already there:

# Append a log entry
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
Add-Content -Path 'C:\Logs\script.log' -Value "[$timestamp] Script completed successfully"

# Append a line if the file doesn't exist yet, create it
Add-Content -Path 'C:\Logs\events.log' -Value "Service started"

# Append all error lines from another file
Get-Content 'C:\Logs\app.log' |
    Where-Object { $_ -like '*ERROR*' } |
    Add-Content -Path 'C:\Logs\errors-only.log'

Specifying File Encoding

Always specify encoding when interoperability matters:

# Read with specific encoding
Get-Content 'C:\Data\export.csv' -Encoding UTF8

# Write with UTF8 (no BOM) — best for cross-platform
Set-Content -Path 'C:\Config\settings.json' -Value $json -Encoding UTF8

# Available encodings in PS5.1: ASCII, BigEndianUnicode, Default, OEM, Unicode, UTF7, UTF8, UTF32
# PS7+ adds UTF8NoBOM and others
# Recommended: use UTF8 for most text files

# Check current file encoding (basic heuristic)
$bytes = [System.IO.File]::ReadAllBytes('C:\Data\file.txt') | Select-Object -First 4
"First bytes: $($bytes -join ' ')"   # EF BB BF = UTF8 BOM

Processing Large Files Line by Line

For very large files (hundreds of MB), loading all lines into memory at once is inefficient. Stream through the pipeline instead:

# Stream a large log file without loading it all into memory
Get-Content 'C:\Logs\bigfile.log' | ForEach-Object {
    if ($_ -match 'CRITICAL') {
        Add-Content 'C:\Logs\critical.log' -Value $_
    }
}

# Even more efficient: use a StreamReader for very large files
$reader = [System.IO.StreamReader]::new('C:\Logs\bigfile.log')
while (-not $reader.EndOfStream) {
    $line = $reader.ReadLine()
    if ($line -match 'ERROR') {
        # process line
    }
}
$reader.Close()

For files under ~100MB, the pipeline approach is fine. For multi-GB log files, the StreamReader approach uses far less memory.

Common Errors and Fixes

  • Get-Content returns array not string — use -Raw for single string: Operations like -replace on the result of Get-Content without -Raw run on each line individually, not on the whole file. This means multi-line patterns won’t match. Add -Raw to get the full file as one string.
  • Set-Content overwrites by default — use Add-Content to append: Calling Set-Content twice on the same file loses the first write. Use Add-Content when you need to accumulate entries, or explicitly concatenate: read with -Raw, append, write back with Set-Content.

Related Cmdlets / See Also

Wrapping Up

Get-Content returns an array of lines by default — use -Raw when you need the whole file as one string. Set-Content writes and overwrites; use Add-Content to append. Always specify -Encoding UTF8 for files shared across platforms. For large files, stream through the pipeline rather than loading everything into memory. Your next step: write a log monitoring script that streams a large log file and extracts only ERROR lines.

Send-Item -To