ngoozeff
ngoozeff

Reputation: 4746

How can I start a background job in Powershell that outlives it's parent?

Consider the following code:

start-job -scriptblock { sleep 10; cmd /c set > c:\env.txt; }
exit

The background job is killed when the parent exits, so the call to cmd.exe never occurs. I would like to write some similar code, such that the parent exits immediately, and the child continues to run in the background.

I would like to keep everything in one script if possible.

Upvotes: 34

Views: 18669

Answers (5)

silico-biomancer
silico-biomancer

Reputation: 300

The answer by niels is out of date for Powershell 7, since InvokeWMIMethod is deprecated since powershell 6 and unavailable in 7. This alternative worked for me, creating a separate powershell and allowing the original window to close, as if the new job was disowned/detached.

Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{
CommandLine = "powershell.exe -noprofile -ExecutionPolicy Bypass invoke-command -scriptblock {
 sleep 10; echo 'hi'; cmd /c set > c:\env.txt;
}" }

Or for a script:

Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{
    CommandLine = "powershell.exe -noexit -noprofile -ExecutionPolicy Bypass & 'C:\script.ps1'"
}

Upvotes: 1

Niels
Niels

Reputation: 1075

If you use Start-Process, you create a new child process that has your powershell session as its parent process. If you kill the powershell parent process that starts this, the new process will be orphaned and keep running. It will, however, not survive if you kill the parent's process tree

Start-Process -FilePath notepad.exe

Powershell cannot start a new independent process outside of its process tree for you. However, this can be done in Windows using CreateProcess and this functionality is exposed through WMI. Luckily, you can call that from powershell:

Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList notepad.exe

This way, the new process will also keep running if the process tree is killed, because the new process does not have your powershell session as a parent, but the WMI host process.

Upvotes: 26

Syberdoor
Syberdoor

Reputation: 2619

If found that using Invoke-Command was able to bypass the restriction that the background job dies with it's parent. The nice thing is that the syntax is almost the same as with start-job, so the scriptblock can be kept as is.

start-job -scriptblock { sleep 10; cmd /c set > c:\env.txt; }

would just turn into

Invoke-Command -ComputerName . -AsJob -scriptblock { sleep 10; cmd /c set > c:\env.txt; }

and suddenly survive the death of it's parent (probably because invoke-command is fit to run programs on another computer so the parent can never matter to it)

Upvotes: 4

JPBlanc
JPBlanc

Reputation: 72610

You can also do it like that

$a = start-job -scriptblock { sleep 10; cmd /c set > c:\env.txt; }
Register-ObjectEvent -InputObject $a -EventName StateChanged -SourceIdentifier "finished"
$b = Wait-Event -SourceIdentifier "finished"
exit

Your script will wait for the end of the scriptblock to finish.

Upvotes: 0

manojlds
manojlds

Reputation: 301087

You will have to start a process:

start-process powershell -ArgumentList "sleep 10; cmd /c set > c:\env.txt" -WindowStyle hidden

Upvotes: 20

Related Questions