How to get script to start over - powershell

I have this section of code that I need to loop through if the file is locked.
What I want to do is if the file is locked the script goes to sleep for 10 seconds then goes back to the if (test-Path) and runs through again until the file(s) are no longer locked.
I'm just not understanding how to do this, any help is appreciated.
if (Test-Path -Path "$path\*")
{
# Code for directory not empty
# enumerate the items array
foreach ($item in $items)
{
# if the item is NOT a directory, then process it.
if ($item.Attributes -ne "Directory")
{
$filePath = $path+$item.name
}
else
{
}
function isFileLocked([string]$LockedPath)
{
$oFile = New-Object System.IO.FileInfo $LockedPath
# Make sure the path is good
if ((Test-Path -LiteralPath $LockedPath) -eq $false)
{
#If file is locked go to sleep for 2 minutes and check again, loop until the file is no longer locked.
Start-Sleep -s 10
# Go back and check directory again to see if more files have come in
#return $false
}

You need to use a while or do loop if you want it to go back to the if statement. If you want it to skip to the next item in the foreach loop you can use continue. help about_continue
Edit: example:
Do {
Start-Sleep -s 10
}
Until (Test-Path $LockedPath)
or
#Script block executes as long as condition value = True
While (! (Test-Path $Lockedpath) )
{Sleep 10}
The do until is easier logically to use, but the benefit of the second option is it tests the condition before executing the script block which in some cases (such as this one), it will be more efficient.

Related

How Do I Exit While Loop From Within a Switch Statement

In PowerShell, how do I exit this while loop that is nested inside a switch statement, without executing the code immediately following the while block? I can't seem to figure it out. Everything I've tried so far results in that block of code being executed.
Here's what I'm trying to accomplish:
Check for the presence of a file and notify user if file is
detected.
Check again every 10 seconds and notify user
If the file is not detected, then exit the loop and switch, and continue
with Step #2
If the file is still detected after 30 seconds, timeout and exit
the script entirely.
Here's the code:
try {
#Step 1
$Prompt = <Some Notification Dialog with two buttons>
switch ($Prompt){
'YES' {
# Display the Windows Control Panel
#Wait for user to manually uninstall an application - which removes a file from the path we will check later.
$Timeout = New-Timespan -Seconds 30
$Stopwatch = [Dispatch.Stopwatch]::StartNew()
while ($Stopwatch.elapsed -lt $Timeout) {
if (Test-Path -Path "C:\SomeFile.exe" -PathType Leaf) {
Write-Host "The file is still there, remove it!"
return
}
Start-Sleep 10
}
#After timeout is reached, notify user and exit the script
Write-Host "Timeout reached, exiting script"
Exit-Script -ExitCode $mainExitCode #Variable is declared earlier in the script
}
'NO' {
# Do something and exit script
}
}
# Step 2
# Code that does something here
# Step 3
# Code that does something here
} catch {
# Error Handling Code Here
}
You can use break with a label, to exit a specific loop (a switch statements counts as a loop), see about_break.
$a = 0
$test = 1
:test switch ($test) {
1 {
Write-Output 'Start'
while ($a -lt 100)
{
Write-Output $a
$a++
if ($a -eq 5) {
break test
}
}
Write-Output 'End'
}
}
Write-Output "EoS"
Is that what you see without the try/catch? I get an exception: Unable to find type [Dispatch.Stopwatch]. Otherwise the return works ok for me.
I think what you want is break with a label going outside the switch? Then steps 2 & 3 will run. I altered the code to make a manageable example. This is more ideal when asking a question. I don't know what exit-script is.
echo hi > file
#try {
#Step 1
$Prompt = '<Some Notification Dialog with two buttons>'
$Prompt = 'yes'
:atswitch switch ($Prompt){
'YES' {
'Display the Windows Control Panel'
#Wait for user to manually uninstall an application - which removes a file from the path we will check later.
$Timeout = New-Timespan -Seconds 30
#$Stopwatch = [Dispatch.Stopwatch]::StartNew()
while (1) {
if (Test-Path -Path "file" -PathType Leaf) {
Write-Host "The file is still there, remove it!"
break atswitch
}
Start-Sleep 10
}
#After timeout is reached, notify user and exit the script
Write-Host "Timeout reached, exiting script"
'Exit-Script -ExitCode $mainExitCode #Variable is declared earlier in the script'
}
'NO' {
'Do something and exit script'
}
}
# Step 2
'Code that does something here'
# Step 3
'Code that does something here2'
#} catch {
# 'Error Handling Code Here'
#}

Displaying only changes when using get-content -wait

I created the following function which I wanted to use for a very simple CTI solution I have to use at work. This CTI process is writing all received calls to a text logile.
This function starts a new powershell Job and checks if the .log has been saved during the last 2 seconds and gets the last 4 lines of the log (receiving calls always creates 4 new lines).
During the job update I'm using regex to find the line with the phonenumber and time and append this to a richtextbox in a form.
In theory this works exactly as I want it to work. If I manually add new lines and save the file, it's always showing the timecode and phone number.
In the field however, this doesn't work as the CTI process is opening the file and doesn't save the it unless the process is shutting down.
I know that I can use get-content -wait to display new lines. I already tested this in the console and it's displaying new lines as soon as the .log is updated from the CTI process. What I don't know is how to rewrite the function to work with that, displaying only new lines and not all the old stuff when first running the script. I need to keep it in the job for a responsive form. Another thing is, that the computer running the form, doesn't have that much power. I don't know if get-content -wait could cause high memory usage after several hours. Maybe there are also some alternative solutions for a case like that available?
function start-CTIlogMonitoring
{
Param ($CTIlogPath)
Write-Debug "startCTI monitor"
Add-JobTracker -Name "CTILogger" -ArgumentList $CTIlogPath `
-JobScript {
#--------------------------------------------------
#TODO: Set a script block
#Important: Do not access form controls from this script block.
Param ($CTIlogPath) #Pass any arguments using the ArgumentList parameter
while ($true)
{
$diff = ((Get-ChildItem $CTIlogPath).LastWriteTime - (get-date)).totalseconds
Write-Debug "diff $diff"
if ($diff -gt -2)
{
Write-Debug "cti log DIFF detected"
Get-Content -Path "$CTIlogPath" -Tail 4
Start-Sleep -Seconds 1
}
}
#--------------------------------------------------
}`
-CompletedScript { Param ($Job) }`
-UpdateScript {
Param ($Job)
$results = Receive-Job -Job $Job | Out-String # Out-String required to get new lines in RTB
#get the stuff from results and make it more appearing to read for humans
if ($results -match '(Ein, E, (\d+))')
{
Write-debug "Incoming Call:"
$time = ([regex]'[0-9]{2}:[0-9]{2}:[0-9]{2}').Match($results)
$phoneNumber = ([regex]'Ein, E, (\d+)').Split($results)[1]
Write-Debug "$time ----> $phoneNumber"
if ($richtextboxCTIlogs.lines.count -eq 0)
{
$richtextboxCTIlogs.AppendText("$time ----> $phoneNumber")
}
else
{
$richtextboxCTIlogs.AppendText("`n$time ----> $phoneNumber")
}
$richtextboxCTIlogs.SelectionStart = $richtextboxCTIlogs.TextLength;
$richtextboxCTIlogs.ScrollToCaret()
}
<#else
{
Write-Debug "found nothin"
}#>
}
}

