Do..Until on a never ending loop - loops

I have written a Do..Until statement to check whether a file exists. If the file is not there, it waits a couple seconds and checks again.
It is supposed to end when the file appears. I have tested by running the script without the file and then adding it in the folder as the script is running.
Instead of ending, it continues to loop endlessly. Can anyone see what I have missed?
$path = test-path "C:\Temp\test.txt"
do {
if (!($path)) {
Write-Host "Not here yet..."
Start-Sleep -s 3
}
} until($path)
Write-Host "Files here now"
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

The Test-Path statement is evaluated in the line
$path = Test-Path "C:\Temp\test.txt"
After that the variable $path contains the boolean result of the evaluation. Put the actual check in your conditions:
$path = "C:\Temp\test.txt"
do {
if (-not (Test-Path $path)) {
Write-Host "Not here yet..."
Start-Sleep -s 3
}
} until (Test-Path $path)
or define it as a function that you call in your conditions:
function Test-File {
Test-Path "C:\Temp\test.txt"
}
do {
if (-not (Test-File)) {
Write-Host "Not here yet..."
Start-Sleep -s 3
}
} until (Test-File)

OK, figured it out 3 minutes after posting this (and an hour before that of frustration!).
I needed to put the variable INSIDE the Do..Until statement. Like so:
do{
$path = test-path "C:\Temp\test.txt"
if (!($path))
{Write-Host "Not here yet..."
start-sleep -s 3}
}
until($path)
Write-Host "Files here now"
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

Related

How to continue a loop using PowerShell?

I want to continue my looping. But it does not continue once the first process not found.
This is my code.
function Capture
{
##some process##
Write-Host "Capture"
Start-Sleep -s 1
}
function error
{
##some process##
write-host "error function"
write-host "error function2"
write-host "error function3"
Capture
Exit
}
String = #('The Student Name is','Data Member')
try
{
foreach ($str in $String )
{
$Bio = Get-Content ".\DUMP1.log" -ErrorAction Stop | Where-Object {$_ -like "*$str*"}
if ($null -eq $Bio)
{
Write-Host "not found"
error
continue
}
return $Bio
}
}
catch
{
$msg = "Bio Not Found`n" + $_
$ExitCode = "133"
Exit $ExitCode
}
If the first array not exist in the DUMP.log file, then it stop the process, it does not continue to check the second array.
My expectation is, once the first array can not found, then check the second array, after all the array checking not found, then the process will continue to error function. But The process will not go to error function as long as the process still not checking all the array.
Anyone can help please. Thank you

Leave if loop inside while loop

