Search code examples
loopstemplatespowershell

Powershell text/html templates that include loops


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>

Solution

  • 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.

    Why not turn the whole file into code instead?

    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>