Using @ sign in a powershell menu, not sure what it means

I am using the @ sign in a powershell menu and I am not exactly sure what it means:

function show-menu
{
$menu=@"
1 View vCenter Hosts & statistics
2 Display VM Guest statistics
3 Disply datastore statistics
4 Display VM snapshots
5 Change selected host to process
6 Backup Host and vSwitch configurations
7 Enter Host Maintenance Mode
8 Power off host VM's
9 Power on host VM's
10 Display Host VM GPU's
11 Display VM network interfaces
12 Clear ESXi host SEL log

Q Quit

Select a task by number or Q to quit
"@
cls
start-sleep -m 250
write-host
Write-Host "*** VM Utility Menu ***" -ForegroundColor Cyan
write-host
write-host "You have selected vCenter Server: " $vServer -ForegroundColor Green
write-host

$sel = Read-Host $menu
if ($sel -isnot [int]){
write-host "**Entry is incorrect, please choose a number from the menu**" -ForegroundColor Red
show-menu
}

Switch ($sel)
{
"1" {
Write-Host "View vCenter Hosts and statistics on" $Vserver -ForegroundColor Green
$VChostsStat = get-vmhost | Select Name,ConnectionState,PowerState,NumCpu,CpuUsageMhz,@{ n="MemoryUsedGB"; e={[math]::round( $_.MemoryUsageGB )}},@{ n="TotalMemoryGB"; e={[math]::round( $_.MemoryTotalGB )}},Version | ft
$VChostsStat
start-sleep -m 250
get-decision
}
"2" {

I am not quite sure what the $menu=@" and "@ are doing for me. Obviously it creates the list of menu items displayed on the screen but what’s the FUNCTION of the @ in this case, to build an array of menu items? Please explain a little, thanks.

TIA

This is called a Here-String. A here-string is a single-quoted or double-quoted string in which quotation marks are interpreted literally.

See Get-Help about_quoting_rules

#This is a short query, but imagine adding more or and...and...and
$query = "Select * From SomeDatabase Where SomeValue = 'SomeValue'"

#You try to make it more manageable to read...but you have to make sure you add spaces
$query = "Select * "
$query += "From SomeDatabase "
$query += "Where SomeValue = 'SomeValue'"

#Then comes the magical here string
$query = @"
    Select * 
    From SomeDatabase 
    Where SomeValue = 'SomeValue'
"@

#Another common use is html or text that is indented
$query = @"
<html>
    <head>
        <title>Test</test>
    </head>
    <body>
        <p>Some paragraph</p>
    </body>
</html>
"@

#The only flaw or drawback is you cannot indent the lines with the @ symbols.

Thank you. So am I correct in saying it only defines a block of text (string) to be output and there isn’t anything executable

within that block?

 

…ar

Correct mostly. It creates a string, but doesn’t have to be output. You can use it for anywhere you need a string object.

Not sure if this qualifies, but you can expand strings. For instance this would have HTML outline in the here-string and dynamically get services and add the HTML fragment:

$query = @"
<html>
    <head>
        <title>Test</test>
    </head>
    <body>
        <p>Some paragraph</p>

        $(Get-Service | Select -First 5 | ConvertTo-Html -Fragment -Property Name, Status)
    </body>
</html>
"@

$query

Great point Rob. If the here-string is made with double quotes ", then it will evaluate expressions following a $ as in your example, but if you use single quotes ’ then everything would be literal.

Can someone tell me why THIS code does not consider $msel an integer? It’s basically the same code as above.

$maintmenu=@"
1 Check host connection state
2 Enter Maintenance Mode"
3 Enter Normal Mode

Select a task by number or Q to quit
"@

$msel = Read-Host $maintmenu

if ($msel -isnot [int]){
write-host “Selection is : $msel”
write-host “Entry is incorrect, please choose a number from the menu” -ForegroundColor Red
}
Switch ($msel)
{
1 {‘Check host connection state’}
2 {‘Enter Maintenance Mode’}
3 {‘Enter Normal Mode’}
default {‘No Action’}
}

write-host “Number picked is $msel”

[/pre]

Read-Host ALWAYS returns a string regardless of what the user types. You can convert it by either typecasting or using the toint32() string method. You might want to error check the user input first to make sure it can be converted.

typecasting

$msel = [int]$msel

ToInt32() method

$msel = $msel.toInt32($null)

Forgot to add if you want to error check you can use regex.

"12" -match "^[1-3]$"

This will return $true for “1”, “2” or “3” but $false for anything else.

 

Thanks. But why does code I posted at the top work and this does not (lines 29-33)? What am I missing? Seems the

same to me. It’s the same code, copy pasted and modified from original script.

Your code in the last post wasn’t formatted so I don’t see line numbers. One thing to note about PowerShell which could be the source on confusion is that it will automatically type convert an object if possible and needed. This is why you don’t always have to put quotes around strings. Here are some examples to illustrate what I mean:

PS C:\>
PS C:\>
PS C:\> "15" + 3
153
PS C:\> 3 + "15"
18
PS C:\>
PS C:\> (Get-Process -Id 1028) -like (Get-Process -Id 1144)
True
PS C:\> (Get-Process -Id 1144).ToString()
System.Diagnostics.Process (svchost)
PS C:\> (Get-Process -Id 1028).ToString()
System.Diagnostics.Process (svchost)
PS C:\>

Notice “15” + 3 PS treated the 3 as a string an concatenated, but in 3 + “15” it treated “15” as an integer and performed addition. The -like operator expects this syntax <string> -like <wildcard-expression> so PS converted the process object to strings to perform the comparison which is why 2 different processes (different IDs) came up as $true. I included the ToString() results so you can see what was actually compared.

I also re-wrote your menu below. Notice I didn’t do any explicit type conversions because PS will convert if needed. i.e. 1 -eq “1” is $true.

$maintmenu=@”
1 Check host connection state
2 Enter Maintenance Mode”
3 Enter Normal Mode

Select a task by number or Q to quit
“@

$errormsg = “**Entry is incorrect, please choose a number from the menu**”

do
{
    $msel = Read-Host $maintmenu
    $badentry = $msel -notmatch "^[1-3q]$"
    if ($badentry) {write-host $errormsg  -ForegroundColor Red}
}
while ($badentry)

Switch ($msel)
{
    1 {‘Check host connection state’}
    2 {‘Enter Maintenance Mode’}
    3 {‘Enter Normal Mode’}
    default {‘No Action’}
}

write-host “Number picked is $msel”

 

Mike R-

Finally got back to my coding, was busy fixing a down server. After actually using Winmerge to compare code I found that in fact you are 100%

correct. I was going back and forth between multiple things and thought the menu I described was in the old code, thus I thought it worked before. Wrong! I guess I had slipped new code in there and got sidetracked and thought it was there all along. I tried typecasting it and that works just fine. Thank you for your suggestions, and to everyone!

TY