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.

enter image description here

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.

  1. Windows Terminal is not a shell/command processor.
  2. There is no concept of run in WT.
  3. There is only run with a command processor, PowerShell or cmd.exe. cmd.exe executes .bat, .cmd, .vbs. Powershell executes .ps.
  4. 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:

  1. Double-click on a .bat file: Runs the script in the CMD shell in the "old" "Windows Console" (conhost.exe) terminal
  2. Double-click on a .ps1 file: Opens the script in Notepad
  3. 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 to wt 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.
  • 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 the command. The Value Data here will be wt 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.
  • 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: Sample Registry

Other notes:

  • Best practice is to use fully-qualified paths for powershell.exe and cmd.exe, and you should probably modify the above commands to do so. However, for wt.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 installed wt.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, under cmdfile in the same registry leaf.

  • You can, of course, modify the above for PowerShell Core as well by using pwsh.exe instead of powershell.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`"")
    }
}