Run PowerShell if statement between business hours - powershell

I'm looking to run a conditional statement based on if it's business hours or not. Business hours for us are 08:00 to 17:00. I have the script below, but it's not working.
I'm trying to compare (Get-Date).tostring('%H') to the hour number.
I have also tried ((Get-Date).hour -ge 17) and it still failed.
Any thoughts?
while ($loop -eq 1) {
Write-Host "Running"
# Get last write timestamp
$lastwrite = [datetime](Get-ItemProperty -Path $source -Name LastWriteTime).lastwritetime
if ( ((Get-Date).tostring('%H') -le "8" ) -and ( (Get-Date).tostring('%H') -ge "17" ) ) {
# Do nothing, it is outside of the time window
Write-Host "Nothing to do, outside of business hours"
} elseif (($lastwrite -le (Get-Date).addMinutes(-$ageMinutes)) -and ((Get-Date).tostring('%H') -ge "8" -and (Get-Date).tostring('%H') -le "17")) {
# If it's older than $ageMinutes variable above, send an email
notify
$oldTimestampFound = 1
# Sleep for 4 minutes to not flood inboxes (5 minute sleep total with the while loop)
Write-Host "Alert sent. Sleeping for 4 minutes..."
Start-Sleep -s 300
} elseif (($lastwrite -ge (Get-Date).addMinutes(-$ageMinutes)) -and ($oldTimestampFound -eq 1)) {
$oldTimestampFound = 0
Write-Host "All clear"
notifyAllClear
}
Write-Host "Sleeping for 60 seconds..."
Start-Sleep -s 60
}
I put those Write-Hosts in there to try and debug, but my output is
Running
Sleeping for 60 seconds...

You're formatting the Get-Date wrong, use the -Format HH option for Get-Date Personally I'd set it once as an integer for easy comparisons like this:
[int]$hour = get-date -format HH
If($hour -lt 8 -or $hour -gt 17){ <do nothing> }
Else{
If($lastwrite -le (Get-Date).addMinutes(-$ageMinutes)){ <send email, set oldTimeStampFound flag, and sleep> }
Else{
<Clear oldTimeStampFound flag>
}
}

Related

My if loop is taking effect even when it should not

I am making a random number guessing program where users try to guess a number between 1 and 100 while receiving whether they were higher or lower. one of the stipulations was that there must be a function to quit the program upon the user entering Q or q so I formatted my loop as such:
$RandomNumber = Get-Random -Minimum 1 -Maximum 101
write-output $RandomNumber
$GuessNumber = 0
While ($RandomNumber -ne $Guess)
{
$Guess = Read-Host -Prompt 'Guess a number between 1 and 100 Or enter Q to quit'
if ($Guess -eq 'q' -or 'Q')
{
Write-output "Thank you for playing"
BREAK
}
if ($Guess -gt 100 -or $Guess -le 1)
{
Write-Output 'Invalid Input please enter a number between 1 and 100'
}
if ($Guess -le $RandomNumber)
{
Write-output 'Your number is lower than the hidden number please guess again'
$GuessNumber = $GuessNumber +1
}
if ($Guess -gt $RandomNumber)
{
Write-output 'Your number is higher than the hidden number please guess again'
$GuessNumber = $GuessNumber +1
}
if ($Guess -eq $RandomNumber)
{
$GuessNumber = $GuessNumber +1
Write-output 'Congratulations you guessed the number in' $GuessNumber 'tries'
}
}
the issue I run into is even when the input is equal to 10 for example it still outputs "Thank you for playing" meaning that if loop has taken effect. I am not sure why this occurs as I'm new to powershell and a reasoning for this happening would be helpful
Replace
if ($Guess -eq 'q' -or 'Q')
with
if ($Guess -eq 'q' -or $Guess -eq 'Q')
Your error partially comes from -or 'Q' which evaluates to $true always:
$false -or 'somestring' # => True
And it's also worth noting, -eq is case insensitive ('q' -eq 'Q' is $true).The other error comes from comparing a string with an integer. The output from Read-Host is System.String in this case:
'50' -gt 100 # => True
'50' -lt 100 # => False
On the other hand, you could make your code more readable using a switch statement. For input validation you could use a recursive script block to make sure its always right (between 1 and 100 or Q). To break the outer while loop you can use a labeled break.
$RandomNumber = Get-Random -Minimum 1 -Maximum 101
$validation = {
$choice = Read-Host 'Guess a number between 1 and 100 Or enter Q to quit'
if($choice -notmatch '^([1-9][0-9]?|100|q)$') {
Write-Warning 'Invalid Input, try again.'
return & $validation
}
$choice
}
$attempts = 0
:outer while($true) {
$choice = & $validation
if($choice -eq 'q') {
# End the Game
'Thanks for Playing'
break
}
$attempts++
switch([int]$choice) {
{$_ -gt $RandomNumber} {
"$_ was greater than hidden number"
break
}
{$_ -lt $RandomNumber} {
"$_ was lower than hidden number"
break
}
default {
"You guessed right after $attempts tries, Congratulations!"
break outer
}
}
}
The condition is wrong. $Guess -eq 'q' -or 'Q' returns true always, it should be $Guess -eq 'q' -or $Guess -eq 'Q'.

