Run executable in powershell without waiting for return - powershell

This is really basic, but I can't find the answer. The installer sets up my path so that I can just type the command:
ng serve
at the command prompt and the script runs. I don't want to wait for this program to finish (it's a server, after all). How do I launch the same script (it's a CMD script as far as I can tell) from Powershell without waiting for it to finish (and without having to find the source directory for the script)?

If it's acceptable to terminate the server when the PowerShell session exits, use a background job:
In PowerShell (Core) 7+
ng server &
In Windows PowerShell, explicit use of Start-Job is required:
Start-Job { ng server }
Both commands return a job-information object, which you can either save in a variable ($jb = ...) or discard ($null = ...)
If the server process produces output you'd like to monitor, you can use the Receive-Job cmdlet.
See the conceptual about_Jobs topic for more information.
If the server must continue to run even after the launching PowerShell session exits, use the Start-Process cmdlet, which on Windows launches an independent process in a new console window (by default); use the -WindowStyle parameter to control the visibility / state of that window:
Start-Process ng server # short for: Start-Process -FilePath ng -ArgumentList server
Note: On Unix-like platforms, where Start-Process doesn't support creating independent new terminal windows, you must additionally use nohup - see this answer.

Related

Powershell execute commands on newly started Command Prompt process

Trying to start a new process in a separate command prompt on Windows 10, but can't find how to execute commands in opened prompt. With Powershell, I could use -Command:
Start-Process PowerShell "-Command tasklist"
But how to do that in Command Prompt window? This, obviously, doesnt work:
Start-Process cmd '-Command tasklist'
You're using the PowerShell arguments for cmd.exe. cmd /? will give you the usage, but what you want is cmd /c COMMAND [ARGUMENTS]:
Start-Process cmd "/c ping -n 4 google.com"
#Jeff Zeitlin was kind enough to provide a link to the SS64 CMD usage.
Worth mentioning, you don't need to use Start-Process when running external commands unless:
You are running a GUI application and wait to use the -Wait parameter to wait until the program exits
You want to run something in a different process asynchronously and check the result later
In this case don't forget to use -PassThru and assign the process to a variable, so you can check the result when ready

Powershell closes on executing exe

I am trying to run and .exe in C:programfiles(x86)
I can launch it directly from the filepath. If I run in powershell just closes. No feedback.
Running powershell 5.1.17134 Rev 590
Start-Process -FilePath "C:\Program Files (x86)\App\App.exe"
I tried running powershell -NoExit and then start-process but it returns without any feedback.
If I run it on same machine in Powershell 6.1.0 preview - it runs fine. No issue. How can I track down whats causing this to 1) not run 2)close powershell.
Thanks,
This answer makes a general point. It doesn't solve your problem, if using Start-Process truly crashes the calling PowerShell window.
If C:\Program Files (x86)\App\App.exe is a console application that you want to run in the current console window, do not use Start-Process to invoke it.
Instead, invoke it directly.
Given that its path contains spaces and special characters (( and )), you need to:
quote it
invoke it with &, PowerShell's call operator.
PS> & 'C:\Program Files (x86)\App\App.exe'
Note:
The use of & with a command specified in a quoted string and/or via a variable reference is a syntactic necessity, given that PowerShell has two distinct parsing modes - see about_Parsing.
Invoking a console application directly:
ensures its synchronous execution
That is, control isn't returned to the caller until the application has terminated.
connects its standard output streams - stdout and stderr - to the equivalent PowerShell streams.
That is, you can capture or redirect the application's success output and/or error output, as needed.
You have an interactive shell. You spawn this new process - then your shell closes?
Clearly it is terminating its parent process, and clearly pwsh is doing something different.
I don't think this is truly a powershell question, it's a windows internals one. The suite of tools to use is Sysinternals. The first thing I'd try - and I'd do this on cmd, powershell and pwsh to establish a basis for comparison - is run Process Monitor with a filter on your app's path. Something in its last actions may prove illuminating. Process Explorer may also be useful.
Are you in a corporate environment? I have agents on my machine that kill processes based on heuristics. That can do things like this.
There may be a workaround based on how you invoke the app;
try mklement0's suggestion
try invoking through WMI; this does not provide your powershell process as a parent process: Invoke-WmiMethod -Class win32_process -Name create -ArgumentList "PathToApp.exe"
try invoking via cmd.exe if you are constrained by what's on your target machines
I do think this is off-topic, though.

-s when using Start-Job in powershell

