stopwatch PowerShell input - powershell

I have simple issue, but it is very hard to me to solve it.
I want a stopwatch, when I put a time in seconds and next MessageBox appear.
It works when I input a number (10, 50, 120), but doesn't work when I put 60+5 or 60*2.
How I can convert 60*2 to 120 in PowerShell?
[int]$time = Read-host "Write a time"
Start-Sleep $time
[System.Windows.Forms.MessageBox]::Show("Tea!")

Using Invoke-Expression might be safe if it can be limited to only numeric calculations.
Add-Type -AssemblyName System.Windows.Forms
$userentry = Read-Host -Prompt "Write a time"
if ($userentry -match '^[\d+\-\*/\(\) ]*$') {
try {
$time = Invoke-Expression -Command $userentry -ErrorAction Stop
Start-Sleep $time
[System.Windows.Forms.MessageBox]::Show('Teal')
} catch {
Write-Host 'ERROR: Invalid numeric expression'
}
} else {
Write-Host 'ERROR: Not a valid numeric expression'
}

You could try Invoke-Expression $time to evaluate expressions like this. But be aware, that all types of PowerShell-Code can be send by this

Related

Comparing a String Variable via PowerShell

I'm trying to create an if and else statement within PowerShell that exits my code if the $value variable has a 0 or 1.
The below is my code which is executed remotely:
$MsgBox = {
Add-Type -AssemblyName PresentationFramework
$ButtonType = [System.Windows.MessageBoxButton]::OKCancel
$MesssageIcon = [System.Windows.MessageBoxImage]::Warning
$MessageBody = "Please save anything open in your current Web Browser, this includes Google Chrome, Microsoft Edge, and Mozilla FireFox and close each one respectively. Click Ok when done saving and closing browsers. IT Will be deleting your Cache and Cookies.
-IT"
$MessageTitle = "Read Me"
$Result = [System.Windows.MessageBox]::Show($MessageBody, $MessageTitle,$ButtonType,$MesssageIcon)
Switch ($Result){
'Ok' {
return "1"
}
'Cancel' {
return "0"
}
}
}
$value = invoke-ascurrentuser -scriptblock $MsgBox -CaptureOutput
$exit = "0"
Write-Host $value
if ($value.Equals($exit)){
Write-Host "Script Exiting"
exit
}
else{
Write-Host "Continuing Script"
}
Am I comparing the wrong way in PowerShell?
I tested the code with Write-Host and my scriptblock returns 0 or 1 as planned.
When it gets to the If and Else statement it doesn't seem to compare value and exit, and will instead go straight to the else statement and Write "Continuing Script"
I've also tried with $value -eq 0 and $value -eq "0".
Invoke-AsCurrentUser, from the third-party RunAsUser module, when used with -CaptureOutput invariably outputs text, as it would appear in the console, given that a call to the PowerShell CLI, is performed behind the scenes (powershell.exe, by default).
The captured text includes a trailing newline; to trim (remove) it, call .TrimEnd() before comparing values:
# .TrimEnd() removes the trailing newline.
$value = (Invoke-AsCurrentuser -scriptblock $MsgBox -CaptureOutput).TrimEnd()
if ($value -eq '0') {
Write-Host "Script Exiting"
exit
}
else {
Write-Host "Continuing Script"
}

How to test for a keypress or continue in powershell

I have a basic powershell function that works as a spambot.
function Start-Spambot{
[CmdletBinding()]
param(
[Parameter(Mandatory=$True,HelpMessage="The text you wish to spam.")]
[string]$text,
[Parameter(Mandatory=$True,HelpMessage="How many milliseconds between messages.")]
[decimal]$speed,
[Parameter(Mandatory=$True,HelpMessage="The tota ammout you want to spam.")]
[decimal]$number
)
$count = $number
Invoke-BalloonTip -Message "You have 3 seconds before the program starts spamming." -Title "Start-Spambot $text $speed $number"
Start-Sleep -Seconds 3
while ($count -gt 0){
[System.Windows.Forms.SendKeys]::SendWait("$text")
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
Start-Sleep -Milliseconds $speed
$count -= 1
}
[System.Windows.Forms.SendKeys]::SendWait("$text")
Invoke-BalloonTip -Message "$text has been typed $number times." -Title "Start-Spambot $text $speed $number"
}
The thing i want to add is a kill switch in the while loop. If you press 'esc' for example the script should break the while loop and continue as normal. (invoke-balloontip will make a popup in the bottem right). I have looked at a bunch and googled for hours but they all are just looking to go if the key is pressed and not the other way arround.
Any of you have a simple script or function for this?
Note:
(All of you who are complaining i dont have the code that i tried. Why would i save the things that dont work?)
Edit:
If possible it should work outside of the terminal.
After more searching i finaly found this method:
Add-Type -AssemblyName WindowsBase
Add-Type -AssemblyName PresentationCore
1..10000000 | ForEach-Object {
"I am at $_"
$isDown = [Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::LeftShift)
if ($isDown){
Write-Warning "ABORTED!!"
break
}
start-sleep -Milliseconds 10
}
Edit:
It wont work in the ise wich is kinda anoying

