Is it possible to have scripts run in Windows Terminal?
I have little power shell and cmd scripts that I run now and then. When I click on a bat script, it opens a classic CMD window. When I run a PS1 script, it opens the classic PowerShell window.
Is it possible to have these open in the new Windows Terminal by default when I run them? No, I don't want to open the terminal, navigate to the script, and run it. I just want to click on the .bat and have it run in the CMD window that is within the Windows Terminal app.
Can this be done?
I tried finding the terminal exe so that I could set ps1 files to open with it but I get an error when I try to do that. See screenshot.
Thanks!
Solution 1:
I am putting this here since it is too short for a comment, and that you are not asking a PowerShell code question/issue, but asking a 'How do I configure my environment/chosen tool, to do X or Y.
- Windows Terminal is not a shell/command processor.
- There is no concept of run in WT.
- There is only run with a command processor, PowerShell or cmd.exe. cmd.exe executes .bat, .cmd, .vbs. Powershell executes .ps.
- WT, on launch, starts your default shell/command processor.
- PowerShell.exe
- Pwsh.exe
- cmd.exe
- Bash.exe
- Python.exe
... whatever processors you have configured in your WT settings.
Even if you reghack this, it will still only run the default shell/command processor you have configured in the JSON settings. So, why start a shell host, just to run a shell/command processor.
What NotTheDr01ds
is showing, is already the default when right-clicking a .ps1 file and trying to change that to WT.exe will not work as you'd think.
You can first prove this to yourself by using WinKey + R and typing in the below. One will not work the other will.
wt d:\scripts\hello.ps1
# Results - error and locks WT
<#
[error 0x800700c1 when launching `d:\scripts\hello.ps1']
#>
wt powershell -noprofile -noexit d:\scripts\hello.ps1
# Results - runs the script as expected
<#
Hello World
Tuesday, 23 March, 2021 20:15:28
#>
So, any reg hack would require you to specify the command processor and the filename and any arguments needed for a successful run. As noted in my comments. You can avoid reghacks and just create a shortcut and add this shortcut to your SendTo menu. In Windows Explorer, just type...
shell:SendTo
Or in PowerShell just to this:
explorer shell:SendTo
... and paste your shortcut there (I have lots there), then you have to make the settings changes.
# Example:
"C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.6.10571.0_x64__8wekyb3d8bbwe\wt.exe" powershell -noprofile -noexit
I am letting you know in advance, that this ran into some issues.
Sometimes it worked, sometimes it did not.
Now, of course, if you wanted to do this for any of the command processors, then you need to create multiple shortcuts, alter them as needed, and paste them to the SendTo folder, then right-click your file, and select the proper SendTo shortcut.
Solution 2:
The default behavior today, as you know is:
-
Double-click on a
.bat
file: Runs the script in the CMD shell in the "old" "Windows Console" (conhost.exe
) terminal -
Double-click on a
.ps1
file: Opens the script in Notepad -
Right-click on a
.ps1
file and "Run with PowerShell": Runs the script in the PowerShell shell in the "old" Windows Console terminal
If I understand correctly, you want to change the behavior of (1) and (3) to run the scripts in their respective shells, but inside Windows Terminal.
@postanote makes good points that:
- Registry hacks should be a last resort.
-
shell:SendTo
may work for a number of cases without resorting to modifying the Registry.
So the SendTo technique that @postanote suggests is worth a try to see if it works for you. At first, I thought it might fail if there was a space in the path to the script. But in my testing, Windows seems to use the 8.3 short-path.
The SendTo method does have two downsides:
- SendTo's apply to all file types, so you'll see the Send To -> Windows Terminal when clicking on a file that doesn't support it.
- SendTo entries are nested under a submenu, so they require a bit of extra mouse movement to get to. On the other hand, "Open with"-style commands are right at the top of the right-click menu.
If it turns out you need something more, then read on for the Registry modification instructions. I do feel these are safe modifications, but the normal Registry warnings apply -- Your mileage may vary / Make backups / Here be dragons / etc.
Next caveat, while you probably can modify the default .bat
double-click "Open" command, I don't recommend doing so. There may be other applications (or even Windows functionality) that relies on that default behavior. My recommendation is to add a right-click "Run in Windows Terminal" option for CMD .bat
files, just as we will for PowerShell .ps1
files.
To do so:
- Run
regedit.exe
(or your preferred method of launching the registry editor) - Navigate to
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\batfile\shell
- Right-click on
shell
-> New -> Key - Name the key "run_in_wt"
- Right-click on
run_in_wt
-> New -> String Value - Name the property "MUIVerb"
- Double-click
MUIVerb
and set the Value data to "Run in Windows Terminal" (or however you want it to appear in the right-click menu) - Right-click on
run_in_wt
again -> New -> Key - Name the key "command"
- Double-click on the
(Default)
property and set the Value Data towt new-tab --title "CMD Shell" cmd.exe /k "%1"
- The
/k
switch keeps the shell from exiting after the script is complete. This differs from the "normal" double-click-on-a-bat behavior. If you want the old behavior, just change the/k
to/c
and the shell will exit when the script is complete. - Quoting the
"%1"
here is what allows us to run scripts with spaces in the path. Windows substitutes the full path of the script being right-click for the%1
.
- The
- Test it out
For Powershell/.ps1
files, repeat the process, but:
-
Navigate to
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Microsoft.PowerShellScript.1\Shell
-
Repeat everything up until you change the
(Default)
property for thecommand
. The Value Data here will bewt new-tab --title "Windows PowerShell" powershell.exe -NoExit -Command "if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass } \; & '%1'"
- As before, the
-NoExit
is added to prevent the shell from exiting (and thus closing the tab/window) when the script is complete. - If desired, you can add a
-NoProfile
to keep your PowerShell startup from running when executing scripts this way. I didn't put in there in case your scripts have any dependencies which are loaded in your profile.
- As before, the
-
The
Set-ExecutionPolicy
is copied from the "Run with PowerShell 7" right-click menu that is optional installed with PowerShell Core on Windows. It allows the script to execute (bypasses restrictions) as long as the policy isn't at its strictest level. -
Example Screenshot:
Other notes:
-
Best practice is to use fully-qualified paths for
powershell.exe
andcmd.exe
, and you should probably modify the above commands to do so. However, forwt.exe
I believe it is safer to just use it without the path, since it is an "App Execution Alias" that is defined for the Microsoft Store app. On the other hand, if you didn't install through the Store, perhaps you should use the full path to where you installedwt.exe
as well. -
If there are other extensions you launch (e.g.
.cmd
for CMD), then you'll need to set those up as well. For instance, undercmdfile
in the same registry leaf. -
You can, of course, modify the above for PowerShell Core as well by using
pwsh.exe
instead ofpowershell.exe
. -
The hardest part of this process, IMHO, is making sure the quoting and escaping is correct. Windows parses these commands using the CMD shell's quoting and escaping rules, but then we are also running PowerShell code inside that command which needs to be escaped properly.
-
As you can see from the discussion in the comments, a lot of people feel that "terminal" vs. "shell" is an important distinction, and it is. But I don't believe that we should expect that everyone asking questions here (or even answering them) will have the "perfect" vocabulary to describe what they are asking. We ask; we answer; we learn. Apologies if any of the comments came across too harshly.
Solution 3:
Also providing another option, which is an extension of @postanote's answer. This may make the "Send to" option more reliable, but it definitely has the added benefit of being a single Send to -> Windows Terminal that works for both PowerShell/.ps1
and CMD/.bat
.
It still has the same drawbacks as any other Send To option, as mentioned in my other answer. The Registry Run in Windows Terminal is still an option if you need it, of course.
-
Create
send_to_wt.bat
wherever you normally create your scripts. This batch file will check the extension of the file being right-clicked, and run it in the appropriate interpreter inside Windows Terminal.@echo off setlocal set _filename=%~n1 set _extension=%~x1 if "%_extension%"==".bat" goto wt_cmd if "%_extension%"==".cmd" goto wt_cmd if "%_extension%"==".ps1" goto wt_ps :error wt new-tab --title "Error" cmd.exe /c echo Script extension must be either .ps1 or .bat ^& pause goto commonexit :wt_cmd wt new-tab --title "CMD Shell" cmd.exe /k \"%1\" goto commonexit :wt_ps wt new-tab --title "Windows PowerShell" powershell.exe -NoExit -Command "if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass } \; & '%1'" goto commonexit :commonexit
-
Right-click on
send_to_wt.bat
and copy it to the clipboard. -
Run
explorer.exe shell:sendto
from PowerShell, CMD, or the Windows Start Menu -
Paste Shortcut into the SentTo folder
-
Rename the shortcut to "Windows Terminal"
-
Right click on the shortcut, select Properties, and set it to Run Minimized
That's it.
Update: For completeness, here's a PowerShell version of the same SendTo script, with the added feature of running .ps1
scripts in PowerShell Core if it is available, but falling back to Windows PowerShell if Core is not installed.
Unlike the CMD version, you need to set your Link Target manually to pwsh.exe -f send_to_wt.ps1
(or powershell.exe
):
[String]$fileName = $args -join " "
[String]$fileExtension = [System.IO.Path]::GetExtension($fileName)
switch ($fileExtension) {
{$_ -in @(".bat", ".cmd")} {
[String]$cmdExe = (Get-Command cmd.exe).Path
Start-Process wt.exe -ArgumentList @('new-tab','--title "CMD Shell"',"$cmdExe /k `"$fileName`"")
}
{$_ -in @(".ps1")} {
[String]$psExe = ""
[String]$title = ""
if (Get-Command pwsh.exe -ErrorAction Ignore) {
$psExe = (Get-Command pwsh.exe).Path
$title = "PowerShell Core"
}
else {
$psExe = (Get-Command powershell.exe).Path
$title = "Windows PowerShell"
}
Start-Process wt.exe -ArgumentList @('new-tab',"--title `"$title`"","$psExe -NoLogo -NoExit -f `"$fileName`"")
}
}