do-while loop with timeout

I have the following loop:
$output = (command)
do {
something
} while ($output -match "some string")
Which works fine. I want to add a timeout to the loop, how do I do that? The expectation is that at some point the output won't match the string but I don't want it to run forever if the output matches the string forever.
Just use the Get-Date cmdlet and check for that in your while condition. Example:
$startDate = Get-Date
$output = (command)
do {
something
} while ($output -match "some string" -and $startDate.AddMinutes(2) -gt (Get-Date))
Although using the Get-Date Cmdlet is valid, a cleaner approach would be to use the System.Diagnostics.Stopwatch class, which is available in .NET Core >= 1.0.
$timeout = New-TimeSpan -Seconds 5
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
do {
# do stuff here
} while ($stopwatch.elapsed -lt $timeout)
Sources:
https://mjolinor.wordpress.com/2012/01/14/making-a-timed-loop-in-powershell/
https://mcpmag.com/articles/2017/10/19/using-a-stopwatch-in-powershell.aspx
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch?view=netframework-4.8
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/new-timespan?view=powershell-6

Using the Get-Date cmdlet

I am taking a scripting class as part of my IT degree and I am stumped on my powershell script. I have to use Get-Date to time how long my game is played, and also create a log file that stores number of games played, and the shorted and longest games played. The log file must be created outside the script, but must update within the script. I put the code I have so far below.
$rand = new-object system.random
$number= $rand.next(0,11)
clear-host
do{ $a = read-host -prompt "enter number between 1 and 10"
if ($a -gt $Number) {Write-Host "number is too high"}
elseif ($a -lt $Number) {Write-Host "number is too low"}
elseif ($a -eq $Number) {Write-Host "You did it!! It took you $x tries!"}
else {"You have to guess a number!!!"}
$x = $x + 1
} while ($a -ne $number)
$path = C:\temp\logfile.log.txt
To time execution, grab the date before and after you've done your work, and then subtract the two to get a TimeSpan representing the time it took
$StartTime = Get-Date
# script goes here
$EndTime = Get-Date
$TimeTaken = $EndTime - $StartTime
Write-Host "A total of $($TimeTaken.TotalSeconds) has passed"
You could also use the StopWatch class to accomplish this:
$Watch = [System.Diagnostics.Stopwatch]::StartNew()
# script goes here
$Watch.Stop()
$TimeTaken = $Watch.Elapsed
Write-Host "A total of $($TimeTaken.TotalSeconds) has passed"

Is it possible to output subsequent information to same line

