How to Find the Second Occurrence in a String Using PowerShell (With Examples)

If you’ve spent any time working with strings in PowerShell, you already know that finding the first occurrence of something is easy — IndexOf() handles that in one line. But what about the second occurrence? Or the third? That’s where things get a little more interesting.

In this tutorial, I’ll show you several ways to find the second occurrence of a character or substring in a string using PowerShell. I’ll cover everything from the simple two-step IndexOf() approach to regex-based methods and a reusable function you can drop into any script.

Let’s get into it.

Why Would You Need the Second Occurrence?

More often than you’d think. Here are some real situations where this comes up:

  • You have a file path like C:\Servers\Backups\File.log and you want to extract everything after the second backslash.
  • You’re parsing a log line like ERROR: [2026-05-07] [ServerName] Disk full and need the content inside the second set of brackets.
  • You’re working with DNS names like server01.domain.local and want everything after the second dot.
  • You’re splitting delimited strings where the first delimiter is a header and the second is where the real data starts.
  • You’re cleaning up exported data from SharePoint or a CSV where values have repeating separators.

Any of these sound familiar? Good. Let’s solve them.

Check out Convert String to Decimal in PowerShell

How IndexOf() Works in PowerShell

Before jumping to the second occurrence, it helps to understand IndexOf() properly — because that’s the foundation for most of what follows.

$string = "server.domain.local"
$position = $string.IndexOf(".")
Write-Host $position

Output:

6

IndexOf() returns the zero-based index of the first occurrence of your character or substring. If the character isn’t found, it returns -1.

Now here’s the key thing most people don’t realize: IndexOf() has an overload that lets you specify a starting position. That’s exactly what we use to find the second occurrence.

Read PowerShell New Line in String

Method 1: Using IndexOf() with a Start Position

This is the most straightforward approach. You find the first occurrence, then start your second search from one position after it.

$string = "server.domain.local"

# Find the first occurrence
$first = $string.IndexOf(".")

# Find the second occurrence, starting just after the first
$second = $string.IndexOf(".", $first + 1)

Write-Host "First dot at index: $first"
Write-Host "Second dot at index: $second"

Output:

First dot at index: 6
Second dot at index: 13

I executed the above PowerShell script, and you can see the exact output in the screenshot below:

Find the Second Occurrence in a String Using PowerShell

The magic is in $first + 1 — you’re telling IndexOf() to skip everything up to and including the first match, then look for the next one from there.

If there is no second occurrence, $second will return -1. So it’s a good habit to check for that before doing anything with the result:

if ($second -ge 0) {
Write-Host "Second occurrence found at index: $second"
} else {
Write-Host "No second occurrence found."
}

Check out PowerShell Convert Byte Array to Hex String

Method 2: Extracting a Substring After the Second Occurrence

Usually you don’t just want the position — you want the text that comes after it. Here’s how to combine IndexOf() with Substring() to extract everything after the second occurrence of a character:

$string = "server.domain.local"

$first = $string.IndexOf(".")
$second = $string.IndexOf(".", $first + 1)

# Get everything after the second dot
$result = $string.Substring($second + 1)

Write-Host $result

Output:

local

Here is the exact output in the screenshot below:

PowerShell Find the Second Occurrence in a String

And if you want everything before the second occurrence:

$result = $string.Substring(0, $second)
Write-Host $result

Output:

server.domain

This is super useful for parsing domain names, file paths, version numbers — anything with a repeating delimiter where position matters.

Check out Find Strings in PowerShell Hash Tables

Method 3: Using the Split() Method

Sometimes the cleanest approach isn’t to search for a position at all — it’s to just split the string on your delimiter and work with the resulting array. This is especially readable when you need chunks of the string, not just an index.

$string = "server.domain.local"
$parts = $string.Split(".")

# Parts: parts[0] = "server", parts[1] = "domain", parts[2] = "local"
Write-Host "Second segment: $($parts[1])"
Write-Host "Everything after second dot: $($parts[2])"

Output:

Second segment: domain
Everything after second dot: local

If you want to reconstruct everything after the second occurrence (useful when there are more than three segments), join the remaining elements back together:

