通过任务集成外部工具

存在许多用于自动化任务的工具,例如 linting(代码检查)、构建、打包、测试或部署软件系统。例如 TypeScript 编译器,像 ESLintTSLint 这样的 linter,以及像 MakeAntGulpJakeRakeMSBuild 这样的构建系统。

VS Code can talk to a variety of external tools

这些工具大多从命令行运行,并自动化软件开发周期(编辑、编译、测试和调试)内外的作业。鉴于它们在开发生命周期中的重要性,能够在 VS Code 内部运行这些工具并分析其结果是非常有帮助的。VS Code 中的任务可以配置为运行脚本和启动进程,因此可以在不进入命令行或编写新代码的情况下,在 VS Code 内部使用许多现有工具。工作区或文件夹特定的任务通过工作区 .vscode 文件夹中的 tasks.json 文件进行配置。

扩展也可以使用 任务提供程序 (Task Provider) 来贡献任务,这些贡献的任务可以添加在 tasks.json 文件中定义的工作区特定配置。

注意: 任务支持仅在处理工作区文件夹时可用。在编辑单个文件时不可用。

TypeScript Hello World

让我们从一个简单的“Hello World”TypeScript 程序开始,我们希望将其编译为 JavaScript。

创建一个空文件夹 "mytask",生成一个 tsconfig.json 文件,并从该文件夹启动 VS Code。

mkdir mytask
cd mytask
tsc --init
code .

现在创建一个内容如下的 HelloWorld.ts 文件

function sayHello(name: string): void {
  console.log(`Hello ${name}!`);
}

sayHello('Dave');

按下 ⇧⌘B (Windows, Linux Ctrl+Shift+B) 或从全局 终端 (Terminal) 菜单运行 运行构建任务 (Run Build Task),会显示以下选择器

TypeScript Build Task

第一个条目执行 TypeScript 编译器并将 TypeScript 文件转换为 JavaScript 文件。当编译器完成后,应该会有一个 HelloWorld.js 文件。第二个条目以监视模式启动 TypeScript 编译器。每次保存 HelloWorld.ts 文件时,都会重新生成 HelloWorld.js 文件。

您还可以将 TypeScript 构建或监视任务定义为默认构建任务,以便在触发 运行构建任务 (Run Build Task) (⇧⌘B (Windows, Linux Ctrl+Shift+B)) 时直接执行它。为此,请从全局 终端 (Terminal) 菜单中选择 配置默认构建任务 (Configure Default Build Task)。这将显示一个包含可用构建任务的选择器。选择 tsc: buildtsc: watch,VS Code 将生成一个 tasks.json 文件。如下所示,它将 tsc: build 任务设为默认构建任务

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "type": "typescript",
      "tsconfig": "tsconfig.json",
      "problemMatcher": ["$tsc"],
      "group": {
        "kind": "build",
        "isDefault": true
      }
    }
  ]
}

上面的 tasks.json 示例并未定义新任务。它将 VS Code 的 TypeScript 扩展贡献的 tsc: build 任务注解为默认构建任务。现在,您可以按下 ⇧⌘B (Windows, Linux Ctrl+Shift+B) 来执行 TypeScript 编译器。

任务自动检测

VS Code 目前可为以下系统自动检测任务:Gulp、Grunt、Jake 和 npm。我们正在与相应的扩展作者合作,以添加对 Maven 和 C# dotnet 命令的支持。如果您使用 Node.js 作为运行时开发 JavaScript 应用程序,通常会有一个 package.json 文件来描述您的依赖项和要运行的脚本。如果您已经克隆了 eslint-starter 示例,那么从全局菜单执行 运行任务 (Run Tasks) 将显示以下列表

Tasks ESLint starter

如果尚未执行,请通过运行 npm install 安装必要的 npm 模块。现在打开 server.js 文件,并在语句末尾添加一个分号(请注意,ESLint starter 假设语句没有分号),然后再次执行 运行任务 (Run Tasks)。这次选择 npm: lint 任务。当提示选择要使用的问题匹配器时,选择 ESLint stylish

Tasks ESLint Problem Matcher Selection

执行该任务会产生一个错误,显示在 问题 (Problems) 视图中

Tasks ESLint Problem

此外,VS Code 创建了一个内容如下的 tasks.json 文件

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "type": "npm",
      "script": "lint",
      "problemMatcher": ["$eslint-stylish"]
    }
  ]
}

这指示 VS Code 使用 ESLint stylish 格式扫描 npm lint 脚本的输出以查找问题。

对于 Gulp、Grunt 和 Jake,任务自动检测的工作方式相同。以下是为 vscode-node-debug 扩展检测到的任务示例。

Gulp task auto-detection

提示: 您可以通过 快速打开 (Quick Open) (⌘P (Windows, Linux Ctrl+P)) 输入 'task',然后按 空格 和命令名称来运行您的任务。在本例中,即 'task lint'。

可以使用以下设置禁用任务自动检测

{
  "js/ts.tsc.autoDetect": "off",
  "grunt.autoDetect": "off",
  "jake.autoDetect": "off",
  "gulp.autoDetect": "off",
  "npm.autoDetect": "off"
}

自定义任务

并非工作区中的所有任务或脚本都可以被自动检测到。有时需要定义自己的自定义任务。假设您有一个用于运行测试的脚本,以便正确设置某些环境。该脚本存储在工作区内的脚本文件夹中,在 Linux 和 macOS 上命名为 test.sh,在 Windows 上命名为 test.cmd。从全局 终端 (Terminal) 菜单运行 配置任务 (Configure Tasks),然后选择 从模板创建 tasks.json 文件 (Create tasks.json file from template) 条目。这将打开以下选择器

Configure Task Runner

注意: 如果您没有看到任务运行程序模板列表,则说明您的文件夹中可能已经有一个 tasks.json 文件,并且其内容将在编辑器中打开。在本示例中,请关闭该文件并删除或重命名它。

我们正在努力提供更多的自动检测支持,因此将来此列表会越来越短。由于我们要编写自己的自定义任务,请从列表中选择 其他 (Others)。这将打开带有任务骨架的 tasks.json 文件。将内容替换为以下内容

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Run tests",
      "type": "shell",
      "command": "./scripts/test.sh",
      "windows": {
        "command": ".\\scripts\\test.cmd"
      },
      "group": "test",
      "presentation": {
        "reveal": "always",
        "panel": "new"
      }
    }
  ]
}

任务的属性具有以下语义

  • label:用户界面中使用的任务标签。
  • type:任务类型。对于自定义任务,可以是 shellprocess。如果指定了 shell,则该命令被解释为 shell 命令(例如:bash、cmd 或 PowerShell)。如果指定了 process,则该命令被解释为要执行的进程。
  • command:要执行的实际命令。
  • windows:任何 Windows 特定的属性。当在 Windows 操作系统上执行命令时,将使用这些属性代替默认属性。
  • group:定义任务所属的组。在本例中,它属于 test 组。属于测试组的任务可以通过从 命令面板 (Command Palette) 运行 运行测试任务 (Run Test Task) 来执行。
  • presentation:定义在用户界面中如何处理任务输出。在此示例中,显示输出的集成终端总是 (always) 被显示,并且每次运行任务时都会创建一个 new 终端。
  • options:覆盖 cwd(当前工作目录)、env(环境变量)或 shell(默认 shell)的默认值。选项可以按任务设置,也可以全局或按平台设置。此处配置的环境变量只能从您的任务脚本或进程中引用,如果它们是您的参数、命令或其他任务属性的一部分,则不会被解析。
  • runOptions:定义任务何时以及如何运行。
  • hide:在“运行任务快速选择 (Run Task Quick Pick)”中隐藏任务,这对于复合任务中无法独立运行的元素非常有用。

您可以在 tasks.json 文件中使用 IntelliSense 查看完整的任务属性集及其值。使用 触发建议 (Trigger Suggest) (⌃Space (Windows, Linux Ctrl+Space)) 调用建议,并通过悬停或使用 阅读更多... ('i') 悬浮框阅读说明。

tasks.json IntelliSense

您也可以查看 tasks.json 架构

当 shell 命令包含空格或其他特殊字符(如 $)时,需要对其进行特殊处理。默认情况下,任务系统支持以下行为

  • 如果提供了单个命令,任务系统将按原样将该命令传递给底层 shell。如果该命令需要引用或转义才能正常运行,则该命令必须包含正确的引号或转义字符。例如,要列出名称中包含空格的文件夹目录,在 bash 中执行的命令应如下所示:ls 'folder with spaces'
{
  "label": "dir",
  "type": "shell",
  "command": "dir 'folder with spaces'"
}
  • 如果提供了命令和参数,当命令或参数包含空格时,任务系统将使用单引号。对于 cmd.exe,将使用双引号。像下面的 shell 命令将在 PowerShell 中作为 dir 'folder with spaces' 执行。
{
  "label": "dir",
  "type": "shell",
  "command": "dir",
  "args": ["folder with spaces"]
}
  • 如果您想控制参数的引用方式,参数可以是一个字面量,指定值和引用样式。下面的示例使用转义而不是引用来处理带有空格的参数。
{
  "label": "dir",
  "type": "shell",
  "command": "dir",
  "args": [
    {
      "value": "folder with spaces",
      "quoting": "escape"
    }
  ]
}

