-
Notifications
You must be signed in to change notification settings - Fork 7.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Start-Process -Wait behaves inconsistently vs Wait-Process when the new process launches children then exits before the children #15555
Comments
From where does $ & {
>> Start-Process -FilePath cmd -ArgumentList " /c timeout 5"
>> Get-Process | ? CommandLine -eq "cmd /c timeout 5" | Wait-Process
>> }
$ & {
>> Start-Process -Verb Runas -FilePath cmd -ArgumentList " /c timeout 5"
>> Get-Process | ? CommandLine -eq "cmd /c timeout 5" | Wait-Process
>> }
$ h -Count 2
Id Duration CommandLine
-- -------- -----------
65 5.628 & {…
66 6.044 & {… |
In curious what are you expecting to come out of here. You cannot change Wait-Process to use the grandchild wait and -Wait on Start-Process has been the behaviour since the beginning. Either change will be a breaking change so is it just a docfix you are wanting? |
Potentially Wait-Process could be augmented to add the detection for child processes as an opt-in as well. I do think the asymmetry here is concerning, and doesn't make a lot of sense though; folks will use Start-Process -Wait and then be very confused when they try to do similar with Wait-Process for processes they haven't directly started themselves, and something may break because they behave differently. |
I'm not sure if doing it with
I feel that there are enough edge cases here to consider keeping the behaviour as is. The alternative is to have |
So I just tested this assumption and it is correct. If you were to add a process to a job then any child processes it has already spawned will not be included in the job and thus will not be waited on. Only subsequent processes that it spawns after it was added to the job is. A reproducer for this is Add-Type -TypeDefinition @'
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
public class NativeMethods
{
[DllImport("Kernel32.dll", EntryPoint = "AssignProcessToJobObject", SetLastError = true)]
private static extern bool NativeAssignProcessToJobObject(
SafeHandle hJob,
SafeHandle hProcess
);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "CreateJobObjectW", SetLastError = true)]
private static extern SafeFileHandle NativeCreateJobObjectW(
IntPtr lpJobAttributes,
string lpName
);
public static void AssignProcessToJobObject(SafeHandle job, SafeHandle process)
{
if (!NativeAssignProcessToJobObject(job, process))
throw new Win32Exception();
}
public static SafeHandle CreateJobObjectW(string name)
{
SafeHandle job = NativeCreateJobObjectW(IntPtr.Zero, name);
if (job.IsInvalid)
throw new Win32Exception();
return job;
}
}
'@
$job = [NativeMethods]::CreateJobObjectW('MyJob')
$parentProc = Start-Process powershell.exe -PassThru
# In the new process start a new powershell process again
[NativeMethods]::AssignProcessToJobObject($job, $parentProc.SafeHandle)
# In the new process start another powershell process again
# Use your favour process explorer tool to see the job setup and close processes once you are done
$job.Dispose() Using |
Is it possible to enumerate the current child processes in a different fashion, then? |
You can enumerate all processes and get the parent PID (pwsh has the logic for this luckily) and then assign them to the job yourself. There are 2 problems with this though
|
Those are good points. Yeah, it makes sense that any effort to determine the process hierarchy will pretty much only be able to be a best-effort approach. I think that's an OK approach to take in order to reduce the disparity between Start-Process -Wait and Wait-Process, but we'd still need to document that it's not going to be flawless at detecting child processes. |
One thing I should mention is that I currently rely on this behaviour and have come across people in the Discord either wanted to rely on only the specific process ( |
I agree that IF the |
To start with, clearly documenting this behaviour would be a big help, especially if the docs suggest wrapping the child process in a PS job as a workaround per the above notes. I agree that a new switch definitely should be required if I was extremely confused by all this for some time, because I couldn't understand how A possible solution might be to return a wrapper or subclass of It'd be better if (I wrote some stuff about runas, trustlevel, etc here, but I'll actually raise a separate issue for it.) |
Agreed. Would you mind submitting an issue to the https://github.com/powershell/powershell-docs repo? They also accept community PR's if you're interested in writing up some text to document this behavior.
|
@jborean93 I'm reporting a defect and inconsistency. I don't have a strong opinion on the "right" fix. To understand the full picture in which this fits, take a look at the attached horror script I concocted to run a child process without admin privileges, stream its stdout to the current window, and kill the process tree if the powershell script is terminated. File ext is A doc fix is definitely needed. But beyond that, when it comes to behaviour I see a few options.
Essentially I want consistency and predictability of behaviour, and the behaviour clearly reflected in the docs.
Or in my case, a process I did start myself, as a lower trustlevel, where I wanted to read and echo its output while it was running. You'd think that'd be easy, but you'd be wrong. @rkeithhill Yeah, I'll submit an issue. I've spent a couple of days on this already so I can't presently update the docs, but I will try to get to it once the work delayed by working around this is done. |
I opened #15562 to suggest a way for |
Filed as linked above: MicrosoftDocs/PowerShell-Docs#7700 I also wrote a separate docs issue for the confusing behaviour of
|
Re |
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you. |
2 similar comments
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you. |
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you. |
This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes. |
This issue was encountered when using
runas.exe
to launch a task, but applies to any situation whereStart-Process
launches a child process that terminates before its own children do.Start-Process -Wait
waits until the new process and all its children exit. By contrast,Wait-Process
waits only until the process specified terminates, with no concern for its children. This asymmetry appears to be undocumented, and it's rather unintuitive.PSVersion 7.1.3 but observed in various back-versions too.
The difference is because
Start-Process -Wait
has a special-case behaviour that tracks child processes and tracks them as a powershell job - see theProcessCollection
class for details.Steps to reproduce
Compare:
with
Expected behavior
Intuitively, one would expect that
Start-Process -Wait
and$proc = Start-Process | Wait-Process
would have the same effect. So eitherStart-Process -Wait
would exit as soon as the immediate child process exits, orWait-Process
would wait until the process and all its children terminate.But this is not the case.
Actual behavior
As shown above,
Start-Process -Wait
will wait until the whole process tree exits, wherasWait-Process
exits as soon as the process that was launched exits.Background
I originally encountered this when invoking a command under
runas.exe
to launch it in an unprivileged session.runas.exe
exits as soon as it has started the unprivileged child, so it doesn't forward the child process exit code or channel stdio between child and parent processesI found that
waited for
thecommand.exe
to terminate - as expected - butstopped waiting immediately. Attempted workarounds like waiting for
$proc.WaitForExitAsync()
or polling$proc.HasExited
didn't help since they all reflect the state of the process, not process tree.The underlying problem is really that
runas.exe
behaves likecmd.exe
'sstart
command, not likestart /wait
.Workaround
After reading the Start-Process cmdlet's source to work out why the behaviour differs, I was able to work around this by launching the child process in a powershell job:
tracked the whole process graph, so it exited only when the child exited.
Environment data
P.S.
Please document that
Start-Process -ArgumentList
simply concatenates the arguments with space separators and absolutely no concern for quoting or otherwise grouping up arguments to preserve the argument vector - it behaves like win32'sspawnv
not unix'sexecv
.The text was updated successfully, but these errors were encountered: