String Manipulation
Note
Learn how to work with text in PowerShell: splitting, joining, replacing, formatting, and using regular expressions.
Overview
String manipulation is one of the most common tasks in PowerShell scripting. Whether you're parsing log files, formatting output, cleaning data, or building file paths, you'll constantly work with text. PowerShell provides powerful built-in methods and operators to slice, dice, search, and transform strings efficiently.
Basic String Operations
String Methods (Dot Notation)
Strings in PowerShell are .NET objects with built-in methods:
$text = " Hello, World! "
# Common methods
$text.Trim() # "Hello, World!" (removes leading/trailing spaces)
$text.TrimStart() # "Hello, World! " (removes leading spaces only)
$text.TrimEnd() # " Hello, World!" (removes trailing spaces only)
$text.ToUpper() # " HELLO, WORLD! "
$text.ToLower() # " hello, world! "
$text.Length # 17 (property, not a method)
# Check contents
$text.Contains("World") # $true
$text.StartsWith(" He") # $true
$text.EndsWith("! ") # $true
# Find position
$text.IndexOf("World") # 9 (position of first match)
$text.LastIndexOf("o") # 11 (position of last 'o')
Key Points
- String methods don't modify the original string (strings are immutable)
- Always assign the result to a variable:
$clean = $text.Trim() - Methods are case-sensitive: use
.ToLower()not.tolower() - Property vs Method:
Lengthis a property (no parentheses), most others are methods
Splitting Strings
Using .Split() Method
# Split on a character
$csv = "John,Doe,30,Engineer"
$parts = $csv.Split(",")
# Result: @("John", "Doe", "30", "Engineer")
$parts[0] # "John"
$parts[3] # "Engineer"
# Split on multiple characters
$data = "apple;banana:cherry;grape"
$fruits = $data.Split(";:")
# Result: @("apple", "banana", "cherry", "grape")
# Split with options
$text = "one two three"
$words = $text.Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)
# Result: @("one", "two", "three") - removes empty entries from multiple spaces
Using -split Operator (PowerShell Way)
More powerful than .Split() - supports regex:
# Simple split
$text = "apple,banana,cherry"
$text -split ","
# Result: @("apple", "banana", "cherry")
# Split on whitespace (any amount)
$text = "one two three four"
$text -split "\s+"
# Result: @("one", "two", "three", "four")
# Split on multiple delimiters (regex)
$text = "apple;banana:cherry,grape"
$text -split "[;:,]"
# Result: @("apple", "banana", "cherry", "grape")
# Limit number of splits
$text = "a-b-c-d-e"
$text -split "-", 3
# Result: @("a", "b", "c-d-e") - splits into max 3 parts
# Case-sensitive split
$text = "AppleXbananaXcherry"
$text -split "X" # Case-insensitive (default): @("Apple", "banana", "cherry")
$text -csplit "X" # Case-sensitive: @("Apple", "banana", "cherry")
$text -csplit "x" # Case-sensitive: @("AppleXbananaXcherry") - no match
Joining Strings
Using -join Operator
# Join array elements into a single string
$items = @("apple", "banana", "cherry")
# Join with separator
$items -join ", " # "apple, banana, cherry"
$items -join ";" # "apple;banana;cherry"
$items -join "`n" # Join with newline (one item per line)
# Join without separator
$letters = @("P", "o", "w", "e", "r")
$letters -join "" # "Power"
# Join with multiple characters
$items -join " | " # "apple | banana | cherry"
Using [string]::Join()
# Alternative method (same result as -join)
$items = @("apple", "banana", "cherry")
[string]::Join(", ", $items) # "apple, banana, cherry"
# Join lines from a file
$lines = Get-Content C:\file.txt
$text = [string]::Join("`n", $lines)
Replacing & Removing Text
Using .Replace() Method
Simple character/string replacement:
$text = "Hello World"
# Replace characters
$text.Replace("World", "PowerShell") # "Hello PowerShell"
$text.Replace("l", "L") # "HeLLo WorLd"
$text.Replace(" ", "_") # "Hello_World"
# Remove text (replace with empty string)
$text.Replace("World", "") # "Hello " (note the space remains)
$text.Replace(" ", "") # "HelloWorld"
# Case-sensitive
$text.Replace("hello", "Hi") # "Hello World" (no change - case mismatch)
Using -replace Operator (Regex)
More powerful - supports patterns:
$text = "My phone: 555-1234"
# Simple replacement
$text -replace "555", "123" # "My phone: 123-1234"
# Regex patterns
$text -replace "\d{3}-\d{4}", "XXX-XXXX" # "My phone: XXX-XXXX" (mask phone)
# Remove all numbers
$text -replace "\d", "" # "My phone: -"
# Remove all non-alphanumeric
"Hello, World!" -replace "[^a-zA-Z0-9]", "" # "HelloWorld"
# Replace multiple spaces with single space
"one two three" -replace "\s+", " " # "one two three"
# Case-insensitive (default)
"Hello" -replace "hello", "Hi" # "Hi"
# Case-sensitive
"Hello" -creplace "hello", "Hi" # "Hello" (no change)
"Hello" -creplace "Hello", "Hi" # "Hi"
Extracting Substrings
Using .Substring() Method
$text = "PowerShell"
# Get substring starting at position (0-indexed)
$text.Substring(5) # "Shell" (from position 5 to end)
# Get substring with specific length
$text.Substring(0, 5) # "Power" (start at 0, take 5 characters)
$text.Substring(5, 3) # "She" (start at 5, take 3 characters)
# Practical example: extract file extension
$filename = "report.xlsx"
$extension = $filename.Substring($filename.LastIndexOf(".") + 1)
# Result: "xlsx"
Using Array Index Notation
$text = "PowerShell"
# Single character
$text[0] # "P" (first character)
$text[5] # "S"
$text[-1] # "l" (last character)
$text[-2] # "l" (second from last)
# Range of characters
$text[0..4] # "Power" (characters 0 through 4)
$text[5..9] # "Shell" (characters 5 through 9)
$text[-5..-1] # "Shell" (last 5 characters)
String Formatting
Using -f Format Operator
The PowerShell way to format strings:
# Basic formatting
$name = "Raymond"
$age = 30
"Name: {0}, Age: {1}" -f $name, $age
# Result: "Name: Raymond, Age: 30"
# Reorder parameters
"{1} is {0} years old" -f $age, $name
# Result: "Raymond is 30 years old"
# Number formatting
$price = 1234.567
"{0:C}" -f $price # "$1,234.57" (currency)
"{0:N2}" -f $price # "1,234.57" (number with 2 decimals)
"{0:P}" -f 0.85 # "85.00%" (percentage)
"{0:F1}" -f $price # "1234.6" (fixed-point, 1 decimal)
# Date formatting
$date = Get-Date
"{0:yyyy-MM-dd}" -f $date # "2025-12-15"
"{0:MM/dd/yyyy hh:mm tt}" -f $date # "12/15/2025 02:30 PM"
# Padding and alignment
"{0,10}" -f "Right" # " Right" (right-aligned, 10 chars wide)
"{0,-10}" -f "Left" # "Left " (left-aligned, 10 chars wide)
# Zero-padding
"{0:D5}" -f 42 # "00042" (5 digits with leading zeros)
Using String Interpolation
# Double-quoted strings with variables
$name = "Raymond"
$city = "St. Louis"
"Hello, $name from $city" # "Hello, Raymond from St. Louis"
# Expressions in $()
$items = @(1, 2, 3)
"Count: $($items.Count)" # "Count: 3"
"Total: $($items | Measure-Object -Sum).Sum)" # "Total: 6"
# Escape special characters
"Price: `$19.99" # "Price: $19.99" (backtick escapes $)
Regular Expressions (Regex) Basics
Common Regex Patterns
# Email validation
$email = "user@example.com"
$email -match "^\w+@\w+\.\w+$" # $true
# Phone number
$phone = "555-1234"
$phone -match "\d{3}-\d{4}" # $true
# IP address
$ip = "192.168.1.1"
$ip -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$" # $true
# Extract matches
$text = "Error code: 12345"
if ($text -match "code: (\d+)") {
$matches[1] # "12345" (captured group)
}
Common Regex Symbols
| Pattern | Meaning | Example |
|---|---|---|
. | Any character | a.c matches "abc", "a1c" |
\d | Any digit (0-9) | \d{3} matches "123" |
\w | Any word character (a-z, A-Z, 0-9, _) | \w+ matches "hello" |
\s | Any whitespace | \s+ matches spaces, tabs |
^ | Start of string | ^Hello matches "Hello World" |
$ | End of string | World$ matches "Hello World" |
* | Zero or more | a* matches "", "a", "aaa" |
+ | One or more | a+ matches "a", "aaa" |
{n} | Exactly n times | \d{3} matches "123" |
{n,m} | Between n and m times | \d{2,4} matches "12", "123", "1234" |
[] | Character class | [abc] matches "a", "b", or "c" |
[^] | Negated class | [^0-9] matches any non-digit |
Using -match and $matches
# Test if pattern exists
$text = "Order #12345 received"
if ($text -match "#(\d+)") {
Write-Output "Found order: $($matches[1])" # "12345"
}
# Extract all matches with Select-String
$log = @"
Error: Code 404
Error: Code 500
Error: Code 403
"@
$log | Select-String "Code (\d+)" -AllMatches | ForEach-Object {
$_.Matches | ForEach-Object {
$_.Groups[1].Value # 404, 500, 403
}
}
Common Use Cases
Use Case 1: Parse CSV Data
What it does: Split comma-separated values and create objects
$csvLine = "John,Doe,30,Engineer,New York"
$fields = $csvLine -split ","
[PSCustomObject]@{
FirstName = $fields[0]
LastName = $fields[1]
Age = [int]$fields[2]
Title = $fields[3]
City = $fields[4]
}
Use Case 2: Clean Up User Input
What it does: Remove extra spaces and normalize text
$userInput = " john.doe@EXAMPLE.COM "
# Clean and normalize
$clean = $userInput.Trim().ToLower() # "john.doe@example.com"
# Remove all spaces
$noSpaces = $userInput -replace "\s", "" # "john.doe@EXAMPLE.COM"
Use Case 3: Build File Paths
What it does: Safely construct file paths from components
$parts = @("C:", "Users", "raymond", "Documents", "file.txt")
$path = $parts -join "\"
# Result: "C:\Users\raymond\Documents\file.txt"
# Better: Use Join-Path
$path = Join-Path "C:\Users\raymond" "Documents\file.txt"
Use Case 4: Extract Data from Log Files
What it does: Parse log entries to extract specific information
$logLine = "[2025-12-15 14:30:45] ERROR: Connection timeout (Server: db01)"
# Extract timestamp
if ($logLine -match "\[(.*?)\]") {
$timestamp = $matches[1] # "2025-12-15 14:30:45"
}
# Extract server name
if ($logLine -match "Server: (\w+)") {
$server = $matches[1] # "db01"
}
Use Case 5: Mask Sensitive Data
What it does: Replace sensitive information with masked values
$text = "SSN: 123-45-6789, Credit Card: 1234-5678-9012-3456"
# Mask SSN
$masked = $text -replace "\d{3}-\d{2}-\d{4}", "XXX-XX-XXXX"
# Mask credit card (keep last 4 digits)
$masked = $masked -replace "(\d{4}-\d{4}-\d{4}-)(\d{4})", '****-****-****-$2'
# Result: "SSN: XXX-XX-XXXX, Credit Card: ****-****-****-3456"
Real-World Examples
Example: Parse Email Addresses from Text
Scenario: Extract all email addresses from a text block
$text = @"
Contact us at support@company.com or sales@company.com.
For urgent issues, email admin@company.com.
"@
# Extract emails using regex
$emailPattern = "\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
$emails = [regex]::Matches($text, $emailPattern) | ForEach-Object { $_.Value }
# Result: @("support@company.com", "sales@company.com", "admin@company.com")
Example: Format Names Properly
Scenario: Convert names to proper case (Title Case)
function ConvertTo-ProperCase {
param([string]$Text)
# Split into words
$words = $Text.ToLower() -split "\s+"
# Capitalize first letter of each word
$properWords = $words | ForEach-Object {
$_.Substring(0,1).ToUpper() + $_.Substring(1)
}
# Join back together
$properWords -join " "
}
ConvertTo-ProperCase "JOHN DOE" # "John Doe"
ConvertTo-ProperCase "mary SMITH" # "Mary Smith"
Example: Parse Server Names from URLs
Scenario: Extract server/hostname from URLs
$urls = @(
"https://server01.company.com/api/data"
"http://db-prod.company.com:8080/query"
"ftp://files.company.com/reports"
)
foreach ($url in $urls) {
if ($url -match "://([^/:]+)") {
$server = $matches[1]
Write-Output "Server: $server"
}
}
# Output:
# Server: server01.company.com
# Server: db-prod.company.com
# Server: files.company.com
Example: Build SQL WHERE Clause from Array
Scenario: Create SQL IN clause from PowerShell array
$usernames = @("jdoe", "msmith", "rjones")
# Wrap each in single quotes and join
$quotedUsers = $usernames | ForEach-Object { "'$_'" }
$inClause = $quotedUsers -join ", "
$sql = "SELECT * FROM Users WHERE Username IN ($inClause)"
# Result: "SELECT * FROM Users WHERE Username IN ('jdoe', 'msmith', 'rjones')"
Example: Clean Up File Names
Scenario: Remove invalid characters from file names
function Remove-InvalidFileNameChars {
param([string]$FileName)
# Get invalid characters for file names
$invalid = [System.IO.Path]::GetInvalidFileNameChars() -join ''
$pattern = "[$([regex]::Escape($invalid))]"
# Replace invalid chars with underscore
$FileName -replace $pattern, '_'
}
Remove-InvalidFileNameChars "Report: Q4 2025.xlsx" # "Report_ Q4 2025.xlsx"
Remove-InvalidFileNameChars "Data<>File?.txt" # "Data__File_.txt"
Tips & Tricks
Chain Methods Together
Use Here-Strings for Multi-Line Text
Escape Special Characters
Test Regex Patterns Interactively
.Replace() is Case-Sensitive
Regex Special Characters Need Escaping
# These characters have special meaning in regex: . * + ? ^ $ ( ) [ ] { } | \
# Wrong: trying to match a literal period
"file.txt" -match "." # $true (matches ANY character!)
# Right: escape the period
"file.txt" -match "\." # $true (matches literal period)
# Use [regex]::Escape() for user input
$userInput = "file.txt"
"file.txt" -match [regex]::Escape($userInput) # Safely escapes special chars
String Concatenation vs Array Join
Watch -split With Empty Strings
Working with JSON
For comprehensive JSON documentation, see the dedicated [[Working with JSON]] page.
PowerShell provides ConvertFrom-Json and ConvertTo-Json cmdlets for working with JSON data:
# JSON to PowerShell object
$json = '{"name":"Raymond","age":30}'
$obj = $json | ConvertFrom-Json
$obj.name # "Raymond"
# PowerShell object to JSON
$data = @{server="db01"; port=5432}
$data | ConvertTo-Json
# Read JSON file (always use -Raw)
$config = Get-Content settings.json -Raw | ConvertFrom-Json
For detailed coverage including nested JSON, arrays, the -Depth parameter, file operations, and real-world examples, see Working with JSON.
Related Topics
- Variables & Data Types - String basics and types
- Arrays & Collections - Working with split results
- Operators - Comparison operators with strings
- Object Manipulation - Working with string properties
- Working with JSON - JSON conversion and parsing