I am trying to call a Start-Job in powershell. When I do, it spawns a background powershell with the following arguments:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Version 5.0 -s -NoLogo -NoProfile -EncodedCommand [encoded command I want to run in base64]
However, the powershell command never seems to complete, whatever the command I send it.
I tried spawning a powershell instance like this:
powershell.exe -s
And this also seems to create an instance that seems frozen, not executing or doing anything. Looking online, I cannot seem to find any reference to the -s argument.
Does anybody know what it's for or how to get rid of it so that my start-jobs work properly?
Edit: It's possible that -s is the shorthand for -sta, but my command does not freeze using -sta, but it does using -s.
Edit2: I have since found out that -s is a shorthand for -ServerMode, apparently a Legacy Powershell 2.0 option. I have no idea why that is added when using Start-Job.
Edit3: The command I use is:
$deploymentsJobs += Start-Job -InitializationScript { SomeSmallFunction } (AnotherFunction) -ArgumentList $arg1, $arg2, $arg3}
tl;dr:
The -s option is an expected part of the command line used to launch a background job via a new PowerShell process - it puts the new process in server mode, which is required to communicate with the calling process for background job management.
It is not a legacy option, but it isn't documented either, because it is only meant to be used internally by PowerShell.
Given that everything you describe is as expected, the problem is likely with the specific commands you run via -InitializationScript and in the main script block (the implied -ScriptBlock argument).
As you've discovered, a Start-Job call spawns a powershell -s -NoLogo -NoProfile call behind the scenes (discoverable via Task Manager).
That is, a new PowerShell process is created to run the commands in the background.
An -EncodedCommand parameter with a Base64-encoded command string is only present if you invoked Start-Process with the -Initialization parameter - the main script block (the (implied) -ScriptBlock argument) is not passed via the command line (see below).
-s is used PowerShell-internally - always - to invoke background jobs, and -s, as you've also discovered, is an alias for the -servermode switch. (Given that only -STA is documented, one would expect -s to be short for -STA, but it is not).
-s / -servermode is an implementation detail, used only by PowerShell itself, which is why it isn't documented.
This location in the PowerShell Core source code on GitHub shows you how the command line for the background process is constructed.
Server mode is the mode the background process must be in in order to communicate with the calling process via its standard streams (stdin, stdout, stderr): That is, the command to execute in the background is sent to the background process through its stdin stream, and the background process reports its output via its stdout and stderr streams.[1]
Note that XML-based serialization / deserialization happens during this inter-process communication, using the same infrastructure as PowerShell remoting - see this answer for more information.
[1] Ohad Schneider points out that it is possible to accidentally disrupt this communication if the main script block contains commands such as Start-Process -NoNewWindow with a console program that directly write to the background process' stdout stream - see this answer.

Powershell waits on cmd.exe differently depending on environment