除转义外,还支持以下值

  • strong:使用 shell 的强引用机制,这会抑制字符串内的所有求值。在 PowerShell 和 Linux 及 macOS 的 shell 下,使用单引号 (')。对于 cmd.exe,使用 "
  • weak:使用 shell 的弱引用机制,它仍然会对字符串内的表达式进行求值(例如,环境变量)。在 PowerShell 和 Linux 及 macOS 的 shell 下,使用双引号 (")。cmd.exe 不支持弱引用,因此 VS Code 也使用 "

如果命令本身包含空格,VS Code 默认也会强引用该命令。与参数一样,用户可以使用相同的字面量样式来控制命令的引用。

还有更多任务属性可以配置您的工作流。您可以使用 ⌃Space (Windows, Linux Ctrl+Space) 使用 IntelliSense 获取有效属性的概览。

Tasks IntelliSense

除了全局菜单栏,还可以使用 命令面板 (Command Palette) (⇧⌘P (Windows, Linux Ctrl+Shift+P)) 访问任务命令。您可以过滤 'task' 并查看各种与任务相关的命令。

Tasks in Command Palette

复合任务

您还可以使用 dependsOn 属性将简单的任务组合成复合任务。例如,如果您有一个包含 client 和 server 文件夹的工作区,并且两者都包含构建脚本,则可以创建一个在独立终端中启动这两个构建脚本的任务。如果您在 dependsOn 属性中列出多个任务,它们默认会并行执行。

tasks.json 文件如下所示

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Client Build",
      "command": "gulp",
      "args": ["build"],
      "options": {
        "cwd": "${workspaceFolder}/client"
      }
    },
    {
      "label": "Server Build",
      "command": "gulp",
      "args": ["build"],
      "options": {
        "cwd": "${workspaceFolder}/server"
      }
    },
    {
      "label": "Build",
      "dependsOn": ["Client Build", "Server Build"]
    }
  ]
}

如果指定 "dependsOrder": "sequence",则您的任务依赖项将按其在 dependsOn 中列出的顺序执行。在 dependsOn 中使用 "dependsOrder": "sequence" 的任何后台/监视任务都必须具有一个问题匹配器,用于跟踪它们何时“完成”。以下任务运行任务 Two,任务 Three,然后运行任务 One。

{
  "label": "One",
  "type": "shell",
  "command": "echo Hello ",
  "dependsOrder": "sequence",
  "dependsOn": ["Two", "Three"]
}

用户级任务

您可以使用 任务:打开用户任务 (Tasks: Open User Tasks) 命令创建不绑定到特定工作区或文件夹的用户级任务。此处只能使用 shellprocess 任务,因为其他任务类型需要工作区信息。

输出行为

有时您希望控制运行任务时集成终端面板的行为。例如,您可能希望最大化编辑器空间,并且仅在认为存在问题时才查看任务输出。终端的行为可以使用任务的 presentation 属性进行控制。它提供以下属性

  • reveal:控制是否将集成终端面板置于前台。有效值为
    • always - 面板总是被置于前台。这是默认值。
    • never - 用户必须使用 视图 (View) > 终端 (Terminal) 命令 (⌃` (Windows, Linux Ctrl+`)) 显式地将终端面板置于前台。
    • silent - 仅当未扫描输出中的错误和警告时,终端面板才会被置于前台。
  • revealProblems:控制运行此任务时是否显示“问题”面板。优先于 reveal 选项。默认值为 never
    • always - 执行此任务时总是显示“问题”面板。
    • onProblem - 仅在发现问题时才显示“问题”面板。
    • never - 执行此任务时从不显示“问题”面板。
  • focus:控制终端是否获取输入焦点。默认值为 false
  • echo:控制是否在终端中回显已执行的命令。默认值为 true
  • showReuseMessage:控制是否显示“终端将被任务重用,按任意键关闭”的消息。
  • panel:控制任务运行之间是否共享终端实例。可能的值为
    • shared - 共享终端,其他任务运行的输出被添加到同一个终端中。
    • dedicated - 终端专用于特定任务。如果再次执行该任务,则重用该终端。但是,不同任务的输出会显示在不同的终端中。
    • new - 该任务的每次执行都会使用一个新的干净终端。
  • clear:控制在此任务运行之前是否清除终端。默认值为 false
  • close:控制任务退出时是否关闭运行任务的终端。默认值为 false
  • group:控制任务是否使用拆分窗格在特定终端组中执行。同一组中的任务(由字符串值指定)将使用拆分终端来呈现,而不是使用新的终端面板。

您也可以修改自动检测任务的终端面板行为。例如,如果您想更改上面 ESLint 示例中 npm: run lint 的输出行为,请为其添加 presentation 属性

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "type": "npm",
      "script": "lint",
      "problemMatcher": ["$eslint-stylish"],
      "presentation": {
        "reveal": "never"
      }
    }
  ]
}

您也可以将自定义任务与已检测任务的配置混合使用。一个配置 npm: run lint 任务并添加自定义 运行测试 (Run Test) 任务的 tasks.json 如下所示

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "type": "npm",
      "script": "lint",
      "problemMatcher": ["$eslint-stylish"],
      "presentation": {
        "reveal": "never"
      }
    },
    {
      "label": "Run tests",
      "type": "shell",
      "command": "./scripts/test.sh",
      "windows": {
        "command": ".\\scripts\\test.cmd"
      },
      "group": "test",
      "presentation": {
        "reveal": "always",
        "panel": "new"
      }
    }
  ]
}

