Skip to content

Open and Run Multiple Projects With One Command in WSL2 From Windows Terminal

Tools/ (Posted on )

When I develop projects running on Ubuntu inside WSL2 that are divided into subprojects, I use Windows Terminal to have both backend and frontend open side-by-side at the same time in two panes. Then I run the commands that start these projects up one by one in their respective panes. Instead of doing that by hand whenever its time for work, it can be automated with a custom command that creates the necessary panes, opens the project directories and runs the commands that start the projects up.

Step 1 - Creating a Custom Action

You can create a custom command in Windows Terminal by specifying it in your settings.json inside of the actions array. If you need a refresher on how to customize your Windows Terminal, check out my Windows Terminal Configuration Tips for Improved Workflow post about it. Basically, open the settings by pressing Ctrl + Shift + , and find the aforementioned property in the JSON structure. Here is the structure of a custom command.

"actions": [
  {
    "command": {
      "action": "wt",
      "commandline": "new-tab --title AwesomeApp --tabColor #2e84e8 -p Ubuntu"
    },
    "name": "Open my awesome app"
  }
]

The action defines what Windows Terminal action the command will execute, which in this case is wt, which basically is the command line interface to manage Windows Terminal. The commandline is the arguments you provide to wt. In this case, Windows Terminal will open a new tab in Ubuntu profile, which is specified with the -p flag, and give it a title and a color, which are specified by --title and --tabColor flags respectively. This custom command can be accessed from the command palette by pressing Ctrl + Shift + p and searching for the specified name - Open my awesome app. Or, it can can also be invoked using a key-binding, which requires keys property to be added to the configuration.

"actions": [
  {
    // ...
    "name": "Open my awesome app",
    "keys": "ctrl+alt+b"
  }
]

After running the command, a new tab with a custom title and color gets opened in the Ubuntu profile.

Newly created tab with a title and a custom color
Newly created tab with a title and a custom color

Note that some Linux distributions override the tab title, and you won’t see the title you passed via --title argument. To force the specified title, change the defaults setting under profiles to include suppressApplicationTitle.

"profiles": {
  "defaults": {
    "suppressApplicationTitle": true
  }
}

Step 2 - Creating Multiple Panes and Opening Directories

Now that the command is working and properly creates a new tab, let’s expand it to create two more panes that open directories for two different projects. To split the pane use the split-pane argument which can take flags -V or -H to specify in which direction to do the splitting - vertically or horizontally. Omitting the flags will fall back to auto mode being used, which automatically picks the optimal direction. Each new pane in argument list needs to be separated from the next one with ;. To specify the working directory of each pane use the -d argument. The --title and --tabColor must be repeated for each pane, otherwise, when a pane without a set title or color will be focused, it will fall back to the default respective setting. I will omit the the title, tab color and the profile specifications from now on. I can also omit the new-tab command, because it is the default one. When omitting the profile flag, the default profile gets loaded, which in my case is Ubuntu, therefore it is redundant to specify it each time in my case. I will omit all of these arguments for the sake of readability, because the command is going to get long. Since the configuration is done in JSON format, which requires the command to be one long string, I will show each part of the configuration separately. Later these parts need to be concatenated together, placing ; between them.

-d \\\\wsl$\\{DISTRO_NAME}\\home\\{USER}\\projects

This part creates a new tab and opens projects directory located under home directory.

split-pane -V -d \\\\wsl$\\{DISTRO_NAME}\\home\\{USER}\\projects\\backend

This part splits the previous pane vertically and opens backend directory located under ~/projects directory.

split-pane -H -d \\\\wsl$\\{DISTRO_NAME}\\home\\{USER}\\projects\\frontend

Finally, this part splits the previously split pane horizontally and opens the frontend directory.

{
  "command": {
    "action": "wt",
    "commandline": "-d \\\\wsl$\\Ubuntu\\home\\dev\\projects; split-pane -V -d \\\\wsl$\\Ubuntu\\home\\dev\\projects\\backend; split-pane -H -d \\\\wsl$\\Ubuntu\\home\\dev\\projects\\frontend"
    },
  "name": "Open my awesome app"
}
Multiple panes with different working directories
Multiple panes with different working directories

Step 3 - Executing Bash Commands

This is the most exciting part and the most time consuming one that I figured out by digging through many GitHub issues. To execute a command, you need to specify a path to a shell for the wt command to use. Since I am running Ubuntu in WSL2, I use Bash. Then, I pass Bash the -i and -l flags to open it in interactive mode and log in. Lastly, the -c flag specifies the actual command to execute.

"commandline": "-d \\\\wsl$\\{DISTRO_NAME}\\home\\{USER}\\projects \"%windir%\\System32\\bash.exe\" -i -l -c \"ls && bash\""

This example prints the contents of the current directory. The && bash part is used to prevent the window from closing immediately after successful execution of the ls command.

Going back to our case of three panes with two projects to run, let’s apply the same principle.

-d \\\\wsl$\\Ubuntu\\home\\dev\\projects
split-pane -V -d \\\\wsl$\\{DISTRO_NAME}\\home\\{USER}\\projects\\backend \"%windir%\\System32\\bash.exe\" -i -l -c \"node index.js\"
split-pane -H -d \\\\wsl$\\{DISTRO_NAME}\\home\\{USER}\\projects\\frontend \"%windir%\\System32\\bash.exe\" -i -l -c \"npm start\"

Gluing it all together produces one of the longest lines of text I have unironically written.

{
  "command": {
    "action": "wt",
    "commandline": "-d \\\\wsl$\\Ubuntu\\home\\dev\\projects; split-pane -V -d \\\\wsl$\\Ubuntu\\home\\dev\\projects\\backend \"%windir%\\System32\\bash.exe\" -i -l -c \"node index.js\"; split-pane -H -d \\\\wsl$\\Ubuntu\\home\\dev\\projects\\frontend \"%windir%\\System32\\bash.exe\" -i -l -c \"npm start\""
  },
  "name": "Open my awesome app"
}

Which achieves the desired result of a backend Express application and a frontend React application being started.

Two projects running after executing their start commands
Two projects running after executing their start commands

References