Consider the powershell command:
cmd.exe "/c start notepad.exe"
Using powershell.exe (console) this command completes immediately after starting the cmd/notepad process and does not wait for notepad/cmd to exit. If I want it to wait for completion I have to pipe the output to Out-Null.
Using the powershell ISE this command blocks execution until notepad/cmd is closed.
Further, if I use create a new process (of powershell.exe) in a script running in powershell.exe using System.Diagnostics.Process and redirect standard output the script now blocks execution until notepad/cmd is closed. If I don't redirect output it does not block execution.
But if I use c# to create a new process with the same settings/start info, and run the same script with redirected output it doesn't block execution.
I'm at a loss here. Obviously it has something to do with the setup of the execution and output and maybe "cmd.exe". I'm hoping someone understands what's fundamentally happening behind the scenes differently in these cases. I know the ISE and cmd.exe isn't fully supported but the I don't know why the other 3 aren't consistent.
Why do the scripts not run with the same behavior (especially the powershell console ones) and how do I get them to?
Edit:
After some troubleshooting I was able to get all the powershell.exe versions to run the same (the ISE isn't of importance to me). The odd ball where cmd.exe "/c start notepad.exe" blocks execution is when a new process is created in powershell to run powershell.exe using System.Diagnostics.Process. If output is redirected (the reason I was using System.Diagnostics.Process in the first place, Start-Process doesn't support redirecting except to a file) then calling WaitForExit() on the process object causes the blocking to occur.
Simply substituting WaitForExit() with Wait-Process (and the process ID) causes the powershell script running in the new powershell.exe to execute as expected and exit after running the command. Hopefully this information combined with #mklement0 answer will be sufficient for anyone else having similar problems.
To get predictable behavior in both the regular console and in the ISE:
Start a GUI application asynchronously (return to the prompt right away / continue executing the script):
notepad.exe
Invoking Notepad directly makes it run asynchronously, because it is a GUI-subsystem application.
If you still want to track the process and later check whether it is still running and what its exit code was on termination, use -PassThru, which makes Start-Process return a [System.Diagnostic.Process] instance:
$process = Start-Process -PassThru notepad.exe
$process.HasExited later tells you whether the process is still running.
Once it has exited, $process.ExitCode tells you the exit code (which may not tell you much in the case of a GUI application).
To wait synchronously (at some point):
Use Wait-Process $process.ID to wait (indefinitely) for the process to terminate.
Add a -Timeout value in seconds to limit the waiting period; a non-terminating error is reported if the process doesn't terminate within the timeout period, causing $? to reflect $False.
Start a GUI application synchronously (block until the application terminates):
Start-Process -Wait notepad.exe
-Wait tells Start-Process to wait until the process created terminates; this is the equivalent of cmd /c 'start /wait notepad.exe'.
There's a non-obvious shortcut to Start-Process -Wait: you can repurpose the Out-Null cmdlet to take advantage of the fact that piping invocation of a program to it makes Out-Null to wait for the program's termination (a GUI application has no stdout or stderr output, so there's nothing for Out-Null to discard; the only effect is synchronous invocation):
notepad.exe | Out-Null
In fact, this approach has two advantages:
If arguments must be passed to the GUI application, they can be passed directly, as usual - rather than indirectly, via Start-Process's -ArgumentList parameter.
In the (rare) event that a GUI application reports a meaningful process exit code (e.g, msiexec.exe), the Out-Null approach (unlike Start-Process -Wait) causes it to be reflected in the automatic $LASTEXITCODE variable.
Note: In rare cases, a GUI application may explicitly attach to the caller's console and write information to it; in order to surface that, pipe to | Write-Output instead (you'll still be able to evaluate $LASTEXITCODE) - see this answer.
Note that for console-subsystem applications (e.g., findstr.exe), synchronous execution is the default; Start-Process is only needed for GUI applications (and for special situations, such as wanting to run an application in a new console window or with elevation (run as admin)).
To run a console application or shell command asynchronously (without opening a new console window), you have the following options:
[Preferred] Use Start-Job kick off the command, and Receive-Job to receive its output / success status later.
$j = Start-Job { sleep 2; 'hi' }
To synchronously wait for this job to finish (and remove it automatically), use
Receive-Job -Wait -AutoRemoveJob $j
In PowerShell (Core) 6+:
You can use the simpler ... & syntax (as in
POSIX-like Unix shells such as bash) in lieu of Start-Job; e.g.:
$j = & { sleep 2; 'hi!' } &
Better yet, you can use the lighter-weight, faster Start-ThreadJob cmdlet, which uses threads for concurrency, but otherwise seamlessly integrates with the other *-Job cmdlets (note that it has no short-hand syntax):
$j = Start-ThreadJob { sleep 2; 'hi' }
[Not advisable] You can use something like Start-Process -NoNewWindow powershell -Args ..., but it is ill-advised:
Passing a shell command to powershell via Start-Process requires intricate quoting based on obscure rules - see this answer of mine for background information.
Any stdout and stderr output produced by the application / shell command will by default arrive asynchronously in the current console, interspersed with what you're doing interactively.
While you can redirect these streams to files with RedirectStandardOutput and -RedirectStandardError (and stdin input via -RedirectStandardInput) and you can use -PassThru to obtain a process-information object to determine the status and exit code of the process, Start-Job and Receive-Job are a simpler, more convenient alternative.
P.S.: I don't have an explanation for why cmd.exe "/c start notepad.exe" is blocking in the ISE but not in the regular console. Given the solutions above, however, getting to the bottom of this discrepancy may not be needed.

How to continue executing a Powershell script after starting a web server?

I have a script which calls two other scripts.
script0.ps1
Invoke-Expression C:\script1.ps1
Invoke-Expression C:\script2.ps1
The first script starts a web server:
script1.ps1
./activate anaconda_env
cd C:\webserver
python api_server.py
The second script starts a ngrok service:
script2.ps1
./activate anaconda_env
cd c:\ngrok
./ngrok -subdomain=mysd 8000
The problem is that the script0.ps1 only executes script1.ps1. At this point the web server starts running in the console and so the second command of script0.ps1 is not executed.
How to make write the scripts so both commands are executed? Or, how to write just one script to execute all commands but in two separate consoles?
The final result should be:
1) a web server running in a console with activated anaconda environment
2) a ngrok service running in a console with with activated anaconda environment
Change Script1.ps1 to launch python as a job:
./activate anaconda_env
cd C:\webserver
Invoke-Command -ScriptBlock {.\python.exe api_server.py} -AsJob -ComputerName .
I don't have the specific script you're using, so I tested this with turtle.py which ships with 3.43 and it seems to work.
You don't need to use Invoke-Expression to run a Powershell script from another script. Just run it as if you're on the command line
c:\script1.ps1
c:\script2.ps1
Now if script1.ps1 starts a process that doesn't exit, it will halt execution for the next statements in the script, and thus also prevent the second script from running.
In most cases this sequential execution is exactly what you want.
In your case you can start the scripts asynchronously by using Start-Process.
So your main script becomes something like:
start-process c:\script1.ps1
start-process c:\script2.ps1
Start-Process basically starts a new command shell to run the statement in. Check out the docs for more info. There's a bunch of parameters you can use to tweak how this happens.
To not have invoke-expression close your script you can pipe the output to Out-Null. Your code above would look like:
Invoke-Expression C:\script1.ps1 | Out-Null
Invoke-Expression C:\script2.ps1 | Out-Null