Create multiple terminals and run commands in VSCode
Asked Answered
O

3

17

I'm on a Mac πŸ’». I'm trying to explore a way to create 4 Terminals as soon as I dbl-clicked on my workspace file. I've tried to get one working, but I seem stuck

{
    "folders": [
        {
            "path": "/Users/bheng/Sites/laravel/project"
        }
    ],
    "settings": {
        "workbench.action.terminal.focus": true,
        "terminal.integrated.shell.osx": "ls",
        "terminal.integrated.shellArgs.osx": [
            "ls -lrt"
         ]
    },
    "extensions": {}
}

My goal is to open 4 Terminals

  • Terminal1 : run 'npm run watch'
  • Terminal2 : run 'ls -lrt'
  • Terminal3 : run 'ssh_staging'
  • Terminal4 : run 'mysql'

I've been following this doc : https://code.visualstudio.com/docs/editor/integrated-terminal#_terminal-keybindings

Any hints for me ?

Oratory answered 10/3, 2020 at 15:56 Comment(0)
C
20

I like the accepted answer. However, I prefer not to use the multi-command extension as shown in the accepted answer, I think my approach is simpler.

Please note in my case:

  • my project only needs three tasks and all three tasks should run in parallel (craft-server, craft-app, and craft-site) -- but this approach should work for more than three tasks
  • I prefer to see the output of three tasks in three separate terminals (vs combined in one terminal)
  • my tasks never "finish" (all three tasks "watch" for file changes, so I need the terminals to remain open)

See my tasks.json file below. You'll need to modify the "label" and "command" properties to suit your project. See my notes about the important parts, below.

{
    "version": "2.0.0",
    "tasks": [
        /// ...other tasks...
        {
            "label": "runDevelopment",
            "runOptions": {
                "runOn": "folderOpen"
            },
            "dependsOrder": "parallel",
            "dependsOn": [
                "craft-server",
                "craft-site",
                "craft-app"
            ]
        },
        {
            "label": "craft-server",
            "type": "shell",
            "command": "npx nodemon --watch . --ignore craft-angular/projects/craft-app/ --ignore craft-angular/projects/craft-site/ --ignore dist/ --ignore bin/ --ignore log/ --ignore cypress/ --ignore cypress.json ./bin/www",
            "presentation": {
                "panel": "dedicated"
            }
        },
        {
            "label": "craft-site",
            "type": "shell",
            "command": "cd ./craft-angular  && node --max_old_space_size=8000 ./node_modules/@angular/cli/bin/ng build craft-site --verbose=false --progress=true --watch --output-path=\"./dist/development/craft-site\"",
            "presentation": {
                "panel": "dedicated"
            }
        },
        {
            "label": "craft-app",
            "type": "shell",
            "command": "cd ./craft-angular  && node --max_old_space_size=8000 ./node_modules/@angular/cli/bin/ng build craft-app --verbose=false --progress=true --watch --output-path=\"./dist/development/craft-app\"",
            "presentation": {
                "panel": "dedicated"
            }
        }
    ]
}

