Powershell Assigning and Retrieving Variables - powershell

So I have been battling all day with Powershell. I'm trying to make something like I put down here. I declare a Variable in the beginning of the script, then I declare 2 functions. One function sets a value to the variable, and the other gets the variable.
When getting the variable I get nothing - it's empty.
Does anyone have any idea what I'm doing wrong (I'm guessing something little and stupid)
$ImUsedInMultplePlaces = ""
Function LetsChooseSomething
{
Write-Host "1: something"
Write-Host "2: Something else"
$answer = Read-Host "Pick One"
switch($answer)
{
"1" { $ImUsedInMultiplePlaces = "We chose something!"; Write-Host "I put it in there!" }
"2" { $ImUsedInMultiplePlaces = "We chose something else!"; Write-Host "I put it in there!" }
}
}
Function ShowMeMyChoice
{
Write-Host $ImUsedInMultiplePlaces
}
Write-Host "Welcome to this amazing script, im about to make you choose"
Write-Host ""
LetsChooseSomething
Write-Host ""
Write-Host "Great Choice!"
Write-Host ""
ShowMeMyChoice

It's a question of scope. Replace with these and give it a go.
"1" { $global:ImUsedInMultiplePlaces = "We chose something!"; Write-Host "I put it in there!" }
"2" { $global:ImUsedInMultiplePlaces = "We chose something else!"; Write-Host "I put it in there!" }

The use of global variables makes software harder to read and understand. Since any code anywhere in the program can change the value of the variable at any time, understanding the use of the variable may entail understanding a large portion of the program. Global variables make separating code into reusable libraries more difficult. They can lead to problems of naming because a global variable defined in one file may conflict with the same name used for a global variable in another file (thus causing linking to fail). A local variable of the same name can shield the global variable from access, again leading to harder-to-understand code. The setting of a global variable can create side effects that are hard to locate and predict. The use of global variables makes it more difficult to isolate units of code for purposes of unit testing; thus they can directly contribute to lowering the quality of the code.
I renamed (and modified) the functions slightly, aiming for more clarity.
The first function outputs a string:
function Select-Something
{
Write-Host "1: Something"
Write-Host "2: Something else"
$answer = Read-Host -Prompt "Pick One"
switch($answer)
{
"1" { [string]$output = "We chose something!" ; Write-Host "I put it in there!" }
"2" { [string]$output = "We chose something else!"; Write-Host "I put it in there!" }
}
return $output
}
By adding a ([string]) parameter to the second function it is able to accept any string:
function Show-Selection ([string]$Selection)
{
Write-Host $Selection
}
As you can see, it makes the code easier to read:
Write-Host "Welcome to this amazing script, I'm about to make you choose."
Write-Host ""
$choice = Select-Something
Write-Host ""
Write-Host "Great Choice!"
Write-Host ""
Show-Selection $choice

Related

Is there any way to colour text previously written using "Write-Host" in powershell?