运行行为

您可以使用 runOptions 属性指定任务的运行行为

  • reevaluateOnRerun:控制通过 重新运行上次任务 (Rerun Last Task) 命令执行任务时如何评估变量。默认值为 true,意味着重新运行任务时将重新评估变量。设置为 false 时,将使用任务上一次运行中解析的变量值。
  • runOn:指定任务运行的时间。
    • default - 任务仅在通过 运行任务 (Run Task) 命令执行时运行。
    • folderOpen - 任务将在打开包含文件夹时运行。您第一次打开包含带有 folderOpen 任务的文件夹时,系统会询问您是否允许在文件夹中自动运行任务。您可以稍后使用 管理自动任务 (Manage Automatic Tasks) 命令并在 允许自动任务 (Allow Automatic Tasks)不允许自动任务 (Disallow Automatic Tasks) 之间进行选择来更改您的决定。
  • instanceLimit - 允许同时运行的任务实例数。默认值为 1
  • instancePolicy - 确定当任务达到其 instanceLimit 时会发生什么。可以设置为
  • prompt - 提示用户终止哪个实例(默认)。
  • silent - 不启动新实例(静默)。
  • terminateNewest - 终止最新运行的实例。
  • terminateOldest - 终止最早运行的实例。
  • warn - 不启动新实例(显示警告)。

自定义自动检测的任务

如上所述,您可以在 tasks.json 文件中自定义自动检测的任务。您通常这样做是为了修改演示属性或附加问题匹配器以扫描任务输出中的错误和警告。您可以直接从 运行任务 (Run Task) 列表中按右侧的齿轮图标来插入相应的任务引用到 tasks.json 文件中,从而自定义任务。假设您有以下 Gulp 文件来使用 ESLint lint JavaScript 文件(文件取自 https://github.com/adametry/gulp-eslint

const gulp = require('gulp');
const eslint = require('gulp-eslint');

gulp.task('lint', () => {
  // ESLint ignores files with "node_modules" paths.
  // So, it's best to have gulp ignore the directory as well.
  // Also, Be sure to return the stream from the task;
  // Otherwise, the task may end before the stream has finished.
  return (
    gulp
      .src(['**/*.js', '!node_modules/**'])
      // eslint() attaches the lint output to the "eslint" property
      // of the file object so it can be used by other modules.
      .pipe(eslint())
      // eslint.format() outputs the lint results to the console.
      // Alternatively use eslint.formatEach() (see Docs).
      .pipe(eslint.format())
      // To have the process exit with an error code (1) on
      // lint error, return the stream and pipe to failAfterError last.
      .pipe(eslint.failAfterError())
  );
});

gulp.task('default', ['lint'], function() {
  // This will only run if the lint task is successful...
});

从全局 终端 (Terminal) 菜单执行 运行任务 (Run Task) 将显示以下选择器

Configure Task

按下齿轮图标。这将创建以下 tasks.json 文件

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "type": "gulp",
      "task": "default",
      "problemMatcher": []
    }
  ]
}

通常,您现在会添加一个问题匹配器(在本例中为 $eslint-stylish)或修改演示设置。

使用问题匹配器处理任务输出

VS Code 可以使用问题匹配器处理任务的输出。问题匹配器会扫描任务输出文本中已知的警告或错误字符串,并将其内联报告在编辑器中以及“问题”面板中。VS Code 内置了几个问题匹配器

  • TypeScript$tsc 假设输出中的文件名是相对于已打开文件夹的。
  • TypeScript Watch$tsc-watch 匹配以监视模式执行 tsc 编译器时报告的问题。
  • JSHint$jshint 假设文件名是以绝对路径报告的。
  • JSHint Stylish$jshint-stylish 假设文件名是以绝对路径报告的。
  • ESLint Compact$eslint-compact 假设输出中的文件名是相对于已打开文件夹的。
  • ESLint Stylish$eslint-stylish 假设输出中的文件名是相对于已打开文件夹的。
  • Go$go 匹配 go 编译器报告的问题。假设文件名是相对于已打开文件夹的。
  • CSharp and VB Compiler$mscompile 假设文件名是以绝对路径报告的。
  • Lessc compiler$lessc 假设文件名是以绝对路径报告的。
  • Node Sass compiler$node-sass 假设文件名是以绝对路径报告的。

您也可以创建自己的问题匹配器,我们将在 稍后的章节 中讨论。

将键盘快捷键绑定到任务

如果您需要经常运行任务,可以为该任务定义一个键盘快捷键。

例如,要将 Ctrl+H 绑定到上面的 运行测试 (Run tests) 任务,请将以下内容添加到您的 keybindings.json 文件中

{
  "key": "ctrl+h",
  "command": "workbench.action.tasks.runTask",
  "args": "Run tests"
}

