Reputation: 12670
I have a powershell script that runs some executable using call operator. Something like this:
$executable = 'c:\dev\test.exe'
& $executable | Out-Host
Write-Host 'done'
That test.exe
in turn runs another programs (server.exe
) and communicates with it over TCP/IP (this program requires administrative privileges and because of that my powershell script is run as admin), also it runs trivial batch script (which runs and terminates in 1 second). Once started, server.exe
runs until terminated by test.exe
. All programs involved are console applications. Another fact probably worth noting is that since server.exe
requires admin privileges it runs in separate console window than test.exe
.
Sometimes test.exe
does not terminate server.exe
(sometimes due to crash, sometimes due to bugs in it). When this happens, after test.exe
termination execution does not return to powershell script (I don't see 'done' being printed). If I terminate server.exe
manually, I immediately see 'done' in the powershell console. Why is this happening? Why powershell waits for process to terminate which it had not started and which does not even run in the same console?
In my investigation attempts I tried to reproduce the issue with less amount of code involved I created test2.exe
which simply runs server.exe
(exactly the same way, I have access to test.exe
sources) and terminates. Now when I run the same powershell script but with test2.exe
as $executable
, I see exactly what I expected to see initially: server.exe
starts in separate window, then 'done' is printed in powershell window (and powershell script terminates) while server.exe
still runs.
I watched Process Explorer while test.exe
run and I am sure no other process besides server.exe
are running after test.exe
termination.
I failed to find any in-depth explanation what &
operator does and what it waits for, maybe you have some ideas?
Update: Instead of running with call operator I tried Start-Process
:
$executable = 'c:\dev\test.exe'
Start-Process $executable -Wait
Write-Host 'done'
Now test.exe
runs in it's own console, terminates and powershell still waits for server.exe
to finish.
Also I tried to run the batch script:
c:\dev\test.exe
echo done
and it works fine: as soon as test.exe
terminates 'done' is printed to console and batch file stops (server.exe
is still running).
Another update: changed script to do the following:
$executable = 'c:\dev\test.exe'
$proc = Start-Process $executable -PassThru
do {
Get-Process -Id $proc.Id
Start-Sleep -Seconds 10
} while ($true)
I am getting the regular Get-Process
output few times while test.exe
is running then (after test.exe
terminates) it starts producing "Cannot find process with the process identifier 3028" errors. So I probably can go this way.
Another thing I tried was:
$env:PATH += ';c:\dev\;'
test.exe
Write-Host 'done'
Still, no luck: test.exe
terminates, powershell still waits for server.exe
to terminate.
Tried the job approach, too:
$job = Start-Job {& 'c:\dev\test.exe'}
do
{
$job
Start-Sleep -Seconds 10
} while ($job.JobStateInfo.State -eq [System.Management.Automation.JobState]::Running)
Receive-Job $job
It never quits unless I terminate server.exe
.
Upvotes: 5
Views: 4046
Reputation: 201992
The call operator &
simply attempts invoke the named command (application, script, function, cmdlet) following it, passing any additional arguments to the command. In addition, &
executes the command in a new scope whereas the .
executes the command in the current scope. When executing an application (exe) I don't think it matters that you execute in a new scope. The exe can't impact scoped variables in PowerShell.
WRT the test.exe behavior, PowerShell won't return control to the script until a console exe finishes. Test.exe and test2.exe are both console subsystem exes right? PowerShell behaves differently with Windows subsystem exes. It launches them and returns immediately unless you use a trick like this notepad.exe | Out-Null
.
Upvotes: 6