I want to create the "Select-Multiple" function.
The function takes some parameters, but the most important one is the list of options.
Let's say
#("First Option", "Second Option")
Then the function will display something like:
a All
b First Option
c Second Option
d Exit
Choose your option: > ...
The "Choose your option: > ..." text, will be repeated as long as:
User choose "All" or "Exit" option
User will choose all possible options (other than "All" and "Exit")
At the end the function returns the List of options chosen by the user.
Simple. But... I'd like to highlight the options already chosen by the user.
So if the user chose "b", then "b First Option" gets green colour.
Is it possible to do something like that, without using Clear-Host, as I don't want to clear previous steps?
I attach you my "Select-Multiple" function in powershell, sorry if that's ugly written, but I don't use powershell that often.
function Select-Multiple {
Param(
[Parameter(Mandatory=$false)]
[string] $title,
[Parameter(Mandatory=$false)]
[string] $description,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
$options,
[Parameter(Mandatory=$true)]
[string] $question
)
if ($title) {
Write-Host -ForegroundColor Yellow $title
Write-Host -ForegroundColor Yellow ("-"*$title.Length)
}
if ($description) {
Write-Host $description
Write-Host
}
$chosen = #()
$values = #()
$offset = 0
$all = "All"
$values += #($all)
Write-Host -ForegroundColor Yellow "$([char]($offset+97)) " -NoNewline
Write-Host $all
$offset++
$options.GetEnumerator() | ForEach-Object {
Write-Host -ForegroundColor Yellow "$([char]($offset+97)) " -NoNewline
$values += #($_)
Write-Host $_
$offset++
}
$exit = "Exit"
$values += #($exit)
Write-Host -ForegroundColor Yellow "$([char]($offset+97)) " -NoNewline
Write-Host $exit
$answer = -1
while($chosen.Count -ne $options.Count) {
Write-Host "$question " -NoNewline
$selection = (Read-Host).ToLowerInvariant()
if (($selection.Length -ne 1) -or (([int][char]($selection)) -lt 97 -or ([int][char]($selection)) -gt (97+$offset))) {
Write-Host -ForegroundColor Red "Illegal answer. " -NoNewline
}
else {
$answer = ([int][char]($selection))-97
$value = $($values)[$answer]
if ($value -eq $exit) {
return $chosen
}
if ($value -eq $all) {
return $options
}
else {
if ($chosen.Contains($value)) {
Write-Host -ForegroundColor Red "The value $value was already chosen."
}
else {
$chosen += ($value)
}
}
}
if ($answer -eq -1) {
Write-Host -ForegroundColor Red "Please answer one letter, from a to $([char]($offset+97))"
}
$answer = -1;
}
return $chosen
}
Because of how the console window works, you can't just recolor an existing line. Once you've written something to the console, the only way you can modify it is by overwriting it. This is no different when applying colors.
To understand why this is the case, let's go over how text is colored in PowerShell. Let's use the following command as an example:
Write-Host "Test" -ForegroundColor Green
Here is a (simplified) step by step overview of what this command will do:
Set the ForegroundColor property of the console to green
Write "Test" to the console
Set the ForegroundColor property of the console to whatever it was previously
This explains why we are unable to change the color of text that has already been written to the console. If you want to color a piece of text, you are required to set the console color before writing the text to the console.
However, there are a couple ways to create the same visual effect. In fact there are exactly two ways. One of them is clearing the console and re-writing everything which you mentioned you don't want to do, so let's talk about the other way first.
Overwriting Individual Lines
Let me preface this by saying that this does not work very well with the PowerShell ISE. If you decide to use this, you will have to debug it by either using the normal PowerShell console, or an IDE that supports this. This is also the more complicated option, so if you don't want to deal with the complexity of it, the second option would be the way to go.
The console window allows you to retrieve and set the cursor position by using Console.CursorLeft and Console.CursorTop. You can also use Console.SetCursorPosition() if you need to set both of them at the same time. There is also $Host.UI.RawUI.CursorPosition, but it's long and has some strange side effects when paired with Read-Host, so I would not recommend using it. When you write output to the console, it will write the output to wherever the cursor happens to be. We can use this to our advantage by setting the cursor's position to the beginning of the line we want to change the color of, then overwriting the normal text with colored text or vice versa.
In order to do this, all we need to do is keep track of which option is on which line. This is pretty simple, especially if you have an array of options that is in the same order that you printed them to the console in.
Here is a simple script I made that does exactly this:
$options = $("Option 1", "Option 2", "Option 3")
$initialCursorTop = [Console]::CursorTop
# An array to keep track of which options are selected.
# All entries are initially set to $false.
$selectedOptionArr = New-Object bool[] $options.Length
for($i = 0; $i -lt $options.Length; $i++)
{
Write-Host "$($i + 1). $($options[$i])"
}
Write-Host # Add an extra line break to make it look pretty
while($true)
{
Write-Host "Choose an option>" -NoNewline
$input = Read-Host
$number = $input -as [int]
if($number -ne $null -and
$number -le $options.Length -and
$number -gt 0)
{
# Input is a valid number that corresponds to an option.
$oldCursorTop = [Console]::CursorTop
$oldCursorLeft = [Console]::CursorLeft
# Set the cursor to the beginning of the line corresponding to the selected option.
$index = $number - 1
[Console]::SetCursorPosition(0, $index + $initialCursorTop)
$choice = $options[$index]
$isSelected = $selectedOptionArr[$index]
$choiceText = "$($number). $($choice)"
if($isSelected)
{
Write-Host $choiceText -NoNewline
}
else
{
Write-Host $choiceText -ForegroundColor Green -NoNewline
}
$selectedOptionArr[$index] = !$isSelected
[Console]::SetCursorPosition($oldCursorLeft, $oldCursorTop)
}
# Subtract 1 from Y to compensate for the new line created when providing input.
[Console]::SetCursorPosition(0, [Console]::CursorTop - 1)
# Clear the input line.
Write-Host (' ' * $Host.UI.RawUI.WindowSize.Width) -NoNewline
[Console]::CursorLeft = 0
}
The main advantage of this approach is that it doesn't need to clear the entire console in order to update text. This means you can display whatever you want above it without worrying about it being cleared every time the user inputs something. Another advantage is that this performs a minimal number of operations in order to accomplish the task.
The main disadvantage is that this is relatively volatile. This requires you to use exact line numbers, so if something happens that offsets some of the lines (such as one option being multiple lines), it will more than likely cause some major issues.
However, these disadvantages can be overcome. Since you have access to $Host.UI.RawUI.WindowSize.Width which tells you how many characters you can put on a single line, we know that any string with a length greater than this will be wrapped onto multiple lines. Another option is just to keep track of which line the cursor starts on, then you can clear all text between the starting position and where the cursor currently is.
Clearing the Console
This approach is much simpler because you don't have to worry about what is on which line or where the cursor is at all. The idea is that you simply clear the entire console, then re-write everything with the changes you want to make. This is the nuclear approach, but it's also the most reliable.
Here is the same example as above using this approach instead:
$options = $("Option 1", "Option 2", "Option 3")
# An array to keep track of which options are selected.
# All entries are initially set to $false.
$selectedOptionArr = New-Object bool[] $options.Length
while($true)
{
Clear-Host
for($i = 0; $i -lt $options.Length; $i++)
{
if($selectedOptionArr[$i])
{
Write-Host "$($i + 1). $($options[$i])" -ForegroundColor Green
}
else
{
Write-Host "$($i + 1). $($options[$i])"
}
}
Write-Host # Add an extra line break to make it look pretty
Write-Host "Choose an option>" -NoNewline
$input = Read-Host
$number = $input -as [int]
if($number -ne $null -and
$number -le $options.Length -and
$number -gt 0)
{
# Input is a valid number that corresponds to an option.
$index = $number - 1
$choice = $options[$index]
$selectedOptionArr[$index] = !$selectedOptionArr[$index]
}
}
The main advantage of this approach is that it's super simple and easy to understand.
The main disadvantage is that this clears the entire console every time the user inputs something. In most cases this isn't a huge problem, but it can be with large data sets.
I agree with Trevor Winge, I it is most likely not possible to change earlier console outputs appearance, but it is for certain not worth your while. There are certain limitations of the console, and that is ok since that is what GUIs are for. I hope you feel encouraged by this situation to look into Windows.Froms or WPF. Make use of the controls! checkboxes would be interesting for your scenario. I know that is not exactly what you asked for, but it is garanteed a journey that is worth your time. When i started my first GUI with powershell, i was astonished how much i could accomplish with little afford. Stackoverflow is full of examples.