变量替换

在编写任务配置时,使用一组预定义的常用变量(如活动文件 (${file}) 或工作区根文件夹 (${workspaceFolder}))非常有用。VS Code 支持在 tasks.json 文件的字符串中进行变量替换,您可以在 变量参考 (Variables Reference) 中查看预定义变量的完整列表。

注意: 并非所有属性都接受变量替换。具体来说,只有 commandargsoptions 支持变量替换。

以下是将当前打开的文件传递给 TypeScript 编译器的自定义任务配置示例。

{
  "label": "TypeScript compile",
  "type": "shell",
  "command": "tsc ${file}",
  "problemMatcher": ["$tsc"]
}

同样,您可以通过在该名称前加上 ${config: 来引用项目的配置设置。例如,${config:python.formatting.autopep8Path} 返回 Python 扩展设置 formatting.autopep8Path

以下是一个自定义任务配置示例,它使用 python.formatting.autopep8Path 设置定义的 autopep8 可执行文件在当前文件上执行 autopep8

{
  "label": "autopep8 current file",
  "type": "process",
  "command": "${config:python.formatting.autopep8Path}",
  "args": ["--in-place", "${file}"]
}

如果您想为 tasks.jsonlaunch.json 指定 Python 扩展使用的选定 Python 解释器,可以使用 ${command:python.interpreterPath} 命令。

如果简单的变量替换还不够,您还可以通过在 tasks.json 文件中添加一个 inputs 部分,从任务用户处获取输入。

Inputs Example

有关 inputs 的更多信息,请参阅 变量参考 (Variables Reference)

特定于操作系统的属性

任务系统支持定义特定于操作系统的值(例如,要执行的命令)。为此,将操作系统特定的字面量放入 tasks.json 文件,并在该字面量内指定相应的属性。

以下是一个使用 Node.js 可执行文件作为命令,并在 Windows 和 Linux 上进行不同处理的示例

{
  "label": "Run Node",
  "type": "process",
  "windows": {
    "command": "C:\\Program Files\\nodejs\\node.exe"
  },
  "linux": {
    "command": "/usr/bin/node"
  }
}

有效的操作系统属性是用于 Windows 的 windows,用于 Linux 的 linux 以及用于 macOS 的 osx。在操作系统特定范围内定义的属性会覆盖在任务或全局范围内定义的属性。

全局任务

任务属性也可以在全局范围内定义。如果存在,它们将用于特定任务,除非这些任务定义了具有不同值的相同属性。在下面的示例中,有一个全局 presentation 属性,它定义所有任务都应在新的面板中执行

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "presentation": {
    "panel": "new"
  },
  "tasks": [
    {
      "label": "TS - Compile current file",
      "type": "shell",
      "command": "tsc ${file}",
      "problemMatcher": ["$tsc"]
    }
  ]
}

提示: 要访问全局范围的 tasks.json 文件,请打开命令面板 (⇧⌘P (Windows, Linux Ctrl+Shift+P)) 并运行 任务:打开用户任务 (Tasks: Open User Tasks) 命令。

PowerShell 中的字符转义

当默认 shell 为 PowerShell,或者配置任务使用 PowerShell 时,您可能会看到意外的空格和引号转义。意外转义仅发生在 cmdlet 中,因为 VS Code 不知道您的命令是否包含 cmdlet。下面的示例 1 显示了在 PowerShell 中不起作用的转义情况。示例 2 显示了实现良好转义的最佳跨平台方式。在某些情况下,您可能无法遵循示例 2,此时您需要执行示例 3 中所示的手动转义。

"tasks": [
    {
        "label": "PowerShell example 1 (unexpected escaping)",
        "type": "shell",
        "command": "Get-ChildItem \"Folder With Spaces\""
    },
    {
        "label": "PowerShell example 2 (expected escaping)",
        "type": "shell",
        "command": "Get-ChildItem",
        "args": ["Folder With Spaces"]
    },
    {
        "label": "PowerShell example 3 (manual escaping)",
        "type": "shell",
        "command": "& Get-ChildItem \\\"Folder With Spaces\\\""
    }
]

更改任务输出的编码

任务经常处理磁盘上的文件。如果这些文件以不同于系统编码的编码存储在磁盘上,则需要让作为任务执行的命令知道要使用哪种编码。由于这取决于操作系统和所使用的 shell,因此没有控制此操作的通用解决方案。以下是关于如何实现这一点的建议和示例。

如果您需要调整编码,则应检查更改操作系统使用的默认编码,或者至少通过调整 shell 的配置文件来更改所用 shell 的编码,是否有意义。

如果您只需要为特定任务进行调整,则将更改编码所需的 OS 特定命令添加到任务命令行。以下示例是针对 Windows 使用代码页 437 作为其默认设置的情况。该任务显示包含西里尔字母的文件内容,因此需要代码页 866。假设默认 shell 设置为 cmd.exe,列出该文件的任务如下所示

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "label": "more",
      "type": "shell",
      "command": "chcp 866 && more russian.txt",
      "problemMatcher": []
    }
  ]
}

