Get-Content is the cmdlet PowerShell gives you for reading files. Point it at a text file and it returns the contents as one string per line:
Get-Content -Path .\servers.txt
Output:
WEB01
WEB02
SQL01
FILE01
That one-string-per-line design is why it’s also the easiest way to read a file line by line. Below I cover loops, arrays, raw text, the first and last lines, multiple files, bytes, and the spots where PowerShell 7.6 and Windows PowerShell 5.1 disagree.
How do you read a file line by line with Get-Content?
Put Get-Content inside a foreach loop in PowerShell and each pass gets the next line:
foreach ($line in Get-Content -Path .\servers.txt) {
"Checking $line"
}
Output:
Checking WEB01
Checking WEB02
Checking SQL01
Checking FILE01
When you pipe the lines instead, every line carries its line number in a ReadCount property, so you don’t need your own counter:
Get-Content -Path .\servers.txt | ForEach-Object {
"Line $($_.ReadCount): $_"
}
Output:
Line 1: WEB01
Line 2: WEB02
Line 3: SQL01
Line 4: FILE01

You’ll also see Get-Content typed as gc, cat or type, its built-in aliases. cat is an alias only on Windows, so spell out Get-Content in scripts that run on Linux or macOS.
Read a file into an array
Assign Get-Content to a variable and you have an array of lines. Index it like any other array, and use -1 for the last line:
$lines = Get-Content -Path .\servers.txt
"Lines: $($lines.Count)"
"First: $($lines[0])"
"Last: $($lines[-1])"
"Second and third: $($lines[1..2] -join ', ')"
Output:
Lines: 4
First: WEB01
Last: FILE01
Second and third: WEB02, SQL01
The one-line file trap
If the file has only one line, Get-Content returns a single string instead of an array. Indexing a string gives you letters:
$lines = Get-Content -Path .\one-line.txt
"Without @(): first = [$($lines[0])], rest = [$($lines[1..($lines.Count - 1)] -join ', ')]"
$lines = @(Get-Content -Path .\one-line.txt)
"With @(): first = [$($lines[0])], rest = [$(($lines | Select-Object -Skip 1) -join ', ')]"
Output:
Without @(): first = [A], rest = [u, A]
With @(): first = [Austin], rest = []
![PowerShell one-line file returns a string so [0] gives the first letter](https://powershellfaqs.com/wp-content/uploads/2026/10/powershell-one-line-file-array-index-trap.png)
$lines[0] returned the letter A, and the range 1..0 counted backwards through the letters. Wrapping Get-Content in @() keeps it an array, and Select-Object -Skip is safer than a range.
An empty file is the other edge case: Get-Content returns nothing, and indexing that gives the cannot index into a null array error. The same @() wrapper protects you there too.
Get-Content -Raw: read the whole file as one string
To get the file as one block of text, for example to run one -replace across line breaks, add -Raw:
$lines = Get-Content -Path .\servers.txt
$text = Get-Content -Path .\servers.txt -Raw
"Without -Raw: $($lines.GetType().Name) with $($lines.Count) items"
"With -Raw: $($text.GetType().Name) with $($text.Length) characters"
"Ends with a line break: $($text.EndsWith("`n"))"
Output:
Without -Raw: Object[] with 4 items
With -Raw: String with 29 characters
Ends with a line break: True

Without -Raw you get an Object[] array with one item per line. With -Raw you get one String, and the line breaks stay inside it, including the one at the end of the file.
-Raw is also what you want before ConvertFrom-Json, since JSON needs the whole document at once. Here’s how to read a JSON file into an array that way.
Get the first, last or a specific line
-TotalCount reads only the first lines and stops, and -Tail reads from the end of the file:
"First line: $(Get-Content -Path .\servers.txt -TotalCount 1)"
"Last line: $(Get-Content -Path .\servers.txt -Tail 1)"
"Last two: $((Get-Content -Path .\servers.txt -Tail 2) -join ', ')"
Output:
First line: WEB01
Last line: FILE01
Last two: SQL01, FILE01
Both beat loading a whole big log. Their aliases are -First/-Head and -Last.