Strings, Formatting and Regex

Reviewed & published by Brayan K

By the end of this lesson you'll format text precisely with the -f operator and here-strings, slice and stitch strings with methods, -split, and -join, and wield regular expressions through -replace, -match/$Matches, and Select-String — the toolkit for parsing logs, reshaping data, and validating input.

Part of the free PowerShell course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn

💡 Real-World Analogy

Working with text is like being a tailor. The -f operator is your tape measure and pattern — it lays values into exact, aligned slots. -split and -join are scissors and needle: cut a string into pieces, then stitch them back the way you want. And regex is your master pattern — a reusable template that recognises a shape (a date, an email, a log level) anywhere it appears, so you can find it, extract it, or reshape it with surgical precision.

1. Formatting with -f & Here-Strings

The -f operator fills numbered placeholders ({0}, {1}, …) with values and supports .NET format specifiers after a colon — {0:N2} for two decimals, {0:P0} for a percentage, {0,-10} for alignment. A here-string (@"..."@) holds multiple lines verbatim and still expands $variables, which is perfect for templates and reports. Read this worked example, then run it.

# The -f operator fills placeholders {0}, {1}, ... with values, in order.
# It also supports .NET format specifiers after a colon: {0:N2}, {0,-10}, etc.
"{0} scored {1:P0}" -f 'Ada', 0.92          # Ada scored 92%
"{0,-8}{1,6:N2}" -f 'Total', 1234.5         # left/right alignment

# A HERE-STRING (@"..."@) holds multiple lines verbatim and still expands $vars.
$name = 'Ada'
$report = @"
Name: $name
Date: $(Get-Date -Format yyyy-MM-dd)
Status: OK
"@
$report

2. String Methods, -split & -join

Because strings are .NET objects, they carry methods like .Trim(), .ToUpper(), .Replace(), and .Substring(). To break a string apart, use the -split operator (its delimiter is a regex); to glue an array back together, use -join. These two are the bread and butter of turning raw text into structured data and back.

# Strings are .NET objects, so they have METHODS:
'  Hello World  '.Trim()              # 'Hello World'
'Hello'.ToUpper()                     # 'HELLO'
'Hello World'.Replace('World','PS')   # 'Hello PS'  (plain text, not regex)
'PowerShell'.Substring(0, 5)          # 'Power'

# -split turns a string into an array (the delimiter is a REGEX):
'a,b,c' -split ','                    # a  b  c  (three elements)

# -join glues an array back into one string:
1,2,3 -join '-'                       # '1-2-3'

Now you try. Fill in the two blanks using the hints in the comments, then run it.

# 🎯 YOUR TURN — replace each ___ then run it.
# Goal: turn a CSV line into an array, then re-join it with ' | '.

$line = 'red,green,blue'

# 1) Split on the comma into an array
$parts = $line ___ ','      # 👉 the split operator is: -split

# 2) Join the parts back with ' | ' between them
$parts ___ ' | '            # 👉 the join operator is: -join

# ✅ Expected output:
# red | green | blue

3. Regular Expressions: -match, $Matches & -replace

The -match operator tests a string against a regex and, on success, fills the automatic $Matches hashtable: $Matches[0] is the whole match, numbered/named groups follow. -notmatch is its inverse. The -replace operator edits text using regex and back-references ($1, $2) in the replacement. Remember these operators are case-insensitive by default — prefix with c (-cmatch) for case sensitivity.

# -match tests a string against a REGEX and fills $Matches on success.
'order-4821' -match '(\w+)-(\d+)'      # True
$Matches[0]                            # whole match: order-4821
$Matches[1]                            # first group:  order
$Matches[2]                            # second group: 4821

# Named groups land in $Matches by name:
'2026-06-21' -match '(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})' | Out-Null
"Year is $($Matches.y)"                # Year is 2026

# -replace uses regex and back-references ($1, $2) in the replacement:
'2026-06-21' -replace '(\d{4})-(\d{2})-(\d{2})', '$3/$2/$1'   # 21/06/2026

# -notmatch is the inverse of -match:
'hello.txt' -notmatch '\.log$'         # True

Pro Tip

PowerShell's escape character is the backtick, not the backslash. Inside double quotes, `$ is a literal dollar sign and `n is a newline. But in a regex pattern, the backslash still has its normal regex meaning (\d, \.) — so write regex patterns in single quotes to stop PowerShell touching the backslashes.

Putting It Together: Select-String (grep)

Select-String is PowerShell's grep: it searches piped text or files on disk for a regex and returns rich match objects (with file name and line number). Add -SimpleMatch for a literal, non-regex search.

# Select-String is PowerShell's grep: search input or FILES for a regex.

# Search piped text, returning the matching lines:
'apple', 'banana', 'apricot' | Select-String -Pattern '^ap'

# Search files on disk, with line numbers, recursively:
Select-String -Path .\*.log -Pattern 'ERROR|WARN'

# -SimpleMatch turns OFF regex for a literal search:
'a.b.c' | Select-String -SimpleMatch -Pattern '.'

Each result is a MatchInfo object, so you can pipe it onward — e.g. Select-String 'ERROR' app.log | Select-Object LineNumber, Line.

Common Errors (and the fix)

📋 Quick Reference — Strings & Regex

ToolPurposeExample
-fFormat with placeholders'{0:N2}' -f 3.14159
@"..."@Multi-line here-string@" line1 line2 "@
-split / -joinBreak up / glue together'a,b' -split ','
-replaceRegex substitution$d -replace '-','/'
-match / $MatchesTest & capture$s -match '(\d+)'
Select-Stringgrep for text/filesSelect-String 'ERROR' *.log

