Start multiple programs using Powershell without leaving the window open afterward - powershell

I want to load a series of programs and then have the PowerShell instance close afterward. This is what I have:
Start-Process -FilePath <path to chrome>
Start-Process -FilePath <path to firefox>
Start-Process -FilePath <path to vs code>
So basically, I want to run it like a batch file where it launches the listed programs and then goes away. But instead, it leaves the powershell window open and if I manually close it, VScode shuts too (the browsers stay open for some reason).
I see there's a -wait option for start-process, but I want the opposite: -nowait. Just open and go away. How can I do this? The other commands like invoke and startjob don't seem right.

Since these are GUI applications, you don't need to worry about usingStart-Process -Wait:$False; GUI applications don't block in PowerShell (or Command Prompt) by default. You can simply run those programs with the call-operator & (technically optional to use the call-operator but it covers the path-with-spaces case):
& "\path\to\guiprogram.exe"
If you need to do this with a non-GUI application, then you would need to useStart-Process -Wait:$False (or omit -Wait altogether since Start-Process doesn't block by default) to continue executing:
Start-Process -Wait:$False "\path\to\program.exe"
Start-Process "\path\to\program.exe"
-SwitchParameter:$Value syntax allows you to set the explicit value of [switch] parameters. Usually the switch parameter is implicitly managed. It is in an "on (true)" state if provided, and an "off (false)" state when omitted.
However, you can explicitly set the value of a switch parameter by suffixing the parameter with :$Value. The value's truthiness will be evaluated and the switch state will match "on" for $True and "off" for $False.

Related

is there a wait parameter in PowerShell to allow the script to wait until a command is completed

I trying to install a software using Start-Process in PowerShell, I would like for the script to wait until a command is completed before proceeding to the next one. I'm not experienced I tired the script below but it did not work.
Start-Process -Wait -FilePath "C:\Temp\Latitude_5X10_Precision_3550_1.15.0.exe" -ArgumentList "/S" -PassThru
Your Start-Process call is correct, but -Wait invariably only tracks the lifetime of the directly launched process (C:\Temp\Latitude_5X10_Precision_3550_1.15.0.exe in your case).
That is, you're out of luck if the target process itself spawns yet another process in order to perform its task and then returns before that child process has terminated.
Additional work is then needed, if even feasible:
If you know the name of the child process, you can try to find and track it via Get-Process.
Alternatively, if you know of an indirect sign that the task has completed, such as the existence of a directory or a registry entry, look for that.
As an aside: console(-subsystem) applications can be invoked directly for synchronous (blocking) execution (e.g., foo.exe bar baz or & $fooExePath bar baz), which is the preferred method, because it connects the application's output streams to PowerShell's streams.

PowerShell Start-Process with -ArgumentList doesn't work

A very easy line in a powershell script
Start-Process -FilePath displayswitch -ArgumentList "/extend"
It is supposed to extend my screen. Yet it doesn't work because there is something wrong with the ArgumentList. What is it?
In general, do not use Start-Process to invoke CLIs (command-line interfaces; i.e., programs that accept command-line arguments), unless you explicitly want to launch them asynchronously, in a new window - see this answer for background information.
In the case at hand - for reasons unknown to me[1] - Start-Process additionally seems to prevent the displayswitch.exe executable from functioning properly when arguments are passed (the syntax of your Start-Process command is correct).
However, that is a moot point if you invoke displayswitch.exe directly, which is generally the right choice for CLIs:[2]
displayswitch.exe /extend
[1] The problem may be related to the fact that displayswitch.exe is a GUI-subsystem rather than a console-subsystem application. That is, it doesn't allocate a console window for itself and operates asynchronously when called from an existing console window. Using Start-Process therefore makes the executable run without a console window. That said, running displayswitch.exe /extend from the Windows Run dialog (WinKey-R), where no console window is involved either, works correctly.
[2] Given that displayswitch.exe is a GUI-subsystem application, it will act asynchronously even in direct invocation, but I assume that's not a problem here. If your Start-Process command had worked, it would have been asynchronous too. Adding -Wait to a Start-Process call makes it synchronous, and you can emulate this behavior in direct invocation by piping to Out-Null. However, due to how displayswitch.exe is implemented, even displayswitch.exe /extend | Out-Null doesn't result in fully synchronous operation.

Powershell not running on cmd

I'm trying to run the following command but when I do it the CMD windows closes without the command being executed
the command is
cmd.exe /c powershell.exe (New-Object System.Net.WebClient).DownloadFile(https://site,'%temp%\file.txt');Start-Process '%temp%\file.txt'
You don't need cmd here at all¹. You can spawn a process without a shell just as well. Furthermore, it's usually a good idea to quote arguments to avoid surprises with argument parsing (cmd for example has its own meaning for parentheses, which may well interfere here).
powershell -Command "(New-Object System.Net.WebClient).DownloadFile('https://site',$Env:Temp + '\file.txt');Invoke-Item $Env:Temp\file.txt"
I've also added quotes around the URL you want to download, since that wouldn't otherwise work, either. And since cmd is no longer around, environment variables can be expanded by PowerShell as well, with a different syntax.
Start-Process also is for starting processes and Invoke-Item is closer to what you actually want, although I'm sure with ShellExecute behavior, Start-Process could launch Notepad with a text file as well if desired.
¹ If in doubt, it's always a good idea to reduce the number of parts, processes and different wrapped concepts needed. Same reason why you don't use Invoke-Expression in PowerShell to run other programs: Unnecessary, and complicates everything just further by introducing another layer of parsing and interpretation.

Open a powershell window from an existing powershell window, and keep it open after running code

I can open a powershell window from an existing one and provide it code to run here
But I can't get the second window to stay open once the code has run
Here's what I want to run in the first powershell window, and I would like the powershell window that opens to stay open after running the code (currently, it closes immediately)
start powershell { ECHO "hi" }
Note
I tried some suggestions here but not having any luck
Also, I got a fix (of sorts) using something like start powershell { ECHO "hi"; TIMEOUT 20 } but that's not going to keep the window permanently open
PowerShell -Command {Write-Host "Test" } -NoExit
from about Powershell. To start the Powershell in a new window you can use:
start-process powershell.exe -ArgumentList "-noExit", "-command","Write-host 'TEST'; Write-Host 'Test2'"
Important -Command must be the last parameter (about Powershell):
When the value of Command is a string, Command must be the last parameter specified because any characters typed after the command are interpreted as the command arguments.
Generally:
If you pass a command (-Command) or script file (-File) to execute to PowerShell's CLI (powershell.exe in Windows PowerShell, pwsh.exe in PowerShell [Core] v6+), PowerShell by default executes the command / script and then exits.
With a command or script specified for execution, you need to add the -NoExit switch if you want the new session to remain open.
Caveat: (Unless you call directly from within PowerShell with a script block), a positional argument - i.e., one neither preceded by -Command nor -File - is implicitly bound to:
-Command in Windows PowerShell
-File in PowerShell [Core] v6+.
Therefore, it's advisable to use the target parameter name explicitly.
With Start-Process (whose built-in alias on Windows - but not on Unix - is start):
Note: The primary use of Start-Process is to launch an independent process asynchronously in a new window. However, the latter isn't supported on Unix-like platforms, where Start-Process's utility is therefore limited.
start powershell { ECHO "hi" } happens to work from PowerShell (except that the window closes right after executing the command), but it's important to note that you cannot actually pass script blocks to Start-Process, only strings.
Start-Process accepts an executable name (implied -FilePath parameter), and an array of string arguments (implied -ArgumentList / -Args parameter).
If you pass a script block ({ ... }), it is automatically stringified, meaning that its literal string contents are used (stripped of the { and }) as the (only) -ArgumentList string value.
Thus, bypassing the unnecessary script-block creation, your command - with -NoExit applied as desired - should be (with explicitly named parameters; note that -Command and its argument must come last):
Start-Process -FilePath powershell -ArgumentList '-NoExit -Command ECHO "hi"'
Note:
While passing arguments individually, as an array to -ArgumentList is arguably conceptually cleaner, it is actually better to pass all arguments as a single string, using embedded quoting as necessary, due to a longstanding bug - see GitHub issue #5576.
Trying to open an interactive shell in a new window as a different user, via the -Credential parameter, is broken up to at least PowerShell 7.1, resulting in keyboard input getting blocked both in the new window and in the caller's window - see this answer for a workaround with runas.exe and GitHub issue #12129.

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.