1

Is it possible to force the execution of some code if a PowerShell script is forcefully terminated? I have tried try..finally and Traps, but they both don't seem to work, at least when I press Ctrl-C from PowerShell ISE.

Basically, I have a Jenkins build that executes a PowerShell script. If for any reason I want to stop the build from within Jenkins, I don't want any subprocess to lock the files, hence keeping my build project in a broken state until an admin manually kill the offending processes (nunit-agent.exe in my case). So I want to be able to force the execution of a code that terminates nunit-agent.exe if this happens.

UPDATE: As @Frode suggested below, I tried to use try..finally:

$sleep = {
  try {
    Write-Output "In the try block of the job."
    Start-Sleep -Seconds 10
  }
  finally {
    Write-Output "In the finally block of the job."
  }
}

try {
  $sleepJob = Start-Job -ScriptBlock $sleep
  Start-Sleep -Seconds 5
}
finally {
  Write-Output "In the finaly block of the script."
  Stop-Job $sleepJob
  Write-Output "Receiving the output from the job:"
  $content = Receive-Job $sleepJob
  Write-Output $content
}

Then when I executed this and broke the process using Ctrl-C, I got no output. I thought that what I should got is:

In the finally block of the script.
Receiving the output from the job:
In the try block of the job.
In the finally block of the job.

1 Answer 1

4

I use try {} finally {} for this. The finally-block runs when try is done or if you use ctrl+c, so you need to either run commands that are safe to run either way, ex. it doesn't matter if you kill a process that's already dead..

Or you could add a test to see if the last command was a success using $?, ex:

try {
    Write-Host "Working"
    Start-Sleep -Seconds 100

} finally {
    if(-not $?) { Write-Host "Cleanup on aisle 5" }
    Write-Host "Done"
}

Or create your own test (just in case the last command in try failed for some reason):

try {
    $IsDone = $false
    Write-Host "Working"
    Start-Sleep -Seconds 100
    #.....
    $IsDone = $true

} finally {
    if(-not $IsDone) { Write-Host "Cleanup on aisle 5" }
    Write-Host "Done"
}

UPDATE: The finally block will not work for output as the pipeline is stopped on CTRL+C.

Note that pressing CTRL+C stops the pipeline. Objects that are sent to the pipeline will not be displayed as output. Therefore, if you include a statement to be displayed, such as "Finally block has run", it will not be displayed after you press CTRL+C, even if the Finally block ran.

Source: about_Try_Catch_Finally

However, if you save the output from Receive-Job to a global variable like $global:content = Receive-Job $sleepJob you can read it after the finally-block. The variable is normally created in a different local scope and lost after the finally-block.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks @Frode. I tried it but it didn't work. See my edit above.
Oh, I see, that makes sense now, and also makes me happy :) So what is the recommended way of communicating with the main process? Basically, from the sub process, I want to say something like build was stopped, hence we are terminating the NUnit tests, so we could tell from Jenkins server what has happened.
Don't see too many options atm. I would write to a log file or use exit-codes. Ex. let your finally block end with exit 1234.
Thanks. Yeah, I was thinking of using log files and dump it from the main script.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.