如果任务在 PowerShell 中执行,则命令需要写为 chcp 866; more russian.txt。在 Linux 和 macOS 上,可以使用 locale 命令来检查区域设置并调整必要的环境变量。

任务实战示例

为了突出任务的强大功能,这里有几个关于 VS Code 如何使用任务来集成 linters 和编译器等外部工具的示例。

将 TypeScript 编译为 JavaScript

TypeScript 主题 包含一个示例,该示例创建了一个任务,用于将 TypeScript 编译为 JavaScript,并从 VS Code 内部观察任何相关错误。

将 Less 和 SCSS 编译为 CSS

CSS 主题提供了关于如何使用任务来生成 CSS 文件的示例。

  1. 使用构建任务手动编译
  2. 使用文件监视器实现编译步骤的自动化

定义问题匹配器

VS Code 内置了一些最常见的问题匹配器。但是,市面上有许多编译器和 linting 工具,它们都会产生各自的错误和警告风格,因此您可能需要创建自己的问题匹配器。

我们有一个 helloWorld.c 程序,开发人员错误地将 printf 键入为 prinft。使用 gcc 编译它将产生以下警告

helloWorld.c:5:3: warning: implicit declaration of function ‘prinft’

我们想要生成一个可以捕获输出中的消息并在 VS Code 中显示相应问题的匹配器。问题匹配器非常依赖 正则表达式。以下部分假设您熟悉正则表达式。

提示: 我们发现具有 ECMAScript (JavaScript) 风格的 RegEx101 游乐场 是开发和测试正则表达式的好方法。

捕获上述警告(及错误)的匹配器如下所示

{
  // The problem is owned by the cpp language service.
  "owner": "cpp",
  // The file name for reported problems is relative to the opened folder.
  "fileLocation": ["relative", "${workspaceFolder}"],
  // The name that will be shown as the source of the problem.
  "source": "gcc",
  // The actual pattern to match problems in the output.
  "pattern": {
    // The regular expression. Example to match: helloWorld.c:5:3: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]
    "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
    // The first match group matches the file name which is relative.
    "file": 1,
    // The second match group matches the line on which the problem occurred.
    "line": 2,
    // The third match group matches the column at which the problem occurred.
    "column": 3,
    // The fourth match group matches the problem's severity. Can be ignored. Then all problems are captured as errors.
    "severity": 4,
    // The fifth match group matches the message.
    "message": 5
  }
}

请注意,file、line 和 message 属性是必需的。fileLocation 指定任务输出中生成并与问题匹配的文件路径是 absolute(绝对路径)还是 relative(相对路径)。如果任务同时生成绝对路径和相对路径,则可以使用 autoDetect 文件位置。使用 autoDetect 时,路径首先被测试为绝对路径,如果文件不存在,则路径被假定为相对路径。

severity 指定如果模式未包含严重性级别时应使用的问题严重性。severity 的可能值为 errorwarninginfo

这是一个完成的 tasks.json 文件,其中包含上述代码(已删除注释),并包装了实际的任务详细信息

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build",
      "command": "gcc",
      "args": ["-Wall", "helloWorld.c", "-o", "helloWorld"],
      "problemMatcher": {
        "owner": "cpp",
        "fileLocation": ["relative", "${workspaceFolder}"],
        "source": "gcc",
        "pattern": {
          "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
          "file": 1,
          "line": 2,
          "column": 3,
          "severity": 4,
          "message": 5
        }
      }
    }
  ]
}

在 VS Code 中运行它并按 ⇧⌘M (Windows, Linux Ctrl+Shift+M) 获取问题列表,将得到以下输出

GCC Problem Matcher

注意: C/C++ 扩展 包含 GCC 的问题匹配器,因此无需定义我们自己的。

还有几个可以在模式中使用的属性。这些是

  • location - 如果问题位置是行,或者行,列,或者开始行,开始列,结束行,结束列,那么可以使用我们的通用位置匹配组。
  • endLine - 问题结束行的匹配组索引。如果编译器未提供结束行值,则可以省略。
  • endColumn - 问题结束列的匹配组索引。如果编译器未提供结束列值,则可以省略。
  • code - 问题代码的匹配组索引。如果编译器未提供代码值,则可以省略。

您也可以定义仅捕获文件的匹配器。为此,请定义一个 pattern,并将可选的 kind 属性设置为 file。在这种情况下,无需提供 linelocation 属性。

注意: 如果 kind 属性设置为 file,则功能性模式必须至少为 filemessage 提供匹配组。如果未提供 kind 属性或者 kind 属性设置为 location,则函数模式还必须提供 linelocation 属性。

注意: 问题匹配器仅解析给定命令的输出。如果您想解析写入单独文件(例如日志文件)的输出,请让您运行的命令在执行结束之前打印出单独文件中的行。