I have a script that pings an ip address and send that information to a console window. In the case of high ping times or missed pings, it is also written to a log. I would like to keep only the high ping times and missed pings in the console window and allow the good pings to overwrite each other. Is that possible?
For high ping times, this is the output (similar code is used for missed pings).
$out = ("{0}ms at $(get-date -format G)" -f $ping.ResponseTime)
write-host $out -foregroundcolor "yellow"
$out >> .\runningPing$ipAddress.txt
For normal ping times, the output is this.
$out ("{0}ms" -f $ping.ResponseTime)
write-host $out -foregroundcolor "green"
I'd like to make that last line just overwrite itself for normal pings, but let the high and missed pings push up the screen as the program runs. Is that something I can do in PS?
SOLUTION
Thanks to #Mathias R. Jensen, I came up with this solution:
if ($ping.statuscode -eq 0) {
if ($ping.responsetime -gt $waitTime) {
$highPings = $highPings + 1
$out = ("{0}ms at $(get-date -format G)" -f $ping.ResponseTime)
[console]::SetCursorPosition(0,$highPings + $droppedPings + 1)
write-host $out -foregroundcolor "yellow"
$out >> $outFile
}
else {
$out = ("{0}ms $i of $pingCount" -f $ping.ResponseTime)
[console]::SetCursorPosition(0,$highPings + $droppedPings + 2)
write-host $out -foregroundcolor "green"
}
}
else {
$droppedPings = $droppedPings + 1
$out = ("missed ping at $(get-date -format G)")
[console]::SetCursorPosition(0,$highPings + $droppedPings + 1)
write-host $out -foregroundcolor "red"
$out >> $outFile
}
I think you should use Write-Progress for the good pings. You don't need to give a percentage, and you can use the -Status parameter to show just the last good one.
Here's a small example I wrote that might demonstrate how it would look/operate (you can execute this yourself to see, it doesn't ping anything it's just a simulation):
$goods = 0
0..100 | % {
if ((Get-Random -Minimum 0 -Maximum 100) -ge 50) {
$goods += 1
Write-Progress -Activity Test -Status "Last good ping: $_ ($goods total good pings)"
} else {
Write-Warning "Bad ping"
}
Start-Sleep -Seconds 1
}
In this case you could have even calculated, for example a percentage of good pings and used that in Write-Progress but I wanted to show that you don't need to use it as a progress bar for it to be useful.
As I mentioned in the comments, the cursor position can be controlled by this method:
[control]::SetCursorPosition([int]$x,[int]$y)
The [console] type accelerator points to the same Console class that enables you to WriteLine() to the console in a C# console application. You can also control colors and other console behavior if you feel like it:
Clear-Host
[console]::ForegroundColor = "Red"
1..10|%{
[console]::SetCursorPosition(2+$_,$_-1)
[console]::WriteLine("Hello World!")
}
[console]::ForegroundColor = "Green"
briantist has the better approach to this but I was playing around and came up with this as well. It will not work in ISE but should do it's job on the PowerShell console. It uses "`b" which is the backspace character so that the text will overwrite itself on the console host write. Might not help you but could be useful to others.
switch($ping.ResponseTime){
{$_ -ge 0 -and $_ -le 100}{
$out = "{0}ms" -f $_
$options = #{ForegroundColor = "Green"; NoNewline = $true}
$backup = "`b" * $out.Length
}
{$_ -ge 500 -and $_ -le 900}{
$out = "{0}ms at $(get-date -format G)" -f $_
$options = #{ForegroundColor = "Yellow"; NoNewline = $false}
$backup = "`n"
}
}
Write-Host "$backup$out" #options
Uses a switch to set the options based on the the range of ping times. Sets a small hash table which is splatted to the write-host. Not perfect but it shows another way to do it.
Again this mostly done for fun.

Powershell - Check number for several ranges

Hi I am new to Powershell and need some help:
My Script gets a number by userinput. I want to check this number if it´s between these ranges. So far so easy, but the input from 1-9 is with a leading zero.
With google, I got this working without the special case "leading zero".
do {
try {
$numOk = $true
$input = Read-host "Write a number between 1-12"
} # end try
catch {$numOK = $false }
} # end do
# check numbers
until (
($input -ge 1 -and $input -lt 13) -or
($input -ge 21 -and $input -lt 25) -or
($input -ge 41 -and $input -lt 49) -or
($input -ge 61 -and $input -lt 67) -and $numOK)
Write-Host $input
For example:
Input "5" Output "5"
Input "05" stucks in loop, should be "05"
AddOn: Is it possible to block inputs like 1-9 and just accept inputs like 01-09?
you can use the -f operator to format the string here is to get a 2 digits number
PS>"{0:00}" -f 5
05
some readings : http://ss64.com/ps/syntax-f-operator.html