Passing bool parameters to my script always results in the same output

So I'm working on a script and I would like to add an optional parameter. For some reason it doesn't work the way I thought it should, so clearly I'm not getting something here.
I tried 2 methods:
Method 1
function Thing {
[CmdletBinding()]
param (
[Parameter(Mandatory=$false)][bool]$StartThing
)
if ($StartThing -eq $true) {
Write-Host "Thing started" -ForegroundColor Green
}
else {
Write-Host "Thing didn't start" -ForegroundColor Red
}
}
Thing
Then, I'm running: .\test.ps1 -StartThing $true
The output I'm getting is always Thing didn't start. No matter what value I pass in.
Method 2
I tried using switch instead in this form:
function Thing {
param (
[switch] $StartThing
)
switch ($StartThing) {
$true { Write-Host "Thing started" -ForegroundColor Green }
Default { Write-Host "Thing didn't start" -ForegroundColor Red }
}
}
Thing -StartThing
This one doesn't work for me either. When I run .\test.ps1 -StartThing the output is always Thing started. I tried adding $true or $false inputs but the output is the same no matter what.
I'm not sure what I'm doing wrong.
Any help would be great :)
StartThing is not being passed to your function. You always run is without any params (example from method 1, for method 2 the issue is similar):
Thing
If you want to run the file in the form provided, you need to add param block to the script file itself.
NOTE: Only relevant parts of code were added, I haven't changed the working parts. Please see Olaf's helpful answer to improve the script even better:
# Param block for script
param (
[Parameter(Mandatory=$false)][bool]$StartThing
)
function Thing {
[CmdletBinding()]
param (
[Parameter(Mandatory=$false)][bool]$StartThing
)
if ($StartThing -eq $true) {
Write-Host "Thing started" -ForegroundColor Green
}
else {
Write-Host "Thing didn't start" -ForegroundColor Red
}
}
# Function runs with the parameter provided
Thing -StartThing $StartThing
This might clear it up a little:
function Thing {
param (
[switch] $StartThing
)
if ($StartThing) {
"You provided the parameter 'StartThing'"
}
else {
"You did not provide the parameter 'StartThing'"
}
}
Now you simply run your function with
Thing -StartThing
And the result would be:
You provided the parameter 'StartThing'