定义多行问题匹配器

一些工具会将源文件中发现的问题分散在多行中,尤其是在使用时尚的报告器(stylish reporters)时。一个例子是 ESLint;在 stylish 模式下,它产生如下输出

test.js
  1:0   error  Missing "use strict" statement                 strict
 1 problems (1 errors, 0 warnings)

我们的问题匹配器是基于行的,因此我们需要用与实际问题位置和消息(1:0 error Missing "use strict" statement)不同的正则表达式来捕获文件名(test.js)。

为此,请为 pattern 属性使用一个问题模式数组。这样,您可以为要匹配的每一行定义一个模式。

注意: 在多行问题匹配器中,输出中的每一行都必须由模式数组按顺序匹配。即使中间行不需要用于捕获值,也不能跳过它们。

以下问题模式匹配来自 ESLint stylish 模式的输出 - 但仍然存在一个我们需要在下一步解决的小问题。下面的代码有一个正则表达式来捕获文件名,第二个用来捕获行、列、严重性、消息和错误代码

{
  "owner": "javascript",
  "fileLocation": ["relative", "${workspaceFolder}"],
  "pattern": [
    {
      "regexp": "^([^\\s].*)$",
      "file": 1
    },
    {
      "regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$",
      "line": 1,
      "column": 2,
      "severity": 3,
      "message": 4,
      "code": 5
    }
  ]
}

但是,如果资源上有多个问题,此模式将不起作用。例如,想象来自 ESLint 的以下输出

test.js
  1:0   error  Missing "use strict" statement                 strict
  1:9   error  foo is defined but never used                  no-unused-vars
  2:5   error  x is defined but never used                    no-unused-vars
  2:11  error  Missing semicolon                              semi
  3:1   error  "bar" is not defined                           no-undef
  4:1   error  Newline required at end of file but not found  eol-last
 6 problems (6 errors, 0 warnings)

模式的第一个正则表达式将匹配 "test.js",第二个匹配 "1:0 error ..."。下一行 "1:9 error ..." 被处理但未被第一个正则表达式匹配,因此没有捕获到任何问题。

为了使此工作,多行模式的最后一个正则表达式可以指定 loop 属性。如果设置为 true,它会指示任务系统只要正则表达式匹配,就将多行匹配器的最后一个模式应用于输出中的行。

由第一个模式捕获的信息(在本例中匹配 test.js)将与匹配 loop 模式的后续每一行相结合,以创建多个问题。在此示例中,将创建六个问题。

这是一个完全捕获 ESLint stylish 问题的匹配器

{
  "owner": "javascript",
  "fileLocation": ["relative", "${workspaceFolder}"],
  "pattern": [
    {
      "regexp": "^([^\\s].*)$",
      "file": 1
    },
    {
      "regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$",
      "line": 1,
      "column": 2,
      "severity": 3,
      "message": 4,
      "code": 5,
      "loop": true
    }
  ]
}

注意:如果您在同一资源上遇到多个具有完全相同行和列的问题,则只会显示一个问题。这适用于所有问题匹配器,而不仅仅是多行问题匹配器。

修改现有问题匹配器

如果现有的问题匹配器接近您需要的,您可以在 tasks.json 任务中对其进行修改。例如,$tsc-watch 问题匹配器仅适用于已关闭的文档。如果您想让它适用于所有文档,可以对其进行修改

{
  "type": "npm",
  "script": "watch",
  "problemMatcher": {
    "base": "$tsc-watch",
    "applyTo": "allDocuments"
  },
  "isBackground": true
}

其他可修改的问题匹配器属性包括 backgroundfileLocationownerpatternseveritysource

后台/监视任务

某些工具支持在后台运行,同时监视文件系统以发现更改,然后在磁盘上的文件更改时触发操作。对于 Gulp,这种功能通过 npm 模块 gulp-watch 提供。TypeScript 编译器 tsc 通过 --watch 命令行选项内置了对此的支持。

为了提供后台任务在 VS Code 中处于活动状态并生成问题结果的反馈,问题匹配器必须使用额外信息来检测输出中的这些 state 更改。让我们以 tsc 编译器为例。当编译器在监视模式下启动时,它会将以下额外信息打印到控制台

> tsc --watch
12:30:36 PM - Compilation complete. Watching for file changes.

当磁盘上包含问题的文件发生更改时,会出现以下输出

12:32:35 PM - File change detected. Starting incremental compilation...
src/messages.ts(276,9): error TS2304: Cannot find name 'candidate'.
12:32:35 PM - Compilation complete. Watching for file changes.

查看输出显示以下模式

  • File change detected. Starting incremental compilation... 被打印到控制台时,编译器运行。
  • Compilation complete. Watching for file changes. 被打印到控制台时,编译器停止。
  • 在这两个字符串之间报告问题。
  • 编译器也会在初始启动后运行一次(无需在控制台上打印 File change detected. Starting incremental compilation...)。

