Reputation: 47417
I know how to do a string replacement from inside a text template, something simple like
text file
Hello ${name}
posh
[string] $name = "World"
$template = Get-Content 'c:\temp\docs\template.txt'
$expanded = $ExecutionContext.InvokeCommand.ExpandString($template)
$expanded
Output Expected
Hello World
This is all well and good, but what if I need something more advanced?
text file
<div>title</div>
<ul>
$(${points} | % { $_ })
</ul>
posh
[string[]]$points = "First Point", "Second Point"
$template = Get-Content 'c:\temp\docs\template.txt' -force
$expanded = Invoke-Expression $template
$expanded
output expected
<div>title<div>
<ul>
<li>First Point</li>
<li>Second Point</li>
</ul>
What do I need to do to create loop templates inside a text file?
EDIT:
here's the concept I'm going for as far as the template would be concerned.
<div class="container-fluid">
<div class="page-header">
<h1>${name}</h1>
<p class="lead">${synopsis}</p>
</div>
<div class="row">
<div class="col-md-12">
<h2> Syntax </h2>
</div>
<pre>
<code>${syntax}</code>
</pre>
</div>
<div class="col-md-12">
<h2> Detailed Description </h2>
<div>${description}</div>
</div>
{ifdef links}
<div class="col-md-12">
<h2> Related Commands </h2>
<div>${links}</div>
</div>
{endif links}
{ifdef parameters}
<div class="col-md-12">
<h2> Parameters </h2>
<table>
<tr>
<th>Name</th>
<th>Description</th>
<th>Required?</th>
<th>Pipeline Input</th>
<th>Default Value</th>
</tr>
<tr>
{foreach param in ${parameters}}
<td>$($param.Name)</td>
<td>$($param.Description)</td>
<td>$(param.Required)</td>
<td>$(param.PipelineInput)</td>
<td>$(param.DefaultValue)</td>
{endforeach}
</tr>
</table>
</div>
{endif parameters}
{ifdef notes}
<div class="col-md-12">
<h2> Notes </h2>
<div>${notes}</div>
</div>
{endif notes}
{ifdef examples}
<div class="col-md-12">
<h2> Examples </h2>
{foreach example in ${examples}}
<h3> Example </h3>
<pre>${example.code}</pre>
<div>${example.remarks}</div>
{endforeach}
</div>
</div>
Upvotes: 4
Views: 5950
Reputation: 5046
I know this is an old question, and you have a good workaround, but I thought others might be interested in the answer. What you had in the original question was very close.
This works
template.txt
<div>title</div>
<ul>$(${points} | ForEach {
@"
<li>$_<\li>
"@
})
</ul>
PowerShell
$points = 'First Point', 'Second Point', 'Third Point'
$template = Get-Content 'template.txt' -Raw
$expanded = $ExecutionContext.InvokeCommand.ExpandString($template)
$expanded
Output
<div>title</div>
<ul>
<li>First Point<\li>
<li>Second Point<\li>
<li>Third Point<\li>
</ul>
You got the one trick, using $( on the second line of the template to open an expression. But your expression didn't use the proper syntax. The second trick is to keep using ExpandString
like you did in the working example you provided. Once the proper syntax is provided in the template, it will be correctly evaluated by ExpandString
(because we're using an interpolated strings expression). The output matches your expected output (except I left in "Third Point" which I added while testing and fixing the formatting).
Upvotes: 3
Reputation: 46720
I am really interested in a good answer for this but I will expand on my suggestion in the comment. I'm sure you are capable of doing this yourself but this could be of value to someone.
If your file contained
<div>title</div>
<ul>
${list}
</ul>
Then we could do this to populate the list
$list = ("First Point", "Second Point" | ForEach-Object{"`t<li>$_</li>"}) -join "`r`n"
$template = Get-Content 'c:\temp\docs\template.txt' -raw
$ExecutionContext.InvokeCommand.ExpandString($template)
Yes I know this is not exactly what you are asking but it does have a similar result.
Disclaimer: I would like to point out that expanding strings and executed code from files are two different things. What if someone put malicious code in your template file? You could blindly execute it. Realistic problem? No. Possible catastrophe? Maybe.
I update a small part of your sample edit to prove the method. Yes the HTML is malformed and sucks. It exists to show what I mean.
Sample File
'<div class="container-fluid">'
' <div class="page-header">'
" <h1>$name</h1>"
" <p class=`"lead`">$synopsis</p>"
' </div>'
' <tr>'
foreach($param in $parameters){
" <td>$($param.Name)</td>"
" <td>$($param.Description)</td>"
}
' </tr>'
I picked that snippet since you see a simple variable, simple variable in quotes and a loop structure. I have single quotes around most of the lines since they will just be sent as standard string output. So, with that, we use the following code:
$name = "Matt"
$synopsis ="Ughhh... stuff"
$parameters = 1,2 | Foreach-object{[pscustomobject]@{Name="$_ Factory";Description="$_ Apples"}}
$template = Get-Content 'c:\temp\docs\template.txt' -raw
Invoke-Expression $template
Which nets the following, again awful, output.
<div class="container-fluid">
<div class="page-header">
<h1>Matt</h1>
<p class="lead">Ughhh... stuff</p>
</div>
<tr>
<td>1 Factory</td>
<td>1 Apples</td>
<td>2 Factory</td>
<td>2 Apples</td>
</tr>
Upvotes: 5