Powershell splatting a nested hash table

I have a function that returns a complex nested hash table data structure, where part of it forms the arguments for a further function call, and part of it is strings for reporting errors that result in the arguments not being populated.
Ideally I would like to splat just the arguments hash table, but I am starting to think that can't be done.
So, an example of the basic problem looks like this...
function Test {
param (
[String]$A,
[String]$B,
[String]$C
)
Write-Host "A: $A"
Write-Host "B: $B"
Write-Host "C: $C"
}
$data = #{
arguments = #{
A = 'A string'
B = 'Another string'
C = 'The last string'
}
kruft = 'A string that doesn not need to get passed'
}
Ideally I want to splat $data.arguments, so something like this...
Test #data.arguments
But that doesn't work, resulting in the error
The splatting operator '#' cannot be used to reference variables in an expression. '#data' can be used only as an argument to a command. To
reference variables in an expression use '$data'.
So I tried...
Test #(data.arguments)
Which results in the error
data.arguments : The term 'data.arguments' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the
spelling of the name, or if a path was included, verify that the path is correct and try again.
I also tried...
Test #($data.arguments)
Which results in the whole hash table being passed as a single argument and the output is
A: System.Collections.Hashtable
B:
C:
What DOES work is...
$arguments = $data.arguments
Test #arguments
Which has me thinking you really cannot splat anything but a simple variable that is an appropriate hash table. But, I am hoping someone can verify that is indeed true, or point out the solution I haven't come up with yet.
The actual code requires 5 arguments, with somewhat verbose names because I prefer descriptive names, so splatting is very much an appropriate solution. Needing to make a new variable with just the hash table to be passed isn't an issue, just wondering if it really is the only option.
That's not possible.
Any variable (and only variables) provided with "#" instead of "$" for a function parameter is declared as TokenKind "SplattedVariable".
See:
https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.language.tokenkind?view=powershellsdk-7.0.0
PS:
Some quick tests from my side which could have succeed (apart from PS design):
Write-Host 'Test 1' -ForegroundColor Yellow
Test #$data.arguments
Write-Host 'Test 2' -ForegroundColor Yellow
Test #$($bla = $data.arguments)
Write-Host 'Test 3' -ForegroundColor Yellow
Test #$bla = $data.arguments
Write-Host 'Test 4' -ForegroundColor Yellow
Test #$bla = $data.arguments.GetEnumerator()
Write-Host 'Test 5' -ForegroundColor Yellow
Test #$($data.arguments.GetEnumerator())
... but they didn't.
I cannot claim to fully understand what you're attempting, but here are a couple of solutions that might help.
Your Test function is expecting three strings, but I don't see anything in your example that satisfies that. I would either change it to accept a Hashtable (which is the data type in question) or have it accept a string array and pass $data.arguments.values
Using a Hashtable:
function Test {
param (
[Hashtable]$hash
)
write-host $hash.A
write-host $hash.B
write-host $hash.C
}
Test $data.arguments
Using a String array:
function Test {
param (
[String[]]$a
)
$a | % { write-host $_ }
}
Test $data.arguments.values
This is a totally artificial way (more of a hack) of doing this but you could pass it as a hash table then self-call with the arguments splatted
function Test {
param (
[Parameter()]
[hashtable]$ht,
[String]$A,
[String]$B,
[String]$C
)
if($null -eq $ht){
Write-Host "A: $a"
Write-Host "B: $B"
Write-Host "C: $C"
}
else
{
Write-Host "ht.A: $($ht.a)"
Test -a $($ht.A+ " new blob") -b $ht.B -c $ht.C
Test #ht
}
}
$tdata = #{
arguments = #{
A = 'A string'
B = 'Another string'
C = 'The last string'
}
kruft = 'A string that doesn not need to get passed'
}
Write-Host 'Test ht' -ForegroundColor Yellow
Test -ht $tdata.arguments

Powershell beginner here, cant figure out how to transfer parameters from funtions to outside the function, like this