Where Command not Working? [duplicate]

Very new to coding in general, so I fear I am missing something completely obvious. I want my program to check for a file. If it is there, just continue the code. If it has not arrived, continue cheking for a given amount of time, or untill the file shows up. My loop works on its own, so when i only select the do-part in Powershell ISE, it works. But when i try running it inside the if statement, nothing happens. The loops doesnt begin.
$exists= Test-Path $resultFile
$a = 1
if ($exists -eq "False")
{
do
{
$a++
log "Now `$a is $a "
start-sleep -s ($a)
$exists= Test-Path $resultFile
write-host "exists = $exists"
}
while (($a -le 5) -and ($exists -ne "True"))
}
Another way of doing this is using a while loop:
$VerbosePreference = 'Continue'
$file = 'S:\myFile.txt'
$maxRetries = 5; $retryCount = 0; $completed = $false
while (-not $completed) {
if (Test-Path -LiteralPath $file) {
Write-Verbose "File '$file' found"
$completed = $true
# Do actions with your file here
}
else {
if ($retryCount -ge $maxRetries) {
throw "Failed finding the file within '$maxRetries' retries"
} else {
Write-Verbose "File not found, retrying in 5 seconds."
Start-Sleep '5'
$retryCount++
}
}
}
Some tips:
Try to avoid Write-Host as it kills puppies and the pipeline (Don Jones). Better would be, if it's meant for viewing the script's progress, to use Write-Verbose.
Try to be consistent in spacing. The longer and more complex your scripts become, the more difficult it will be to read and understand them. Especially when others need to help you. For this reason, proper spacing helps all of us.
Try to use Tab completion in the PowerShell ISE. When you type start and press the TAB-key, it will automatically propose the options available. When you select what you want with the arrow down/up and press enter, it will nicely format the CmdLet to Start-Sleep.
The most important tip of all: keep exploring! The more you try and play with PowerShell, the better you'll get at it.
As pointed out in comments, your problem is that you're comparing a boolean value with the string "False":
$exists -eq "False"
In PowerShell, comparison operators evaluate arguments from left-to-right, and the type of the left-hand argument determines the type of comparison being made.
Since the left-hand argument ($exists) has the type [bool] (a boolean value, it can be $true or $false), PowerShell tries to convert the right-hand argument to a [bool] as well.
PowerShell interprets any non-empty string as $true, so the statement:
$exists -eq "False"
is equivalent to
$exists -eq $true
Which is probably not what you intended.