Mini-Challenge: parse a log line

No blanks this time — just a brief and an outline to keep you on track. Build it, run it, and check your output against the example in the comments. Pulling structured fields out of raw log text is one of the most common real-world scripting jobs.

# 🎯 MINI-CHALLENGE: parse a log line
# No code is filled in — just an outline. Build it and check the output.
#
# Given:  $log = '2026-06-21 14:03:07 [ERROR] Disk full on /data'
#
# 1. Use -match with a regex that captures, in NAMED groups:
#       date, time, level, message
#    e.g. (?<date>\d{4}-\d{2}-\d{2}) ... (?<level>\w+) ... (?<message>.+)
# 2. Build a [pscustomobject] from $Matches with those four properties.
# 3. Use the -f operator to print a one-line summary:
#       "[<level>] <date> -> <message>"
# 4. BONUS: use -replace to reformat <date> from yyyy-MM-dd to dd/MM/yyyy.
#
# ✅ Example output:
#    [ERROR] 2026-06-21 -> Disk full on /data

# your code here

🎉 Lesson Complete

Practice quiz

What does the -f format operator do?

  • Inserts values into a format string by index
  • Forces a type
  • Finds a file
  • Filters a collection

Answer: Inserts values into a format string by index. The -f operator substitutes values into placeholders like {0} and {1} in a .NET format string.

Which syntax creates a here-string in PowerShell?

  • @"..."@
  • <<EOF ... EOF
  • [[ ... ]]
  • $( ... )

Answer: @"..."@. A here-string is bounded by @" on its own line and "@ on its own line, preserving multiple lines verbatim.

Which operator splits a string into an array?

  • -cut
  • -divide
  • -split
  • -join

Answer: -split. -split breaks a string into an array on a delimiter (which is treated as a regex pattern).

Which operator joins an array into a single string?

  • -merge
  • -join
  • -split
  • -concat

Answer: -join. -join combines array elements into one string, optionally with a separator between them.

What does the -replace operator use for its pattern?

  • Glob patterns
  • Plain text only
  • Wildcards
  • A regular expression

Answer: A regular expression. -replace treats its first argument as a regex pattern and supports replacement back-references.

After a successful -match, where are captured groups stored?

  • $Matches
  • $Groups
  • $Captures
  • $Result

Answer: $Matches. A successful -match populates the automatic $Matches hashtable with the whole match and any captured groups.

What does -notmatch return?

  • The match count
  • True when the regex does NOT match
  • The first match
  • An array of matches

Answer: True when the regex does NOT match. -notmatch is the inverse of -match: it returns True when the pattern does not match the string.

Which cmdlet searches text or files for a regex, like grep?

  • Search-File
  • Find-String
  • Match-Text
  • Select-String

Answer: Select-String. Select-String searches input or files for a pattern and returns match objects with line numbers.

In a double-quoted string, how do you write a literal dollar sign?

  • It is not possible
  • Double it: $$
  • Escape it with a leading backtick character
  • Use a leading backslash

Answer: Escape it with a leading backtick character. PowerShell's escape character is the backtick, which placed before a dollar sign produces a literal dollar sign and avoids variable expansion.

Which comparison operator does a case-INSENSITIVE regex match by default?

  • -cmatch
  • -match
  • -clike
  • -exact

Answer: -match. By default -match is case-insensitive; prefix with c (-cmatch) to make it case-sensitive.

Continue this course

Frequently asked questions

When should I use the -f operator instead of just $variable interpolation?

Use simple interpolation ("Hello $name") for quick, plain insertions. Reach for the -f format operator when you need FORMATTING: fixed decimal places ({0:N2}), percentages ({0:P0}), padding and alignment ({0,-10}), or when the same value appears multiple times by index. -f also keeps the template and the data visually separate, which reads better for tables and reports. Both are valid; -f shines whenever presentation matters.

What is the difference between a here-string and a normal string?

A here-string (@"..."@ for expandable, @'...'@ for literal) spans MULTIPLE lines verbatim, so you can include line breaks, unescaped quotes, and blocks of text without fighting the parser. The opening @" must be the last thing on its line and the closing "@ must be the first thing on its line. The expandable form still interpolates $variables and $(...) subexpressions; the single-quoted literal form does not. Use here-strings for templates, multi-line messages, and embedded JSON or SQL.

What is the difference between -replace and the .Replace() method?

The string .Replace('a','b') method does a literal, plain-text substitution of every 'a' with 'b' and knows nothing about patterns. The -replace operator treats its first argument as a REGULAR EXPRESSION and supports back-references like $1 in the replacement, so it can do powerful pattern-based edits (reformatting dates, stripping prefixes, etc.). Use .Replace() for simple literal swaps and -replace when you need regex or capture groups.

Why is $Matches empty or showing old data?

$Matches is only populated by a SUCCESSFUL -match (or -cmatch) operation, and it holds the results of the MOST RECENT successful match. If your -match returned False, $Matches keeps whatever the previous successful match left there, which looks like stale data. Always check that -match returned True before reading $Matches, and remember that -split, Select-String, and the regex .NET classes do NOT populate $Matches the same way.

Is -match case-sensitive?

No — by default the string comparison operators (-match, -replace, -like, -eq, -split) are case-INSENSITIVE in PowerShell. To force case sensitivity, prefix the operator with c: -cmatch, -creplace, -clike, -ceq. There's also an explicit i form (-imatch) for case-insensitive, which is the default. This trips up people coming from other languages where regex is case-sensitive unless flagged otherwise; in PowerShell it's the reverse.