为了捕获此信息,问题匹配器可以提供一个 background 属性。

对于 tsc 编译器,合适的 background 属性如下所示

"background": {
    "activeOnStart": true,
    "beginsPattern": "^\\s*\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM)? - File change detected\\. Starting incremental compilation\\.\\.\\.",
    "endsPattern": "^\\s*\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM)? - Compilation complete\\. Watching for file changes\\."
}

除了问题匹配器上的 background 属性外,任务本身必须标记为 isBackground,以便任务继续在后台运行。

一个用于在监视模式下运行 tsc 任务的完整手动制作的 tasks.json 如下所示

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "watch",
      "command": "tsc",
      "args": ["--watch"],
      "isBackground": true,
      "problemMatcher": {
        "owner": "typescript",
        "fileLocation": "relative",
        "pattern": {
          "regexp": "^([^\\s].*)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\):\\s+(error|warning|info)\\s+(TS\\d+)\\s*:\\s*(.*)$",
          "file": 1,
          "location": 2,
          "severity": 3,
          "code": 4,
          "message": 5
        },
        "background": {
          "activeOnStart": true,
          "beginsPattern": "^\\s*\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM)? - File change detected\\. Starting incremental compilation\\.\\.\\.",
          "endsPattern": "^\\s*\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM)? - Compilation complete\\. Watching for file changes\\."
        }
      }
    }
  ]
}

后续步骤

这就是任务 - 让我们继续...

  • tasks.json 架构 - 您可以查看完整的 tasks.json 架构和说明。
  • 基本编辑 - 了解功能强大的 VS Code 编辑器。
  • 代码导航 - 快速浏览您的源代码。
  • 语言支持 - 了解我们支持的编程语言,包括 VS Code 内置的和通过社区扩展提供的。
  • 调试 - 在 VS Code 编辑器中直接调试源代码。

常见问题

任务可以使用与为集成终端指定的 shell 不同的 shell 吗?

可以。您可以使用 "terminal.integrated.automationProfile.*" 设置来设置将用于 VS Code 中所有自动化的 shell,其中包括任务。

    "terminal.integrated.automationProfile.windows": {
        "path": "cmd.exe"
    }

或者,您可以使用 options.shell 属性覆盖任务的 shell。您可以按任务、全局或按平台进行设置。例如,要在 Windows 上使用 cmd.exe,您的 tasks.json 将包含

{
    "version": "2.0.0",
    "windows": {
        "options": {
            "shell": {
                "executable": "cmd.exe",
                "args": [
                    "/d", "/c"
                ]
            }
        }
    },
    ...

后台任务可以用作 launch.json 中的 prelaunchTask 吗?

可以。由于后台任务会一直运行直到被杀死,后台任务本身没有表明它已“完成”的信号。要将后台任务用作 prelaunchTask,您必须向该后台任务添加适当的后台 problemMatcher,以便任务系统和调试系统有一种方式知道该任务“完成”了。

您的任务可以是

{
  "type": "npm",
  "script": "watch",
  "problemMatcher": "$tsc-watch",
  "isBackground": true
}

注意: $tsc-watch 是一个后台问题匹配器,这是后台任务所必需的。

然后,您可以在 launch.json 文件中将该任务用作 prelaunchTask

{
  "name": "Launch Extension",
  "type": "extensionHost",
  "request": "launch",
  "runtimeExecutable": "${execPath}",
  "args": ["--extensionDevelopmentPath=${workspaceRoot}"],
  "stopOnEntry": false,
  "sourceMaps": true,
  "outFiles": ["${workspaceRoot}/out/src/**/*.js"],
  "preLaunchTask": "npm: watch"
}

有关后台任务的更多信息,请转到 后台/监视任务

为什么运行任务时出现“找不到命令 (command not found)”?

当您的终端无法识别要运行的任务命令时,会出现“找不到命令”消息。这种情况最常发生是因为命令被配置为 shell 启动脚本的一部分。任务作为非登录和非交互式运行,这意味着不会运行 shell 的启动脚本。特别是 nvm 以在配置中使用启动脚本而闻名。

有几种方法可以解决此问题

  1. 确保您的命令在路径中,并且不需要启动脚本将其添加到路径中。这是解决该问题的最彻底方法,也是推荐的解决方案。
  2. 您可以为任务进行一次性修复,以作为登录或交互式运行。不建议这样做,因为它可能会产生其他后果。但是,对于单个任务,它也可以是一个快速且简单的修复。下面是一个以 bash 作为 shell 执行此操作的任务示例
{
  "type": "npm",
  "script": "watch",
  "options": {
    "shell": {
      "args": ["-c", "-l"]
    }
  }
}

上述 npm 任务将像任务系统默认情况下那样,用一个命令 (-c) 运行 bash。但是,该任务还将 bash 作为登录 shell (-l) 运行。

© . This site is unofficial and not affiliated with Microsoft.