Skip to content

Instantly share code, notes, and snippets.

@jorgeasaurus
Created February 14, 2026 17:15
Show Gist options
  • Select an option

  • Save jorgeasaurus/035847545f1f1ddbe9b950315a01e6b2 to your computer and use it in GitHub Desktop.

Select an option

Save jorgeasaurus/035847545f1f1ddbe9b950315a01e6b2 to your computer and use it in GitHub Desktop.
Using .NET Methods in PowerShell - practical examples you'll actually reuse.
#Requires -Version 7.0
<#
.SYNOPSIS
Using .NET Methods in PowerShell - practical examples you'll actually reuse.
.DESCRIPTION
Companion script for the blog post. Covers strings, regex, paths, file I/O,
dates, crypto/hashing, parsing/validation, collections, and quality-of-life helpers.
Run the whole file or copy individual sections. Everything targets PowerShell 7+.
#>
# ============================================================================
# How to call .NET from PowerShell (the 3 patterns)
# ============================================================================
# 1) Call static methods on a type
[System.Guid]::NewGuid()
[System.Math]::Round(3.14159, 2)
# 2) Create an instance and call instance methods
$sb = [System.Text.StringBuilder]::new()
$sb.Append("hello").Append(" ").Append("world").ToString()
# 3) Use a type accelerator (short alias PowerShell provides)
[datetime]::UtcNow
[regex]::Match("abc123", "\d+").Value
# ============================================================================
# 1) Strings: faster, cleaner transforms with .NET
# ============================================================================
# Trim whitespace
$text = " Jorge "
$text.Trim() # "Jorge"
# Case-insensitive comparison
[string]::Equals("A", "a", [System.StringComparison]::OrdinalIgnoreCase) # True
# Null/empty check
[string]::IsNullOrEmpty($text) # False
# Whitespace-only check
[string]::IsNullOrWhiteSpace(" ") # True
# Concatenate without +
[string]::Concat("Hello", " ", "world")
# Join a collection with a delimiter
[string]::Join(", ", @("a", "b", "c"))
# Case-insensitive contains
"Hello".Contains("he", [System.StringComparison]::OrdinalIgnoreCase) # True
# Replace substring
"report-2026".Replace("2026", "2027")
# Starts/ends with comparison rules
"Report.csv".StartsWith("report", [System.StringComparison]::OrdinalIgnoreCase)
"Report.csv".EndsWith(".csv", [System.StringComparison]::OrdinalIgnoreCase)
# Invariant casing for stable comparisons
"i".ToUpperInvariant()
# Split with empty entries removed
"one,,two".Split(@(","), [System.StringSplitOptions]::RemoveEmptyEntries)
# Join with platform-specific newline
[string]::Join([System.Environment]::NewLine, @("a", "b"))
# Format with placeholders
$user = "Jorge"
$count = 42
[string]::Format("User {0} has {1} items.", $user, $count)
# Build many lines efficiently with StringBuilder
$sb = [System.Text.StringBuilder]::new()
1..5 | ForEach-Object { [void]$sb.AppendLine("Line $_") }
$sb.ToString()
# ============================================================================
# 2) Regex: use [regex] for control and readability
# ============================================================================
# Get all ticket IDs
$text = "Tickets: INC123, INC456, INC789"
[regex]::Matches($text, "INC\d+").Value
# Parse named groups
$log = "user=jorge status=200"
$m = [regex]::Match($log, "user=(?<user>\w+)\s+status=(?<status>\d+)")
$m.Groups["user"].Value
$m.Groups["status"].Value
# Case-insensitive regex match
[regex]::IsMatch("Hello", "^hello$", [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
# ============================================================================
# 3) Paths and filenames: stop fighting separators
# ============================================================================
# Combine paths safely
[System.IO.Path]::Combine($HOME, "logs", "app.log")
# Extract file name and extension
[System.IO.Path]::GetFileName("/tmp/report.csv") # "report.csv"
[System.IO.Path]::GetExtension("/tmp/report.csv") # ".csv"
# Get the per-user app data folder
[System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ApplicationData)
# Get temp directory and random name
$tempRoot = [System.IO.Path]::GetTempPath()
$tempName = [System.IO.Path]::GetRandomFileName()
# Change extension without touching the file
[System.IO.Path]::ChangeExtension("/tmp/report.csv", ".json")
# ============================================================================
# 4) File I/O: predictable encoding, fast reads/writes
# ============================================================================
# Normalize to a full path
[System.IO.Path]::GetFullPath("./logs/app.log")
# Get the parent directory name
[System.IO.Path]::GetDirectoryName("/tmp/report.csv")
# Create a temp file name
[System.IO.Path]::GetTempFileName()
# Check file/directory existence
[System.IO.File]::Exists("/tmp/report.csv")
[System.IO.Directory]::Exists("/tmp")
# Enumerate subdirectories lazily
[System.IO.Directory]::EnumerateDirectories($HOME)
# Write UTF-8 text explicitly
$path = [System.IO.Path]::Combine($HOME, "demo.txt")
[System.IO.File]::WriteAllText($path, "hello", [System.Text.Encoding]::UTF8)
# Read UTF-8 text explicitly
$content = [System.IO.File]::ReadAllText($path, [System.Text.Encoding]::UTF8)
# Write raw bytes
$bytes = 0..255
[System.IO.File]::WriteAllBytes($path, $bytes)
# Read raw bytes
$readBytes = [System.IO.File]::ReadAllBytes($path)
$readBytes.Length
# Stream file names without loading all at once (may throw error on .trash or inaccessible dirs)
$root = $HOME
[System.IO.Directory]::EnumerateFiles($root, "*.log", [System.IO.SearchOption]::AllDirectories) |
Select-Object -First 20
# Read lines lazily (streaming)
[System.IO.File]::ReadLines($path) | Select-Object -First 5
# Append text without rewriting the file
[System.IO.File]::AppendAllText($path, "more")
# File size via FileInfo
$fileInfo = [System.IO.FileInfo]::new($path)
$fileInfo.Length
# Enumerate files with DirectoryInfo
$dirInfo = [System.IO.DirectoryInfo]::new($HOME)
$dirInfo.EnumerateFiles("*.log") | Select-Object -First 5
# ============================================================================
# 5) Dates and time: UTC, ISO 8601, and duration math
# ============================================================================
# Get current UTC time
[datetime]::UtcNow
# ISO 8601 string
[datetime]::UtcNow.ToString("o") # e.g. 2026-02-13T...
# Parse exact date with invariant culture
[datetime]::ParseExact("2026-02-13", "yyyy-MM-dd", [System.Globalization.CultureInfo]::InvariantCulture)
# Measure elapsed time
$start = [datetime]::UtcNow
Start-Sleep -Milliseconds 250
$elapsed = [datetime]::UtcNow - $start
$elapsed.TotalMilliseconds
# Use DateTimeOffset for offset-aware timestamps
[System.DateTimeOffset]::UtcNow.ToString("o")
# Convert UTC to local time zone
$localZone = [System.TimeZoneInfo]::Local
[System.TimeZoneInfo]::ConvertTimeFromUtc([datetime]::UtcNow, $localZone)
# Convert Unix epoch seconds to DateTimeOffset
[System.DateTimeOffset]::FromUnixTimeSeconds(1700000000)
# Build a TimeSpan from minutes
[System.TimeSpan]::FromMinutes(5)
# Measure elapsed time with Stopwatch
$sw = [System.Diagnostics.Stopwatch]::StartNew()
Start-Sleep -Milliseconds 120
$sw.Stop()
$sw.ElapsedMilliseconds
# ============================================================================
# 6) Hashing and crypto: quick integrity checks
# ============================================================================
# Open file and compute SHA256 hash
$path = [System.IO.Path]::Combine($HOME, "demo.txt")
$stream = [System.IO.File]::OpenRead($path)
try {
$sha = [System.Security.Cryptography.SHA256]::Create()
$hashBytes = $sha.ComputeHash($stream)
}
finally {
$stream.Dispose()
}
# Convert hash bytes to hex
$hashHex = [System.Convert]::ToHexString($hashBytes).ToLowerInvariant()
$hashHex
# Generate cryptographically strong random bytes
$bytes = [byte[]]::new(32)
[System.Security.Cryptography.RandomNumberGenerator]::Fill($bytes)
# Encode as Base64 for storage or display
[System.Convert]::ToBase64String($bytes)
# Hash a byte array directly (no stream)
$hashBytes2 = [System.Security.Cryptography.SHA256]::HashData(
[System.Text.Encoding]::UTF8.GetBytes("hello")
)
[System.Convert]::ToHexString($hashBytes2).ToLowerInvariant()
# ============================================================================
# 7) Parsing and validation helpers
# ============================================================================
# TryParse for integers
$intValue = 0
[int]::TryParse("42", [ref]$intValue)
# TryParseExact for dates
$parsedDate = [datetime]::MinValue
[datetime]::TryParseExact(
"2026-02-13",
"yyyy-MM-dd",
[System.Globalization.CultureInfo]::InvariantCulture,
[System.Globalization.DateTimeStyles]::None,
[ref]$parsedDate
)
# TryParse for GUIDs
$parsedGuid = [guid]::Empty
[guid]::TryParse("5f2a1f7c-3f9a-4a6b-9a2e-1c5c1b2d3e4f", [ref]$parsedGuid)
# TryParse for enums
$day = [System.DayOfWeek]::Sunday
[System.Enum]::TryParse([System.DayOfWeek], "Friday", $true, [ref]$day)
# ============================================================================
# 8) Small quality-of-life helpers I reuse everywhere
# ============================================================================
# Create a GUID string
[guid]::NewGuid().ToString()
# Encode and decode Base64
$text = "hello"
$b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($text))
$back = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($b64))
$back
# Compare versions reliably
$v1 = [version]"7.4.1"
$v2 = [version]"7.5.0"
$v2 -gt $v1
# Platform-specific newline and env var lookup
[System.Environment]::NewLine
[System.Environment]::GetEnvironmentVariable("PATH")
# Empty typed array and LINQ Any
$emptyStrings = [System.Array]::Empty[string]()
[System.Linq.Enumerable]::Any([int[]]@(1, 2, 3)) # True
# Expand environment variables in a string (Windows only, %VAR% syntax is not used on macOS/Linux)
[System.Environment]::ExpandEnvironmentVariables("%TEMP%\demo.txt")
# Read command-line args for the current process
[System.Environment]::GetCommandLineArgs()
# Start a process with arguments and read output
$psi = [System.Diagnostics.ProcessStartInfo]::new(
"pwsh",
"-NoProfile -Command `"Get-Date`""
)
$psi.RedirectStandardOutput = $true
$psi.UseShellExecute = $false
$proc = [System.Diagnostics.Process]::Start($psi)
$proc.StandardOutput.ReadToEnd().Trim()
$proc.WaitForExit()
# Use List for ordered data
$list = [System.Collections.Generic.List[string]]::new()
$list.Add("a")
$list.Add("b")
$list
# Use HashSet for fast membership checks
$set = [System.Collections.Generic.HashSet[string]]::new()
$set.Add("a")
$set.Contains("a")
# Use Dictionary with TryGetValue
$dict = [System.Collections.Generic.Dictionary[string, int]]::new()
$dict["a"] = 1
$value = 0
$found = $dict.TryGetValue("a", [ref]$value)
# Convert arrays with Array.ConvertAll
$numbers = [int[]]@(1, 2, 3)
$strings = [System.Array]::ConvertAll(
$numbers,
[System.Converter[int, string]]{ param($n) $n.ToString() }
)
$strings
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment