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"
Related
$StartID = Read-Host -Prompt "StartID"
$StopID = Read-Host -Prompt "StopID"
$i = $StartID
do {
Write-Host $startID
Write-Host $StopID
$i = ($StartId + 1)
} until ($i -gt $StopID)
The first problem is that after the statement $i = $startid + 1, the $i equals 11 and not 2.
The second problem is that even though the until statement says that it should stop when $i -gt $stop the loop continues forever.
How do I get the $i to increase by 1 and not 10 and how do I stop the loop when $i -gt $stop.
Read-Promptreturns a string per default (this stackoverflow answer explains different ways for conversion). You've to convert/cast the string to a numeric value:
[int]$start = Read-Host -Prompt "Start"
[int]$stop = Read-Host -Prompt "Stop"
do {
Write-host $start
$start++
} until ($start -ge $stop)
Hope that helps.
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
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.
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>
}
}
once again I contact you because I am once again having problems with a Powershell script assignment.
My current assignement is to make a script that lets a user put in 1-10 values. The values have to be positive numbers. After that I have to calculate an average of an array.
My idea in this was the following:
clear-host
$averageArray = #()
Write-Host "How many numbers do you want to put in?" -for yellow
Write-Warning "Maximum is 10!"
Write-Host "Press the Enter key to continue."
$amountValues = Read-host
while($amountVAlues -notmatch "^([1-9]|[1][0])$") {
$amountValues = Read-Host "Please enter a number between 1 and 10"
if($amountVAlues -notmatch "^([1-9]|[1][0])$") {Write-host "Error! Please enter a number between 1 and 10!"}
}
$value1 = read-host "Enter number one"
while($value1 -notmatch '^\d+$') {
$value1 = Read-Host
if($value1 -notmatch '^\d+$') {Write-host "Error! Please enter a positive number!"}
}
$value2 = read-host "Enter number two"
while($value2 -notmatch '^\d+$') {
$value2 = Read-Host
if($value2 -notmatch '^\d+$') {Write-host "Error! Please enter a positive number!"}
}
$averageArray = $averageArray + $value1
write-host "$averageArray"
read-host
I created an array, and made the user input a number between 1-10 for the amount of total $values they want in the array. After that I wanted to loop the input of a $value, and input it in the array. I wanted to loop it as many times as the $amountValues variable.
Problem with doing this is that if I would loop it, $variable1 would get overwritten an 'x' amount of times.
Is there any way to input the values via a loop into the array?
I'ld do it like this:
while ( (1..10) -notcontains $g)
{
$g = read-host "How many numbers do you want to put in? (value from 1 to 10) "
}
$ar=#()
for ($i=0; $i -lt $g; $i++)
{
$ar += read-host "Enter value $($i+1)"
}
$averageArray = ($ar | Measure-Object -Average).average
write-host "Average is : $averageArray"