$string = "C:\Windows\System32\drivers\etc"
$parts = $string.Split("\")

# Join everything from index 3 onwards (after the second backslash)
$result = $parts[3..($parts.Length - 1)] -join "\"
Write-Host $result

Output:

drivers\etc

The [3..($parts.Length - 1)] slice grabs every element from index 3 to the end, and -join "\" puts the backslash delimiters back in. Clean and readable.

Read Split a String by Semicolon in PowerShell

Method 4: Using [regex]::Matches() to Find All Occurrences

When you need more than just the second occurrence — or you want to find the nth occurrence dynamically — [regex]::Matches() is your best friend. It returns all matches in one shot, and you can index into the result.

$string = "one.two.three.four.five"

$matches = [regex]::Matches($string, "\.")

# Access the second occurrence (index 1, since it's zero-based)
$secondMatch = $matches[1]

Write-Host "Second dot at index: $($secondMatch.Index)"

Output:

Second dot at index: 7

Since [regex]::Matches() returns a collection, you can grab any occurrence you want — just change the index:

$matches[0]  # First occurrence
$matches[1] # Second occurrence
$matches[2] # Third occurrence

Always check how many matches were found before indexing into the collection, or you’ll get a null reference error:

if ($matches.Count -ge 2) {
Write-Host "Second occurrence found at index: $($matches[1].Index)"
} else {
Write-Host "Less than 2 occurrences found."
}

Check out Split String by Space in PowerShell

Method 5: Finding the Second Occurrence of a Word (Not Just a Character)

Everything above works just as well for full words or multi-character substrings. The IndexOf() approach handles it natively:

$string = "The server is down. The server needs a restart."

$first = $string.IndexOf("server")
$second = $string.IndexOf("server", $first + 1)

Write-Host "First 'server' at index: $first"
Write-Host "Second 'server' at index: $second"

Output:

First 'server' at index: 4
Second 'server' at index: 20

And with [regex]::Matches() for multi-character patterns:

$string = "The server is down. The server needs a restart."

$matches = [regex]::Matches($string, "server")

Write-Host "Total occurrences: $($matches.Count)"
Write-Host "Second occurrence at index: $($matches[1].Index)"

Output:

Total occurrences: 2
Second occurrence at index: 20

Check out Split a String and Get the Second Element in PowerShell

Method 6: Case-Insensitive Search for the Second Occurrence

By default, IndexOf() is case-sensitive. If your string might have mixed casing, you’ll want to tell it to ignore case:

$string = "Error: Server offline. ERROR: Disk full."

$first = $string.IndexOf("error", [System.StringComparison]::OrdinalIgnoreCase)
$second = $string.IndexOf("error", $first + 1, [System.StringComparison]::OrdinalIgnoreCase)

Write-Host "Second 'error' (case-insensitive) at index: $second"

Output:

Second 'error' (case-insensitive) at index: 23

The [System.StringComparison]::OrdinalIgnoreCase parameter is the key here — it tells IndexOf() not to care about letter casing when comparing.

With regex, case-insensitive matching is even simpler — just pass [System.Text.RegularExpressions.RegexOptions]::IgnoreCase:

$matches = [regex]::Matches($string, "error", [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
Write-Host "Second occurrence at index: $($matches[1].Index)"

Read PowerShell Split String by Comma

Method 7: A Reusable Find-NthOccurrence Function

If you find yourself doing this regularly, it’s worth wrapping the logic in a reusable function. Here’s one you can drop into any script or PowerShell profile:

function Find-NthOccurrence {
param (
[string]$InputString,
[string]$SearchValue,
[int]$Nth = 1,
[switch]$IgnoreCase
)

$currentIndex = 0
$foundCount = 0

while ($currentIndex -lt $InputString.Length) {
if ($IgnoreCase) {
$pos = $InputString.IndexOf($SearchValue, $currentIndex, [System.StringComparison]::OrdinalIgnoreCase)
} else {
$pos = $InputString.IndexOf($SearchValue, $currentIndex)
}

if ($pos -eq -1) { return -1 } # No more occurrences

$foundCount++

if ($foundCount -eq $Nth) {
return $pos
}

$currentIndex = $pos + $SearchValue.Length
}

return -1
}

Now you can call it like this:

$string = "one.two.three.four.five"

# Find the 2nd occurrence of "."
$index = Find-NthOccurrence -InputString $string -SearchValue "." -Nth 2
Write-Host "2nd dot at index: $index"

# Find the 3rd occurrence of "."
$index = Find-NthOccurrence -InputString $string -SearchValue "." -Nth 3
Write-Host "3rd dot at index: $index"

# Case-insensitive search
$string2 = "Apple Banana apple Cherry apple"
$index = Find-NthOccurrence -InputString $string2 -SearchValue "apple" -Nth 2 -IgnoreCase
Write-Host "2nd 'apple' at index: $index"

Output:

2nd dot at index: 7
3rd dot at index: 13
2nd 'apple' at index: 14

This function handles any nth occurrence, supports case-insensitive matching, and returns -1 gracefully when the occurrence doesn’t exist. It’s the kind of utility function that quietly saves you a lot of time across multiple projects.

Check out Split Strings by Newlines in PowerShell

A Real-World Example

Let’s say you’re processing log file lines that look like this:

2026-05-07|INFO|Server01|Disk usage at 85%
2026-05-07|WARN|Server02|CPU spike detected
2026-05-07|ERROR|Server03|Service stopped unexpectedly

You want to extract the server name — which is the text between the second and third pipe characters. Here’s how:

$lines = @(
"2026-05-07|INFO|Server01|Disk usage at 85%",
"2026-05-07|WARN|Server02|CPU spike detected",
"2026-05-07|ERROR|Server03|Service stopped unexpectedly"
)

foreach ($line in $lines) {
$parts = $line.Split("|")
$serverName = $parts[2]
$message = $parts[3]
Write-Host "Server: $serverName | Message: $message"
}

Output:

Server: Server01 | Message: Disk usage at 85%
Server: Server02 | Message: CPU spike detected
Server: Server03 | Message: Service stopped unexpectedly

Or if you specifically want to use the second occurrence of | with IndexOf():

foreach ($line in $lines) {
$first = $line.IndexOf("|")
$second = $line.IndexOf("|", $first + 1)
$third = $line.IndexOf("|", $second + 1)

$serverName = $line.Substring($second + 1, $third - $second - 1)
Write-Host "Server: $serverName"
}

Both approaches work — the Split() version is cleaner for structured data, while the IndexOf() version gives you more precision when you need exact character positions.

Common Mistakes to Avoid

A few things that catch people out:

  • Forgetting that indexes are zero-based — The first character in a string is at index 0, not 1. Keep this in mind when using Substring() after finding a position.
  • Not checking for -1 — If the character you’re searching for doesn’t exist, IndexOf() returns -1. If you then do Substring(-1 + 1), you’ll get unexpected results. Always validate first.
  • Using $first instead of $first + 1 as the start position — If you start the second search at $first instead of $first + 1, you’ll just find the same first occurrence again. Add 1 (or the length of your search word) to move past it.
  • Assuming Split() is always the answer — Split() is clean but it changes how you access the data. If your string has a variable number of delimiters, or if you need the actual index position, IndexOf() or regex is more reliable.

Quick Reference

What You Want to DoMethod to Use
Find position of 2nd occurrenceIndexOf() with start position
Extract text after 2nd occurrenceIndexOf() + Substring()
Split string into chunksSplit() + array indexing
Find all occurrences at once[regex]::Matches()
Find nth occurrence dynamicallyCustom Find-NthOccurrence function
Case-insensitive nth occurrenceIndexOf() with OrdinalIgnoreCase

Wrapping Up

Finding the second (or nth) occurrence of something in a string in PowerShell isn’t as difficult as it first looks. The core technique — using IndexOf() with a start position — is simple once you see it in action. From there, [regex]::Matches() gives you more flexibility when you need all occurrences at once, and the reusable function wraps everything up nicely for scripts you’ll use over and over.

Whether you’re parsing log files, cleaning up SharePoint data, or splitting file paths, these methods cover every situation you’re likely to hit.

You may also like the following tutorials:

100 PowerShell cmdlets download free

100 POWERSHELL CMDLETS E-BOOK

FREE Download an eBook that contains 100 PowerShell cmdlets with complete script and examples.