I'm writing a PowerShell script that is constantly checking the status of a game server that I'm running and, depending on the states of certain exe's and variables, certain things will happen. The way I have this constructed is, first I have a timer start and I have a while loop that runs up until the timer hits 280 seconds. Within this while loop there are a series of if than else statements that look at all of the aforementioned variable and states of exe's and then they execute different things. What I'd like is that when it gets to the end of a condition (if the exe is not found and the server status is 0), then email me about that and then go back to the beginning of the entire while loop and keep looping and checking the status of the server.
Here is the script right now in:
//$serverStatus = 1 means servers up, 0 means down, 3 means crashed
$timeout = new-timespan -Seconds 280
$sw = [diagnostics.stopwatch]::StartNew()
while ($sw.elapsed -lt $timeout){
Start-Sleep -Seconds 10
if (((get-process $process -ea SilentlyContinue) -eq $Null) -And ($serverStatus -eq "1")) {
#Server is down and email
"0" > "D:\Ark Scripts\serverStatus.txt"
Write-Host "it's down - email"
sendMail -sendTo "my email" -emailBody "Ark Server is down."
} elseif (((get-process $process -ea SilentlyContinue) -eq $Null) -And ($serverStatus -eq "0")) {
#Server is down and we are aware
Write-Host "it's down - we know"
} elseIf (((get-process $process -ea SilentlyContinue) -ne $Null) -And ($serverStatus -eq "0")) {
#Server is up and email
"1" > "D:\Ark Scripts\serverStatus.txt"
Write-Host "it's up - email"
sendMail -sendTo "my email" -emailBody "Ark Server is back up."
} elseif ($serverStatus -eq "3") {
Send-RCON "listplayers" > "D:\Ark Scripts\RCONlog.txt"
$log = Get-Content "D:\Ark Scripts\RCONlog.txt"
if ($log -eq "Authentication failed!") {
Write-Host "crashed and we know"
} else {
"1" > "D:\Ark Scripts\serverStatus.txt"
Write-Host "Back up"
}
} else {
#Checks RCON connection for crashes and sends crash email if connection fails.
Send-RCON "listplayers" > "D:\Ark Scripts\RCONlog.txt"
$log = Get-Content "D:\Ark Scripts\RCONlog.txt"
if ($log -eq "Authentication failed!") {
Write-Host "crashed"
"3" > "D:\Ark Scripts\serverStatus.txt"
sendMail -sendTo "my email" -emailBody "Ark Server has crashed."
return
} else {
"1" > "D:\Ark Scripts\serverStatus.txt"
Write-Host "it's up"
}
}
}
Exit-PSSession
I just want this while loop to keep running every 10 seconds to check the state of the variables and whether the exe is found and constantly cycle.
This wont work, script will never go into the section "#Server is up and email will never"
} elseif (((get-process $process -ea SilentlyContinue) -eq $Null) -And ($serverStatus -eq "0")) {
#Server is down and we are aware
Write-Host "it's down - we know"
} elseIf (((get-process $process -ea SilentlyContinue) -ne $Null) -And ($serverStatus -eq "0")) {
#Server is up and email
You should nest your if statements to get a clear picture of what your doing

Powershell override command

I'm really new to powershell (I've used javascript a bit before), and I was wondering if there's a way to have a powershell script continue running though a loop until a user inputs a specific command to override the loop and break out of it? I've been able to pause the program to wait for a response, but I need the loop to continue running until someone enters a string such as "end." For example, in the following bit of code I wanted to break the while loop and complete a specific command if the user typed "quit," "charger on," or "charger off" at any point.
$overrideProgram = "null"
$overrideProgram = Read-Host
while ($overrideProgram -ne "quit",$overrideProgram -ne "charger on",$overrideProgram -ne "charger off") {
while ($true) {
$chargeLevel = (Get-WmiObject win32_battery).estimatedChargeRemaining #defining the variable "chargeLevel" as the charge percentage
if ($chargeLevel -le 40) {chargerOn}
if ($chargeLevel -ge 80) {chargerOff}
Start-Sleep -s 30 # 30 second delay before next check
}
}
Thanks so much.
Best way I can think to do this is to start a background job and use a script to control the termination of that job:
$job = start-job -scriptblock {
while ($true) {
$chargeLevel = (Get-WmiObject win32_battery).estimatedChargeRemaining #defining the variable "chargeLevel" as the charge percentage
if ($chargeLevel -le 40) {chargerOff}
if ($chargeLevel -ge 80) {chargerOn}
Start-Sleep -s 30 # 30 second delay before next check
}
}
Write-Host "Background job started with id: $($job.id)"
$run = $true
while ($run) {
$op = Read-Host -Prompt "What ya wanna do?"
switch($op) {
"quit" { $run = $false }
"charger on" { $run = $false }
"charger off" { $run = $false }
default { continue }
}
}
stop-job $job.id
receive-job $job.id # If you want to see the output
remove-job $job.id
In the above, a job is started and then your script will just sit in a loop waiting for the correct input. Once it has that, it will exit the loop and proceed to stop and remove the job that was started to begin with.
You will need to extend the contents of the -scriptblock argument of Start-Job to include the chargerOn and chargerOff definitions. If the script is going to be considerably complex, probably best to save it to a file and use the -FilePath argument of Start-Job

Loop section of script