Powershell loop only if condition is true

Very new to coding in general, so I fear I am missing something completely obvious. I want my program to check for a file. If it is there, just continue the code. If it has not arrived, continue cheking for a given amount of time, or untill the file shows up. My loop works on its own, so when i only select the do-part in Powershell ISE, it works. But when i try running it inside the if statement, nothing happens. The loops doesnt begin.
$exists= Test-Path $resultFile
$a = 1
if ($exists -eq "False")
{
do
{
$a++
log "Now `$a is $a "
start-sleep -s ($a)
$exists= Test-Path $resultFile
write-host "exists = $exists"
}
while (($a -le 5) -and ($exists -ne "True"))
}
Another way of doing this is using a while loop:
$VerbosePreference = 'Continue'
$file = 'S:\myFile.txt'
$maxRetries = 5; $retryCount = 0; $completed = $false
while (-not $completed) {
if (Test-Path -LiteralPath $file) {
Write-Verbose "File '$file' found"
$completed = $true
# Do actions with your file here
}
else {
if ($retryCount -ge $maxRetries) {
throw "Failed finding the file within '$maxRetries' retries"
} else {
Write-Verbose "File not found, retrying in 5 seconds."
Start-Sleep '5'
$retryCount++
}
}
}
Some tips:
Try to avoid Write-Host as it kills puppies and the pipeline (Don Jones). Better would be, if it's meant for viewing the script's progress, to use Write-Verbose.
Try to be consistent in spacing. The longer and more complex your scripts become, the more difficult it will be to read and understand them. Especially when others need to help you. For this reason, proper spacing helps all of us.
Try to use Tab completion in the PowerShell ISE. When you type start and press the TAB-key, it will automatically propose the options available. When you select what you want with the arrow down/up and press enter, it will nicely format the CmdLet to Start-Sleep.
The most important tip of all: keep exploring! The more you try and play with PowerShell, the better you'll get at it.
As pointed out in comments, your problem is that you're comparing a boolean value with the string "False":
$exists -eq "False"
In PowerShell, comparison operators evaluate arguments from left-to-right, and the type of the left-hand argument determines the type of comparison being made.
Since the left-hand argument ($exists) has the type [bool] (a boolean value, it can be $true or $false), PowerShell tries to convert the right-hand argument to a [bool] as well.
PowerShell interprets any non-empty string as $true, so the statement:
$exists -eq "False"
is equivalent to
$exists -eq $true
Which is probably not what you intended.

Pause a Powershell script and resume

I have a script which currently pulls the file location from a CSV and uploads the files to a database, using a ForEach-Object loop.
What I'd like it to do is upload 1000 files, then be able to pause the loop and resume it later from file 1001.
I don't want to use the Start-Sleep command, as I do not want the script to automatically resume after a set amount of time.
This is a one time deal, so I'd rather not convert it to a workflow.
What command or cmdlet can be used to accomplish this?
The Read-Host command would be perfect if there were a way to break the script and then resume from the same line later.
Here's how I'd do it:
$i = 0;
foreach ($file in (Get-ChildItem $path_to_directory)) {
# Code to upload the file referenced by $file
if (++$i -eq 1000) {
Write-Host -NoNewLine '1000 files have been uploaded. Press capital "C" to continue uploading the remaining files...'
do {
} until (($Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyUp').Character) -ceq 'C')
}
}
Using pause, as already suggested in Bluecakes' answer, is a perfectly good solution. The advantage of this method is that it gives you more control. pause always requires the Enter key and always gives you the same prompt, Press Enter to continue...:, whereas this way you can define both to your liking.
More significantly, and the reason I personally prefer to do it this way in my own scripts, is that you can protect against accidental key presses. In my example I made the required keystroke a capital C, so that it's very unlikely that you'd continue by accident.
However, if you don't care about any of that and just want a quick and simple say to do it, then pause is really all you need.
Use pause:
For ($i=1; $i -lt 2000; $i++) {
if ($i -eq 1001)
{
pause
}
Write-Host $i
}
Something along these lines could work for you...
For ($i=1; $i -lt 50; $i++) {
if ($i -eq 10)
{
$input = Read-Host "Should I continue Y/N"
if ($input -eq "N")
{
break
}
}
Write-Host $i
}