How to make script exit on first error occurrence in powershell?

I have the function below where i pass in two arrays. I would like the script to exit on the first occurrence of an error.
I tried different variations but i can't make the script to stop early.
I have tried to use ($LastExitCode -eq 0) it doesn't seem to work, all the scripts still continue running.
I also tried If ($? -ne "True") it doesn't work either, all the tests don't run at all.
function Run-Coverage {
param($openCoverPath,$testExecutorPath,$dllPath,$output)
& $openCoverPath `
-threshold:100 `
-register:user `
-target:$testExecutorPath `
-targetargs:"$dllPath" `
-output:$output `
}
function Run-Tests
{
param($Dll,$Xmls)
for ($i=0; $i -lt $Dlls.length; $i++)
{
$TestParam = $Dlls[$i]
$resultParam = $Xmls[$i]
$dllPath = -join("`"",$projectBasePath,$TestParam," ","/TestCaseFilter:`"(TestCategory!=RequiresData)`"","`"")
$output = -join($outputPath,$resultParam)
try
{
Run-Coverage -openCoverPath $openCoverPath -testExecutorPath $testExecutorPath -dllPath $dllPath -output $output
}
catch
{
Write-Host "Exiting loop"
break
}
}
}
If you add [CmdletBinding()] as the first line inside the function (above the param() block), you can call the function with added Common parameters like ErrorAction, ErrorVariable etc.
try {
Run-Coverage -openCoverPath $openCoverPath -testExecutorPath $testExecutorPath -dllPath $dllPath -output $output -ErrorAction Stop
}
catch {
throw
}
As #Paolo answered - you can do it easily using $ErrorActionPreference = "Stop" or make it a little bit custom using:
trap {
Write-Host "Exception occured: $($_.Exception.Message)";
#Some action to do with the error
exit 1;
}

Using Read-host with Input validation and TRAP

I'm just a typical admin trying to make a simple script for some IT assistants in remote offices, to make domain joins easier while minimizing potential errors. The script's end game is to run the one-liner command Add-Computer -DomainName $DomainToJoin -OUPath $LocDN -NewName $WS_NewName -Restart.
But the whole script is supposed to include input validation for the computer's new name as well as for the target OU for the two-letter code for the remote office.
Googling for code snippets for days, esp. from sites like yours, was very helpful. But the problem I have now is I couldn't find the right codes to combine Read-Host , input length validation, and TRAP to work together without losing the value for my variables.
Pardon my coding as obviously I'm no real PS scripter, and I know the wrong portions of it are very basic. I would want to spend more time if I had the luxury, but I would really so much appreciate it if you could point me in the right direction.
Thank you so much in advance.
Please see my code below:
# START: Display name and purpose of invoked script
$path = $MyInvocation.MyCommand.Definition
Clear-Host
Write-Host $path
Write-Host " "
Write-Host "This script will allow you to do the following in a single-step process:"
Write-Host "(1) RENAME this computer"
Write-Host "(2) JOIN it to MYDOMAIN"
Write-Host "(3) MOVE it to a target OU"
Write-Host "(4) REBOOT"
Write-Host " "
Pause
# Function: PAUSE
Function Pause ($Message = "Press any key to continue . . . ") {
if ((Test-Path variable:psISE) -and $psISE) {
$Shell = New-Object -ComObject "WScript.Shell"
$Button = $Shell.Popup("Click OK to continue.", 0, "Script Paused", 0)
}
else {
Write-Host -NoNewline $Message
[void][System.Console]::ReadKey($true)
Write-Host
}
Write-Host " "
}
# Function: Define the parameters
Function Define-Parameters {
# Specify new computer name, with validation and TRAP
$WS_NewName = $null
while ($null -eq $WS_NewName) {
[ValidateLength(8,15)]$WS_NewName = [string](Read-Host -Prompt "NEW NAME of computer (8-15 chars.)" )
TRAP {"" ;continue}
}
Write-Host " "
# Domain to join.
$DomainToJoin = 'mydomain.net'
# Specify the target OU, with validation and trap
$baseOU='OU=Offices OU,DC=mydomain,DC=net'
$OU2 = $null
while ($null -eq $OU2) {
[ValidateLength(2,2)]$OU2 = [string](Read-Host -Prompt 'Target OU (TWO-LETTER code for your office)' )
TRAP {"" ;continue}
}
Write-Host " "
$LocDN = "OU=$OU2,$baseOU"
}
# Function: Summary and confirmation screen for defined parameters.
Function Confirm-Parameters {
Write-Host "==========================================================================="
Write-Host "Please confirm that you are joining this computer to
$DomainToJoin (MYDOMAIN)"
Write-Host "with the following parameters:"
Write-Host ""
Write-Host ""
Write-Host "Computer's NEW NAME: $WS_NewName"
# Write-Host "Domain to Join: $DomainToJoin"
Write-Host "TARGET mission OU: $OU2"
}
# Call Define-Parameters Function
Define-Parameters
# Call Confirm-Parameters Function
Confirm-Parameters
<#
Some more code here
#>
# FINAL COMMAND if all else works: Join the computer to the domain, rename it, and restart it.
# Add-Computer -DomainName $DomainToJoin -OUPath $LocDN -NewName $WS_NewName -Restart
In your code, you have a lot of things defined very strangely. Your functions create a new scope and the variables you're trying to define therein will disappear after calling them unless you change the variable scope (in this case, to $script: or $global:). Also, to use functions, you need to define them first (your Pause doesn't actually do anything)
Here's something you can do with your Define-Parameters function (I suggest looking at Get-Verb)
# Domain to join.
$DomainToJoin = 'mydomain.net'
# Function: Define the parameters
Function Get-Parameters {
do {
$global:WS_NewName = Read-Host -Prompt 'NEW NAME of computer (8-15 chars)'
} until ($WS_NewName.Length -gt 7 -and $WS_NewName.Length -lt 16)
''
do {
$global:OU2 = Read-Host -Prompt 'Target OU (TWO-LETTER code for your office)'
} until ($OU2 -match '^[a-z]{2}$')
''
$OU2 = "OU=$global:OU2,OU=Offices OU,DC=mydomain,DC=net"
}
I'd strongly recommend moving away from the ISE to do your testing and test in an actual powershell console.
Perhaps Try/Catch block instead of trap?
Try {
[ValidatePattern('^\w{8,15}$')]$compname=read-host 'enter comp name' -ErrorAction Stop
[ValidatePattern('^\w{2}$')]$OU=read-host 'enter OU name' -ErrorAction Stop
}
Catch {
$ErrorMessage = $_.Exception.Message
$ErrorLineNumber = $_.InvocationInfo.ScriptLineNumber
$ErrorCommandName = $_.InvocationInfo.InvocationName
Write-Error -Message "The error message was: <$ErrorMessage>, script line: <$ErrorLineNumber>, command name: <$ErrorCommandName>"
exit 255
}

