PowerShell Here-String: Multiline Text Made Easy

PowerShell Here-String: Multiline Text Made Easy

PowerShell Tips Editor 3 min read
PowerShell Here-String: Multiline Text Made Easy

SQL queries in scripts, HTML email bodies, JSON payloads, multi-line error messages — embedding any of these as a plain string means quoting nightmares, awkward concatenation, and code that’s hard to read. PowerShell here-string syntax lets you define a block of text that spans multiple lines with all the whitespace and special characters preserved. This post covers both flavors — literal and expanding — with practical examples for the scenarios where here-strings make the biggest difference.

Single-Quoted Here-String Syntax

A single-quoted here-string starts with @' on its own line and ends with '@ at the beginning of a line (column zero — no leading whitespace). Everything inside is treated as a literal string — no variable expansion, no escape processing.

$literal = @'
SELECT *
FROM Users
WHERE Department = 'Engineering'
  AND Active = 1
ORDER BY LastName
'@

Write-Output $literal
SELECT *
FROM Users
WHERE Department = 'Engineering'
  AND Active = 1
ORDER BY LastName

Use single-quoted here-strings when you don’t need variable expansion — SQL queries, raw JSON templates, regex patterns with backslashes, and any content where you want literal text preserved exactly.

Double-Quoted Here-String with Variables

A double-quoted here-string starts with @" and ends with "@ at column zero. Inside, PowerShell expands variables and expressions — exactly like a regular double-quoted string but across multiple lines.

$computerName = $env:COMPUTERNAME
$currentDate  = Get-Date -Format "yyyy-MM-dd"
$freeGB       = [math]::Round((Get-PSDrive C).Free / 1GB, 1)

$report = @"
=== Daily System Report ===
Computer  : $computerName
Date      : $currentDate
C: Free   : $freeGB GB
Generated : $(Get-Date -Format 'HH:mm:ss')
"@

Write-Output $report
=== Daily System Report ===
Computer  : WORKSTATION
Date      : 2026-05-04
C: Free   : 98.4 GB
Generated : 08:22:11

Building HTML Email Templates

Here-strings are the cleanest way to build HTML bodies for email alerts. Combine a double-quoted here-string with Send-MailMessage -BodyAsHtml for formatted reports.

$status   = "Completed"
$rowCount = 1482
$duration = "00:03:22"
$server   = $env:COMPUTERNAME

$htmlBody = @"
<html><body style='font-family:Arial'>
<h2>Nightly ETL Job Report</h2>
<table border='1' cellpadding='5' style='border-collapse:collapse'>
  <tr><td><b>Status</b></td><td>$status</td></tr>
  <tr><td><b>Records Processed</b></td><td>$rowCount</td></tr>
  <tr><td><b>Duration</b></td><td>$duration</td></tr>
  <tr><td><b>Server</b></td><td>$server</td></tr>
</table>
</body></html>
"@

Embedding SQL Queries

SQL scripts that span multiple lines are unreadable as concatenated strings. A single-quoted here-string lets you write the SQL exactly as you would in SQL Server Management Studio.

$query = @'
SELECT
    u.UserName,
    u.Email,
    d.DepartmentName,
    COUNT(l.LoginId) AS LoginCount
FROM dbo.Users u
JOIN dbo.Departments d ON u.DeptId = d.DeptId
LEFT JOIN dbo.Logins l ON u.UserId = l.UserId
    AND l.LoginDate >= DATEADD(DAY, -30, GETDATE())
WHERE u.IsActive = 1
GROUP BY u.UserName, u.Email, d.DepartmentName
ORDER BY LoginCount DESC
'@

# Execute via SqlClient or Invoke-Sqlcmd
# Invoke-Sqlcmd -ServerInstance "SQL01" -Database "AppDB" -Query $query

Here-String Gotchas with Closing Marker

The closing marker ('@ or "@) must be at column zero — the very beginning of the line with no spaces or tabs before it. This is the source of nearly every here-string parsing error. PowerShell throws a “The string is missing the terminator” error if the closing marker is indented.

# CORRECT — closing marker at column zero
$text = @'
Line one
Line two
'@

# WRONG — closing marker is indented (common mistake in indented code blocks)
# $text = @'
#     Line one
#     Line two
#     '@   <-- this '@' at column 4 will cause a parsing error

Here-String vs String Concatenation

Compare readability when building a multiline string with and without here-strings:

# Painful string concatenation
$body = "Dear $name,`r`n`r`n" +
        "Your password will expire in $days days.`r`n" +
        "Please change it at: https://portal.example.com`r`n`r`n" +
        "IT Support Team"

# Clean here-string equivalent
$body = @"
Dear $name,

Your password will expire in $days days.
Please change it at: https://portal.example.com

IT Support Team
"@

Here-strings preserve literal newlines, tabs, and special characters without any escape sequences.

Common Errors and Fixes

  • Closing @’ must be at column 0: Indenting the closing marker even by one space or one tab causes a “missing terminator” parse error. If you’re inside a function or conditional block and habit makes you indent, the here-string will fail. Use the editor’s “Go to column 0” feature or simply ensure there is nothing before the '@ or "@ on its line.
  • Single-quoted here-string does not expand variables: Inside @' '@, $variables are printed literally as $variablename. If your here-string output shows literal dollar signs instead of variable values, you’re using single quotes when you need double quotes: switch to @" "$ syntax for the expanding variant.

Related Cmdlets / See Also

Wrapping Up

Here-strings make multi-line text in PowerShell scripts readable and maintainable — no escape characters, no concatenation, just text. As a next step, find any script where you’ve built an HTML email body or SQL query through string concatenation and convert it to a here-string. The improvement in readability will be immediate.

Send-Item -To