Heres an example that wont give a value to the $Prompt outside the function, atleast not from my perspective
function funkyfunction {
$Prompt = Read-Host "Write something"
}
funkyfunction
Write-Host "You wrote: $Prompt"
You want to return the Prompt variable
function funkyfunction {
$Prompt = Read-Host "Write something"
return $Prompt
}
$Prompt = (funkyfunction)
Write-Host "You wrote: $Prompt"
Another way, if you are going to output all the time, is simply to use the variable squeezing also, the variable scope is important.
function funkyfunction
{
$Script:Prompt = Read-Host "Write something"
}
funkyfunction
# Output to the screen is the default, so Write-* not really needed
"You wrote: $Prompt"
# Results
<#
Write something: test
You wrote: test
#>
Or squeezing
function funkyfunction
{
("You wrote: $(($Prompt = Read-Host 'Write something'))")
}
funkyfunction
# Results
<#
Write something: test
You wrote: test
#>

Read individual key presses in PowerShell

Using PowerShell, I would like to read the value of key presses as they occur, without the user having to press enter. For example, if the user presses '1' I would like the PowerShell script to react to the choice immediately without an 'enter'.
My research has turned up ReadKey,
$input = $Host.UI.RawUI.ReadKey('IncludeKeyDown');
But ReadKey returns much more information in the string than I require:
72,h,0,True
While I can parse the key press from this string, I'd prefer a more direct option. Does one exist?
Perhaps you could clarify a bit - but ReadKey returns a KeyInfo object not a string. KeyInfo contains VirtualKeyCode, Character, ControlKeyState and KeyDown members - all fields in your output string in that order. In fact, it looks like PowerShell has just called the .ToString() method in your output example. You will probably want to look at the Character property to find your desired character. Consider the following example where I press 1:
$key = $Host.UI.RawUI.ReadKey()
if ($key.Character -eq '1') {
"Pressed 1"
}
Regarding the use of $Host.UI.RawUI.ReadKey('IncludeKeyDown');
Goyuix answer should be marked as the right answer.
I found the $Host.UI.RawUI.ReadKey rather long and wanted to use [console].
But was a bad decision. This is a warning. Some might know how to use it right.
Key will give you for pressing some keys
1 = D1
1 on the NumPad = NumPad1
a = A (only capitalized letters)
- = OemMinus (yeah)
Additionally the similar looking [console]::ReadKey it will the Key Property.
$key = [console]::ReadKey()
if ($key.Key -eq 'D1') {
"Pressed 1"
}
This will definitely do what you want.
This accepts a single character as input and has validation.
I use a menu system to run programs and utilities using PowerShell.
When I hit a character, it goes right to the option and runs without hitting enter
I have extracted out the essentials of the input and menu system and gave a few example menu items.
Please note: There are 2 modes for this to run in
MODE 1: Uses Read-Host, requires Enter, Runs in ISE. Use this for troubleshooting/building
MODE 2: Uses ReadKey, no Enter required, Does NOT run in ISE... You will need to run this in a PowerShell command line. The code below is currently in Mode 2.
Comment/UnComment the lines as needed to change the mode
##Formatting Variables
$fgc1 = 'cyan'
$fgc2 = 'white'
$indent = ' '
Function MainMenu {
CLS
Write-Host "###############"
Write-Host "## Main Menu ##"
Write-Host "###############"
Write-Host -NoNewLine "$indent" "A " -ForegroundColor 'red'; Write-Host "== Options A" -ForegroundColor $fgc2
Write-Host -NoNewLine "$indent" "B " -ForegroundColor 'red'; Write-Host "== Options B" -ForegroundColor $fgc2
Write-Host -NoNewLine "$indent" "C " -ForegroundColor 'red'; Write-Host "== Options C" -ForegroundColor $fgc2
Write-Host -NoNewLine "$indent" "D " -ForegroundColor 'red'; Write-Host "== Options D" -ForegroundColor $fgc2
Write-Host -NoNewLine "$indent" "E " -ForegroundColor 'red'; Write-Host "== Options E" -ForegroundColor $fgc2
Write-Host -NoNewLine "$indent" "F " -ForegroundColor 'red'; Write-Host "== Options F" -ForegroundColor $fgc2
Write-Host -NoNewLine "$indent" "G " -ForegroundColor 'red'; Write-Host "== Options G" -ForegroundColor $fgc2
Write-Host ""
#This gives you a way to set the current function as a variable. The Script: is there because the variable has to
#be available OUTSIDE the function. This way you can make it back to the menu that you came from as long as all
#of your menus are in functions!
$Script:SourceMenu = $MyInvocation.MyCommand.Name
# Mode 1#
#Use this for troubleshooting so that you can stay in ISE
# Uncomment the 2 lines below to use Read-Host. This will necessitate an ENTER Key. BUT, it WILL work in ISE
#$K = Read-Host - "Which option?"
#MenuActions
# Mode 2#
#Uncomment the line below to use ReadKey. This will NOT necessitate an ENTER Key. BUT, it ## will NOT work ## in ISE
ReadKey
}
Function ReadKey {
Write-Host "Please make your choice..."
Write-Host ""
Write-Host "Press Q to quit"
$KeyPress = [System.Console]::ReadKey()
#This gets the keypress to a common variable so that both modes work (Read-Host and KeyPress)
$K = $KeyPress.Key
#Jumps you down to the MenuActions function to take the keypress and "Switch" to it
MenuActions
}
Function MenuActions {
Switch ($K) {
A {CLS;Write-Host "You Pressed A";Write-Host "Going to pause now... ";&pause}
B {CLS;Write-Host "You pressed B";Write-Host "Going to pause now... ";&pause}
C {CLS;Write-Host "You pressed C";Write-Host "Going to pause now... ";&pause}
D {CLS;Write-Host "You pressed D";Write-Host "Going to pause now... ";&pause}
E {CLS;Write-Host "You pressed E";Write-Host "Going to pause now... ";&pause}
F {CLS;Write-Host "You pressed F";Write-Host "Going to pause now... ";&pause}
G {CLS;Write-Host "You pressed G";Write-Host "Going to pause now... ";&pause}
#This is a little strange of a process to exit out, but I like to use an existing mechanism to exit out
#It sets the $SourceMenu to a process that will exit out.
#I use this same process to jump to a different menu/sub-menu
Q {$SourceMenu = "Exit-PSHostProcess";CLS;Write-Host "Exited Program"}
}
#This next command will loop back to the menu you came from. This, in essence, provides a validation that one of the
#"Switch ($X.key)" options were pressed. This is also a good way to always find your way back to
#the menu you came from. See "$Script:SourceMenu = $MyInvocation.MyCommand.Name" above.
#
#This is also the way that the Menu Item for Q exits out
& $SourceMenu
}
# This runs the MainMenu function. It has to be after all the functions so that they are defined before being called
MainMenu
The main part of the whole thing is:
$KeyPress = [System.Console]::ReadKey()
#This gets the keypress to a common variable so that both modes work (Read-Host and KeyPress)
$K = $KeyPress.Key
#
#Then do something with $K
What about a system that accepts 2 key presses?
Just a little addition here. Since we are talking about a SINGLE key press... how about a double key press? Well, this will work fine. Just stack up the ReadKey commands and assign variables to each, then combine them:
Write-Host "Press the 2 character option you wish"
#Get KeyPress1 Variable
$KeyPress1 = [System.Console]::ReadKey()
#This gets the keypress to a common variable so that both modes work (Read-Host and KeyPress)
$K1 = $KeyPress1.Key
#Get KeyPress1 Variable
$KeyPress2 = [System.Console]::ReadKey()
#This gets the keypress to a common variable so that both modes work (Read-Host and KeyPress)
$K2 = $KeyPress2.Key
#This is just for troubleshooting to prove it works
CLS
Write-Host "This is the state of the variables right now"
Write-Host "Keypress1 is: $K1" -ForegroundColor Green
Write-Host "Keypress1 is: $K2" -ForegroundColor Green
$KEYS = "$K1"+"$K2"
Write-Host "The combined presses are: $KEYS" -ForegroundColor Red
pause
I look forward to questions or comments.
Seems $Host.UI.RawUI.ReadKey() not works in VSCode console.
Here are my ReadKey implementation.
class DConsole {
static [System.ConsoleKeyInfo]ReadKey() {
return [DConsole]::ReadKey($true)
}
static [System.ConsoleKeyInfo]ReadKey([bool]$noEcho = $true) {
$key = [System.Console]::ReadKey()
if ($noEcho) {
$cTop = [System.Console]::CursorTop
[System.Console]::SetCursorPosition(0, $cTop)
}
return $key
}
}
Usage:
$key = [DConsole]::ReadKey()
# or
$key = [DConsole]::ReadKey($false)
Any improvements are welcome.
Back to the question, $key.KeyChar.ToString() may what you want.