PowerShell console timer

I am attempting to create a simple console timer display.
...
$rpt = $null
write-status "Opening report", $rpt
# long-running
$rpt = rpt-open -path "C:\Documents and Settings\foobar\Desktop\Calendar.rpt"
...
function write-status ($msg, $obj) {
write-host "$msg" -nonewline
do {
sleep -seconds 1
write-host "." -nonewline
[System.Windows.Forms.Application]::DoEvents()
} while ($obj -eq $null)
write-host
}
The example generates 'Opening report ....', but the loop never exits.
I should probably use a call-back or delegate, but I'm not sure of the pattern in this situation.
What am I missing?
You should be using Write-Progress to inform the user of the run status of your process and update it as events warrant.
If you want to do some "parallel" computing with powershell use jobs.
$job = Start-Job -ScriptBlock {Open-File $file} -ArgumentList $file
while($job.status -eq 'Running'){
Write-Host '.' -NoNewLine
}
Here is what I think about Write-Host.
The script runs in sequence. When you input a $null object, the while condition will always be true. The function will continue forever until something breaks it (which never happends in your script.
First then will it be done with the function and continue with your next lines:
# long-running
$rpt = rpt-open -path "C:\Documents and Settings\foobar\Desktop\Calendar.rpt"
Summary: Your while loop works like while($true) atm = bad design