Set-Content and Out-File both put text into a file. On a quiet English UTF-8 machine they look interchangeable. On a Czech log, a CSV opened in Excel, or a schedule that later runs under powershell.exe -File, they are not. The bug is almost never the path. It is the encoding you never asked for.
The shortest honest comparison
Out-File |
Set-Content |
|
|---|---|---|
| Default job | Capture pipeline output (what would print on screen) | Write string content into a file |
| Default encoding (Windows PowerShell 5.1) | Unicode (UTF-16 LE) | Default (often ANSI / system code page) |
| Default encoding (PowerShell 7+) | utf8NoBOM | utf8NoBOM |
| Overwrite vs append |
-Append supported |
Use Add-Content to append |
| Great at | Redirecting formatted cmdlet output | Writing known strings you control |
If you memorize one row, memorize the 5.1 defaults. That is where most "my script worked on my laptop" tickets start.
Recipe 1: capture a command's screen output
Get-ChildItem C:\Logs -File |
Sort-Object Length -Descending |
Select-Object -First 20 |
Out-File C:\Reports\top-logs.txt
Out-File is the file version of what you see. Objects still go through formatting first, so you get a readable table (or list), not a serialized object dump. That is exactly what you want for a human-readable report, and exactly what you do not want if the next step is another script parsing fields.
Need the same idea as > / >>?
Get-Service | Out-File C:\Reports\services.txt
Get-Process | Out-File C:\Reports\services.txt -Append
Recipe 2: write the text you mean
Set-Content -Path C:\Apps\config.env -Value @(
'APP_ENV=prod'
'LOG_LEVEL=info'
) -Encoding utf8
Set-Content replaces the whole file with the values you pass. No formatting layer. No surprise table headers. Pass an array and you get one line per element (plus a trailing newline, which is usually what you want).
Always name -Encoding when the file will leave your machine. Prefer:
Set-Content .\note.txt 'Příliš žluťoučký kůň' -Encoding utf8
On PowerShell 7, utf8 is fine. On Windows PowerShell 5.1, utf8 still writes a BOM. If a downstream tool hates BOMs, write bytes yourself or move the write to pwsh 7 with utf8NoBOM.
Recipe 3: the encoding trap in one demo
Run this on Windows PowerShell 5.1 and open both files in a hex editor or Notepad++:
'café' | Out-File .\via-outfile.txt
Set-Content .\via-setcontent.txt 'café'
Typical result on 5.1:
-
via-outfile.txt→ UTF-16 LE (often starts withFF FE) -
via-setcontent.txt→ system ANSI (on Czech Windows, usually Windows-1250)
Neither is "wrong." Both are wrong if you assumed UTF-8. A later Get-Content without -Encoding, a Python script that opens as UTF-8, or Git treating the file as binary will all look like random corruption.
Force the contract:
'café' | Out-File .\via-outfile.txt -Encoding utf8
Set-Content .\via-setcontent.txt 'café' -Encoding utf8
Same string, same encoding, same future.
Recipe 4: when Out-File is the wrong tool
This pattern looks neat and silently loses structure:
# Fragile: formatting depends on console width and host
Get-ChildItem | Out-File .\files.txt
Get-Content .\files.txt | ForEach-Object { $_.Substring(0, 10) } # will break
If another command must read the file, write data, not a screenshot of data:
Get-ChildItem -File |
Select-Object Name, Length, LastWriteTime |
Export-Csv .\files.csv -NoTypeInformation -Encoding utf8
Or, for simple line lists:
(Get-ChildItem -File).Name | Set-Content .\names.txt -Encoding utf8
Rule of thumb: Out-File for eyes, Set-Content / Export-Csv / ConvertTo-Json for machines.
Three traps worth knowing
Host width changes
Out-Filelayout. Wide tables wrap differently when the window is narrow. A report that looked fine in an expanded console can become unreadable when a scheduled task runs with a default width. Fix withOut-File -Width 200(or higher) when you care about columns.>isOut-Filewith the same defaults. In Windows PowerShell 5.1,>creates UTF-16 files. Redirecting a UTF-8-looking string does not make a UTF-8 file. Prefer the cmdlet form with-Encodingwhenever the file is shared.Set-Contentoverwrites; it does not append. To add lines, useAdd-Content(and still pass-Encodingso the first write and later appends agree). MixingOut-File -Encoding utf8with a later bareAdd-Contentis a classic way to glue two encodings into one "corrupted" log.
Pick one default and stick to it
For new scripts on mixed 5.1 / 7 fleets:
$utf8 = if ($PSVersionTable.PSVersion.Major -ge 7) { 'utf8NoBOM' } else { 'utf8' }
Set-Content .\app.log "started $(Get-Date -Format o)" -Encoding $utf8
Or standardize on PowerShell 7 for anything that writes text other tools will read. The command names stay the same. The encoding defaults finally stop fighting you.
Practice both without nuking a real config
Encoding bugs are ugly to debug on production shares. In CMD Master's interactive PowerShell lessons you can try Out-File, Set-Content, and -Encoding in a live browser terminal with instant feedback — no install, no VM. The same free Windows command line practice site also covers CMD and Bash when you hop between shells.
Your turn: have you been bitten by a UTF-16 Out-File landing in a UTF-8 pipeline? Drop the weirdest encoding mix-up you've hit in the comments.
Top comments (0)