To read a file line by line in PowerShell, loop over Get-Content. It returns one string per line, so a foreach loop handles each line in turn:
foreach ($line in Get-Content -Path C:\psfaqs\servers.txt) {
"Checking $line"
}
Output:
Checking WEB01
Checking WEB02
Checking SQL01
Checking FILE01
Below I cover line numbers, reading into an array, the first and last lines, skipping a header, and one encoding problem in Windows PowerShell 5.1. I ran every example in PowerShell 7.6 and 5.1.
Show line numbers
Every line that Get-Content returns carries its line number in a ReadCount property, so you don’t need your own counter:
Get-Content -Path C:\psfaqs\servers.txt | ForEach-Object {
"Line $($_.ReadCount): $_"
}
Output:
Line 1: WEB01
Line 2: WEB02
Line 3: SQL01
Line 4: FILE01

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 C:\psfaqs\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 C:\psfaqs\one-line.txt
"Without @(): first = [$($lines[0])], rest = [$($lines[1..($lines.Count - 1)] -join ', ')]"
$lines = @(Get-Content -Path C:\psfaqs\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/09/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.
Read the first or last line
-TotalCount reads only the first lines and stops, and -Tail reads from the end of the file:
"First line: $(Get-Content -Path C:\psfaqs\servers.txt -TotalCount 1)"
"Last line: $(Get-Content -Path C:\psfaqs\servers.txt -Tail 1)"
"Last two: $((Get-Content -Path C:\psfaqs\servers.txt -Tail 2) -join ', ')"
Output:
First line: WEB01
Last line: FILE01
Last two: SQL01, FILE01
Both are faster than reading the whole file on big logs. The Get-Content documentation lists their aliases, -First and -Last.
Skip the first line
To skip a header row, pipe the lines to Select-Object -Skip 1:
Get-Content -Path C:\psfaqs\states.csv | Select-Object -Skip 1
Output:
California,39538223
Texas,29145505
Florida,21538187
Ohio,11799448
For CSV files, you usually don’t need to skip the header yourself. Import-Csv uses it to name the columns:
$rows = Import-Csv -Path C:\psfaqs\states.csv
"First row: $(($rows | Select-Object -First 1).State)"
"Last row: $(($rows | Select-Object -Last 1).State)"
Output:
First row: California
Last row: Ohio
Looping through CSV rows covers Import-Csv in more detail.
Why do special characters look wrong in Windows PowerShell 5.1?
cafe.txt is saved as UTF-8 without a byte order mark, which is how most editors and Linux tools save text today. Here’s how each version reads it:
"Default: $(Get-Content -Path C:\psfaqs\cafe.txt)"
"-Encoding UTF8: $(Get-Content -Path C:\psfaqs\cafe.txt -Encoding UTF8)"
Output in PowerShell 7:
Default: Café in San José
-Encoding UTF8: Café in San José
Output in Windows PowerShell 5.1:
Default: Café in San José
-Encoding UTF8: Café in San José

Windows PowerShell 5.1 assumes the older Windows code page when there’s no byte order mark, so é turns into two wrong characters. Add -Encoding UTF8 in 5.1. PowerShell 7 reads UTF-8 by default.
Reading very large files
Get-Content is fine for most files, but it’s slow on files with hundreds of thousands of lines. [System.IO.File]::ReadLines() reads one line at a time and is much faster:
foreach ($line in [System.IO.File]::ReadLines('C:\Logs\big.log')) {
if ($line -like '*ERROR*') { $line }
}
Give ReadLines a full path. Counting lines in a file has timing results for these methods on a 300,000-line log.
Frequently Asked Questions
How do I read a file line by line in PowerShell?
Use foreach ($line in Get-Content -Path file.txt) { ... }. Get-Content returns each line as a separate string.
How do I read only the first line of a file?
Use Get-Content -Path file.txt -TotalCount 1. It stops reading after the first line. Use -Tail 1 for the last line.
How do I skip the first line of a file in PowerShell?
Pipe it to Select-Object: Get-Content file.txt | Select-Object -Skip 1.
Why does $lines[0] return only one letter?
The file has one line, so Get-Content returned a string, not an array. Use $lines = @(Get-Content file.txt).
How do I get the line number while reading a file?
Use the ReadCount property of each line: Get-Content file.txt | ForEach-Object { "$($_.ReadCount): $_" }.
Once you can read a file, these are natural next steps:
- Count lines in a file
- Find a string in a file and return the line
- Loop through CSV rows
- Append to a file with Add-Content
Bijay Kumar is an esteemed author and the mind behind PowerShellFAQs.com, where he shares his extensive knowledge and expertise in PowerShell, with a particular focus on SharePoint projects. Recognized for his contributions to the tech community, Bijay has been honored with the prestigious Microsoft MVP award. With over 15 years of experience in the software industry, he has a rich professional background, having worked with industry giants such as HP and TCS. His insights and guidance have made him a respected figure in the world of software development and administration. Read more.