Skip to content
PowerShell FAQs
PowerShell FAQs
  • Home
  • PowerShell Commands
  • Start Here
    • Variables
    • Data Types
    • Array
    • Files
    • String
    • Datetime
  • Download
  • Blogs

Get-Content in PowerShell: Read a File Line by Line (With Examples)

Updated on October 22, 2026 by Bijay Kumar

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.

This Tutorial Covers:

Toggle
  • How do you read a file line by line with Get-Content?
  • Read a file into an array
    • The one-line file trap
  • Get-Content -Raw: read the whole file as one string
  • Get the first, last or a specific line
    • Negative -TotalCount values behave differently in 7 and 5.1
  • Skip the first line or a CSV header
  • Read multiple files with wildcards and -Filter
  • Search and count lines from Get-Content
  • Split text on a custom character with -Delimiter
  • Why do special characters look wrong in Windows PowerShell 5.1?
  • Read a file as bytes: -AsByteStream vs -Encoding Byte
  • What happens when Get-Content gets a folder or a missing file?
  • Read an alternate data stream with -Stream
  • Reading very large files
  • Frequently Asked Questions
    • What does Get-Content do in PowerShell?
    • How do I read a whole file into one string?
    • Is cat the same as Get-Content in PowerShell?
    • How do I get the last few lines of a file?
    • Why does $lines[0] return only one letter?

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
Get-Content in PowerShell reading a file line by line with line numbers from ReadCount
Each line knows its own line number (PowerShell 7)

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
A one-line file breaks array indexing until you add @() (Windows PowerShell 5.1)

$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
Get-Content -Raw returns one string while Get-Content returns an array of lines
-Raw returns one string with the line breaks kept (PowerShell 7)

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.