Please note:

  • I only use the VS Code tasks.json / custom tasks feature (I don't use a VS Code extension)
  • I use the "dependsOn" approach as shown in the accepted answer, so that one task can invoke multiple other tasks in parallel (note "dependsOrder": "parallel")
  • I use the "runOptions": {"runOn": "folderOpen"} approach as shown in the accepted answer, so that VSCode will automatically run my "combined" task when I open my workspace/project
    • "runOn": "folderOpen" is convenient for me (I always want to run my main task when I open the folder containing my project),
    • but it is optional; you could also use keybindings as shown in the accepted answer or here
    • and if you use "runOn": "folderOpen" you need give VS Code a one-time permission to do that, as described here
  • I don't use the "problemMatcher" property (i.e. a VS Code feature to scan output of each terminal); therefore when I run the task, I choose "Continue without scanning the task output"
  • I use the "presentation" property with {"panel":"dedicated"} so each of my tasks gets a separate terminal (aka separate panel)

The runDevelopment task should run automatically when I open the workspace/project/folder (i.e. the location containing the .vscode folder, and the .vscode/tasks.json file, using File > Open Folder... (Ctrl+K Ctrl+O), or File > Open Recent...)

While I want this task to run automatically , below shows how run the task manually (if needed -- like if the automatic task fails, or I kill the automatic task);

You could use Ctrl+Shift+B to run the build task, as commented by @Ruben, and described in the VSCode Keyboard Bindings here.

Or you could use a more step-by-step approach:

  1. I use Ctrl+Shift+P (or F1) to open the "command window"/ "Show All Commands"
  2. then type "Run Tasks"; (hit Enter)
  3. then choose the single "combined" task (for me, it's named runDevelopment; hit Enter)
  4. finally choose "Continue without scanning the task output" and hit Enter (because none of my tasks have a "problemMatcher", I can interpret the task output for myself): running vscode task manually using ctrl+shift+p

This is how the task looks after it is run; note there are 3 separate terminals for 3 separate subtasks: vscode multiple terminals separate terminal for each task

Colene answered 27/11, 2021 at 3:55 Comment(3)
thanks just what I needed, ctrl+shift+b should also works – Apportionment
Is there a way to skip the loading icon for all terminals? It is annoying... – Lacroix
@AilinAlbertoni are your referring to the loading icon to the right of the terminal name, each loading icon is always spinning while its terminal is operating? Good question, I'm not sure.... – Colene
C
11

I've been playing around with this which seems to work. Combining the ability to run a task on folder open and to make that task depend on other tasks I came up with the following. It looks cumbersome but it is actually pretty simple and repetitive.

First, you will need a macro extension like multi-command. Put this into your settings:

"multiCommand.commands": [

    {
      "command": "multiCommand.runInFirstTerminal",
      "sequence": [
        "workbench.action.terminal.new",
        {
          "command": "workbench.action.terminal.renameWithArg",
          "args": {
            "name": "npm watch"
          }
        },
        {
          "command": "workbench.action.terminal.sendSequence",
          "args": {
            "text": "npm run watch\u000D"  // \u000D is a return so it runs
          }
        }
      ]
    },
    {
      "command": "multiCommand.runInSecondTerminal",
      "sequence": [
        "workbench.action.terminal.new",
        {
          "command": "workbench.action.terminal.renameWithArg",
          "args": {
            "name": "ls -lrt"
          }
        },
        {
          "command": "workbench.action.terminal.sendSequence",
          "args": {
            "text": "ls -lrt\u000D"
          }
        }
      ]
    },
    {
      "command": "multiCommand.runInThirdTerminal",
      "sequence": [
        "workbench.action.terminal.new",
        {
          "command": "workbench.action.terminal.renameWithArg",
          "args": {
            "name": "ssh_staging"
          }
        },
        {
          "command": "workbench.action.terminal.sendSequence",
          "args": {
            "text": "ssh_staging\u000D"  // however you run the ssh_staging command
          }
        }
      ]
    },
    {
      "command": "multiCommand.runInFourthTerminal",
      "sequence": [
        "workbench.action.terminal.new",
        {
          "command": "workbench.action.terminal.renameWithArg",
          "args": {
            "name": "mysql"
          }
        },
        {
          "command": "workbench.action.terminal.sendSequence",
          "args": {
            "text": "mysql\u000D"  // however you run the mysql command
          }
        },
        // "workbench.action.focusActiveEditorGroup"
      ]
    }
]

There is one command for each terminal. But within each of those you can do as much as you can get into a macro - which is a lot, especially thanks to the sendSequence command. You can change directories and send another sendSequence command to the same terminal instance, run all the non-terminal commands too, change focus to an editor at the end of the last terminal set-up, etc.

I added the nicety of naming each terminal based on your command using the command workbench.action.terminal.renameWithArg.

In tasks.json:

 "tasks": [

    {
      "label": "Run 4 terminals on startup",
      "runOptions": {"runOn": "folderOpen"},

      "dependsOrder": "sequence",  // or parallel

      "dependsOn": [
        "terminal1",
        "terminal2",
        "terminal3",
        "terminal4"
      ]
    },  

    {
      "label": "terminal1",
      "command": "${command:multiCommand.runInFirstTerminal}"
    },
    {
      "label": "terminal2", 
      "command": "${command:multiCommand.runInSecondTerminal}",
    },
    {
      "label": "terminal3",
      "command": "${command:multiCommand.runInThirdTerminal}"
    },
    {
      "label": "terminal4",
      "command": "${command:multiCommand.runInFourthTerminal}"
    }
 ]

Now whenever you open (or reload) the workspace folder this tasks.json is in the four terminals should be opened, named and run. In my experience, there is about a short delay before vscode runs any folderOpen task.


If you prefer to manually trigger the Run 4 terminals task, you can set up a keybinding like so:

{
  "key": "alt+r",     // whatever keybinding you want
  "command": "workbench.action.tasks.runTask",
  "args": "Run 4 terminals on startup"
},

Here is a demo running with the keybinding, easier to demonstrate than reloading vscode, but there is no difference. I added an interval delay to each terminal running just for demonstration purposes - otherwise it is extremely fast.

opening four terminals and running commands in them

I noticed that vscode freezes if I don't interact with one of the terminals or open another before deleting them all.


There is also a Terminal Manager extension which may be of interest. I haven't tried it.

An extension for setting-up multiple terminals at once, or just running some commands.

But it isn't obvious to me whether this extension can be configured to run on folderOpen - but it appears to contribute a run all the terminals command so you should be able to use that in a task.

Counterespionage answered 13/3, 2020 at 5:57 Comment(2)
I have a problem doing it, VSCode says: Couldn't resolve dependent task 'terminal1' in workspace folder 'file:///home/..' I've added the multi-command lines in settings.json and the taskj in tasks.json, I'm working on Linux Do you have any idea? – Callihan
I am unable to test it on linux. I would start simple by seeing if just a keybinding to run just the terminal1 task by itself works (and I would make that task just echo or ls) just to eliminate other possibilites. Then try the Run 4 terminals task via a keybinding with only one dependent task, your terminal1. I'm assuming you installed the multi-command extension too? – Counterespionage
S
3

I like the second answer that only uses vscode task, but it does not work for my requirement because I cannot input other instructions in the open terminals, otherwise, it will close. I prefer to use the Restore Terminals in vscode.

After the extension installed, you can just create a restore-terminals.json file in .vscode folder:

{
  "artificialDelayMilliseconds": 300,
  "keepExistingTerminalsOpen": false,
  "runOnStartup": true,
  "terminals": [
    {
      "splitTerminals": [
        {
          "name": "server",
          "commands": ["npm i", "npm run dev"]
        },
        {
          "name": "client",
          "commands": ["npm run dev:client"]
        },
        {
          "name": "test",
          "commands": ["jest --watch"]
        }
      ]
    },
    {
      "splitTerminals": [
        {
          "name": "build & e2e",
          "commands": ["npm run eslint", "npm run build", "npm run e2e"],
          "shouldRunCommands": false
        },
        {
          "name": "worker",
          "commands": ["npm-run-all --parallel redis tsc-watch-start worker"]
        }
      ]
    }
  ]
}

Soilure answered 17/12, 2021 at 4:46 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.