mode con: cols=52 lines=16
while ($script -ne "Q") {
$tag = ""
while (-not ($tag)) {
$tag = Read-Host 'Enter tag # or Q to quit'
$date = get-date -f MM-dd-yyyy_HH_mm_ss
$username = [Environment]::Username
if(!(Test-Path -path "C:\Users\$username\Desktop\PSTools\Screenshot Dump\")) {
New-Item "C:\Users\$username\Desktop\PSTools\Screenshot Dump\" -type directory
}
}
if ($tag -eq "Q"){break}
cls
#$ErrorActionPreference = 'silentlycontinue'
cd "C:\Users\$username\Desktop\PSTools"
set-alias psexec "C:\Users\$username\Desktop\PSTools\PsExec.exe"
set-alias nircmd "C:\Users\$username\Desktop\PSTools\nircmd.exe"
------>LOOP START HERE
psexec \\$tag -i -c -s nircmd savescreenshotfull "C:\$tag-$date.png"
move "\\$tag\c$\$tag-$date.png" "C:\Users\$username\Desktop\PSTools\Screenshot Dump\$tag-$date.png"
explorer "C:\Users\$username\Desktop\PSTools\Screenshot Dump\$tag-$date.png"
------>LOOP END HERE
"`n"
}
Basically I'm trying to loop where I have stated above, but I'm use to doing it in command prompt and it's not the same. I want to make it loop X amount of times or until I CTRL+C to quit it.
You could also use a Foreach loop.
Let's say you want to loop 5 times :
$NumberOfLoops = 5
Foreach ($loop in (1..$NumberOfLoops)) { Do Loopy Stuff }
You can use a basic FOR statement to loop. Although, PowerShell has some great looping techniques. It's worth a look through...
http://social.technet.microsoft.com/wiki/contents/articles/4542.powershell-loops.aspx
For an example of a basic FOR loop, just do as so (will loop 10 times ($x=0-9)):
FOR ($x=0; $x -lt 10; $x++) { DO LOOPY STUFF };

PowerShell: Set-Content having issues with "file already in use"

I'm working on a PowerShell script that finds all the files with PATTERN within a given DIRECTORY, prints out the relevant lines of the document with the PATTERN highlighted, and then replaces the PATTERN with a provided REPLACE word, then saves the file back. So it actually edits the file.
Except I can't get it to alter the file, because Windows complains about the file already being open. I tried several methods to solve this, but keep running into the issue. Perhaps someone can help:
param(
[string] $pattern = ""
,[string] $replace = ""
,[string] $directory ="."
,[switch] $recurse = $false
,[switch] $caseSensitive = $false)
if($pattern -eq $null -or $pattern -eq "")
{
Write-Error "Please provide a search pattern." ; return
}
if($directory -eq $null -or $directory -eq "")
{
Write-Error "Please provide a directory." ; return
}
if($replace -eq $null -or $replace -eq "")
{
Write-Error "Please provide a string to replace." ; return
}
$regexPattern = $pattern
if($caseSensitive -eq $false) { $regexPattern = "(?i)$regexPattern" }
$regex = New-Object System.Text.RegularExpressions.Regex $regexPattern
function Write-HostAndHighlightPattern([string] $inputText)
{
$index = 0
$length = $inputText.Length
while($index -lt $length)
{
$match = $regex.Match($inputText, $index)
if($match.Success -and $match.Length -gt 0)
{
Write-Host $inputText.SubString($index, $match.Index) -nonewline
Write-Host $match.Value.ToString() -ForegroundColor Red -nonewline
$index = $match.Index + $match.Length
}
else
{
Write-Host $inputText.SubString($index) -nonewline
$index = $inputText.Length
}
}
}
Get-ChildItem $directory -recurse:$recurse |
Select-String -caseSensitive:$caseSensitive -pattern:$pattern |
foreach {
$file = ($directory + $_.FileName)
Write-Host "$($_.FileName)($($_.LineNumber)): " -nonewline
Write-HostAndHighlightPattern $_.Line
%{ Set-Content $file ((Get-Content $file) -replace ([Regex]::Escape("[$pattern]")),"[$replace]")}
Write-Host "`n"
Write-Host "Processed: $($file)"
}
The issue is located within the final block of code, right at the Get-ChildItem call. Of course, some of the code in that block is now a bit mangled due to me trying to fix the problem then stopping, but keep in mind the intent of that part of the script. I want to get the content, replace the words, then save the altered text back to the file I got it from.
Any help at all would be greatly appreciated.
Removed my previous answer, replacing it with this:
Get-ChildItem $directory -recurse:$recurse
foreach {
$file = ($directory + $_.FileName)
(Get-Content $file) | Foreach-object {
$_ -replace ([Regex]::Escape("[$pattern]")),"[$replace]")
} | Set-Content $file
}
Note:
The parentheses around Get-Content to ensure the file is slurped in one go (and therefore closed).
The piping to subsequent commands rather than inlining.
Some of your commands have been removed to ensure it's a simple test.
Just a suggestion but you might try looking at the documentation for the parameters code block. There is a more efficient way to ensure that a parameter is entered if you require it and to throw an error message if the user doesn't.
About_throw: http://technet.microsoft.com/en-us/library/dd819510.aspx
About_functions_advanced_parameters: http://technet.microsoft.com/en-us/library/dd347600.aspx
And then about using Write-Host all the time: http://powershell.com/cs/blogs/donjones/archive/2012/04/06/2012-scripting-games-commentary-stop-using-write-host.aspx
Alright, I finally sat down and just typed everything sequentially in PowerShell, then used that to make my script.
It was actually really simple;
$items = Get-ChildItem $directory -recurse:$recurse
$items |
foreach {
$file = $_.FullName
$content = get-content $file
$newContent = $content -replace $pattern, $replace
Set-Content $file $newcontent
}
Thanks for all your help guys.

Resources