任务系统的知识体系相对复杂,今天继续介绍任务系统的内容:任务配置的更多参数以及任务结果的分析功能。
在自定义的任务里可以使用 command 属性,并在这个属性里指定我们想要运行的脚本或者程序。但是有的时候我们运行这个脚本的时候需要传入一些参数,这时候就可以简单地把这些参数全部写在 command 里,比如说在运行 echo ‘Hello World’ 这样的脚本时,我们可以把它直接放入 command 这个属性的值中:
{
"label": "echo",
"type": "shell",
"command": "echo 'Hello World'"
}
JSON
我们也可以使用一个新的属性,叫做args。它是一个字符串的数组,在运行指定 command 的时候,args 里的每个值都会被当作其参数传入,所以就上面的例子,我们还可以写为:
{
"label": "echo",
"type": "shell",
"command": "echo",
"args": [
"hello world"
]
}
JSON
但这里要注意的是,因为我们使用的是 JSON 来存储这些参数,而 JSON 的数据格式并不一定能够满足 shell 的要求,比如不同的 shell 对空白符、$ 符之类的都有不同的解析方式,这时候就需要对这些符号进行转义。我们可以将参数调整为下面的格式:
"args": [
{
"value": "Hello World",
"quoting": "escape"
}
]
JSON
我们可以看到,args 里的值,从一个字符串,变成一个对象。它的第一个键是 value 值,也就是原先的字符串,而第二个键 quoting 则决定了该如何处理这段字符串。
quoting在默认情况下是使用escape转义,也就是说任务系统会根据我们所使用的 shell 的要求,对这段字符串进行转义。比如说,bash 下我们使用\来转义特殊字符,那么当我们执行这个任务时,真正运行的脚本如下:
echo Hello\ World
Shell
从上面的代码示例里,你可以看到空格被转义成了 \ 。
除此之外,quoting 这个参数还有另外两个值。第一个是 strong,那么在 bash里, 我们将会使用单引号包裹这段字符串,然后传给脚本,那么最终执行的脚本是:
echo 'Hello World'
Shell
另一个值是 weak,在 bash 里我们则会使用双引号来包裹这段字符串。如:
echo "Hello World"
Shell
strong 和 weak 分别对应了 shell 不同的使用引号的策略,而bash、cmd、powershell 也都有各自的策略。如果你不熟悉,可以搜索 “quoting mechanism” 来查找,当然我们在VS Code关于 Task 参数转义部分的文档也有涉及。
对了,当你在 VS Code 里编辑这个 tasks.json 文件的时候是提供了自动补全和提示的,所以你可以看看它还支持什么别的属性,也可以试着根据提示进行修改。我们在文章的最后,还会介绍几个其他重要的属性的。
到这里我已经基本把任务系统是如何设置的、如何运行的跟你简单地介绍了一遍,相信你已经可以将一些简单的脚本用任务系统来执行了。但是如果说任务系统只是提供一种新的运行脚本的方式,或者说几个快捷键进行一键的脚本运行的话,那跟集成终端比只能说是往前迈了小小的一步。
不过,任务系统还有一个真正的威力,就是我们可以自动地去分析任务运行的结果。
任务运行的结果是由 tasks.json 里任务的一个属性 problemMatcher 来控制的。我们可以选择 VS Code 内置的,或者其他插件提供的结果分析器,甚至可以自己书写结果分析器来分析任务运行结果,然后将其中出现的错误或者警告,显示在问题面板中。
比如说我们跑一个构建脚本,有的时候代码写的不对了,构建脚本就会打印出在哪个文件的第几行有一个什么类型的错误,然后我们再借助合适的结果分析器,去解析相对冗余的结果日志,最后把它们塞入问题面板中。这样我们在书写的过程当中,就不需要到结果日志里去找错误和警告了,只需要查看问题面板,点击错误跳转到代码处,直接进行修改就可以了。
另外,如果我们在运行一些脚本的时候使用了观察模式 (watch mode),那么每次代码有更新,就会重新运行脚本输出日志,这时一个实时分析日志并提供反馈的结果分析器就大大提升效率了。
VS Code 现在已经自带了以下几种问题分析器:
如果这些还不适用于你的项目,那你可以看看插件市场上有没有问题分析器相关的插件Search results – problem matcher | Visual Studio Code , Visual Studio Marketplace,或者看看你使用的语言插件是否已经支持了相对应的结果分析。
VS Code 还支持我们自己书写结果分析器,不过这个涉及一定的正则表达式的知识,如果你已经有所了解,那么可以继续看下去。如果还未有涉及,还请学习正则表达式,然后阅读下面的示例。
首先我们将下面的配置,放入任务配置文件 tasks.json。
{
"version": "2.0.0",
"tasks": [
{
"label": "echo",
"type": "shell",
"command": "echo",
"args": [
{
"value": "index.js:5:3: warning: unused variable",
"quoting": "escape"
}
],
"problemMatcher": {
"owner": "echo",
"fileLocation": ["relative", "${workspaceFolder}"],
"pattern": {
"regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
}
}
]
}
JSON
这个示例,较之前我们介绍的示例,多一个 problemMatcher,其他都是一样的。但是这个 problemMatcher 不再是一个字符串,而是一个对象;我们在这个对象里,自定义了如何去分析任务运行的结果。
这个任务执行的脚本是:
echo index.js:5:3:\ warning:\ unused\ variable
Shell
在 bash 下执行的结果如下:
index.js:5:3: warning: unused variable
这是我们通常能够看到的构建或者测试脚本报错时的输出结果,我们需要从中把以下几个信息提取出来:
通过文件的地址、行号和列号,我们能够快速定位到错误的位置,而错误的级别和信息则能够帮助我们了解错误的具体情况。为了把这个信息提取出来,我们将会使用正则表达式的捕获组(group capture)。比如在我们的例子里,我们提供了一个正则表达式:
"^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$"
当我们拿这个正则表达式去匹配下面的字符串时,
index.js:5:3: warning: unused variable
捕获组 1 对应的是文件的名字,捕获组 2 则是行号,以此类推。然后我们通过给这个任务的 problemMatcher 设置 pattern 来告诉 VS Code,我们想使用什么样的正则表达式去匹配,以及文件名 file、行号 line 、列 column 等该从第几个捕获组读取出来。
"pattern": {
"regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
JSON
除了 pattern 这个属性,我们还需要 fileLocation 文件位置来告诉 VS Code,如何在当前文件夹下定位这个文件。比如我们从错误信息里得到 index.js 是一个相对的地址,然后我们通过把 fileLocation 设置为 [“relative”, “${workspaceFolder}”] 来提示 VS Code,请把 index.js当作相对地址,然后在当前文件夹下定位。
最后,当我们运行了这个任务,我们就能够在问题面板里看到这个错误信息,而当我们点击这个错误时,则能够在编辑器里打开 index.js 这个文件并跳转到第五行。
上面就是一个非常基础的问题分析器 problemMatcher 了。它能够逐行使用正则表达式分析任务执行的结果,并输出给问题面板。但是如果你的任务执行的结果里,错误信息横跨多行,那么这个分析器就不起作用了。这时候你就需要一个更强大的多行结果分析器,这个我会在后面的 VS Code 高级定制章节里进一步介绍,如果你非常感兴趣现在就想动手试一试的话,也可以阅读Tasks in Visual Studio Code自行学习。
很多时候,我们的项目并不只包含一种语言、一个框架,这导致我们需要同时使用多种不同的构建或者测试工具,并需要额外写一些脚本把它们同时运行起来。不过不用担心,VS Code 的任务系统也为这种情况提供了便捷的任务定制方案。比如说我们的项目里有前端和后端两种代码,然后我们希望同时把它们运行起来,这时我们就要首先为前、后端分别定义好各自的任务,这里我们将它们称为 “frontend”“backend”。我们可以新建一个任务,内容如下:
{
"taskName": "compile",
"dependsOn": [
"frontend",
"backend"
],
"group": {
"kind": "build",
"isDefault": true
}
}
JSON
这个任务有个新的属性,叫做 dependsOn。它指定了“compile”这个任务依赖于 “frontend” 和 “backend” 这两个脚本,而这个任务本身并没有制定任何的命令 (command),同时我们还制定了这个任务为默认的生成任务(build),所以当我们按下 Cmd + Shift + B,我们就能够看到“frontend” 和 “backend” 这两个任务都被触发执行了。
通过多任务的设置,我们就真正做到一键运行了。不过要注意,这个功能在 VS Code 里叫做 Compound tasks ,这可能并不是一个特别好记的英文名字。
以上就是我们今天的全部内容了。VS Code 的任务系统,在我看来,精髓全在这个 tasks.json 的书写,也就是说一个 JSON 对象,控制了任务的方方面面。而我只是根据我的理解和学习方式,为你做了一次梳理:
对一个个体而言,任务系统的优势可能还不明显。但是如果你通过设置任务系统、添加错误分析器,把工作流程针对 VS Code 进行一次优化,这样你的同事在使用 VS Code 的时候,也就可以直接使用 VS Code 的任务系统和问题面板了,而无需为命令行脚本工具而烦恼了。
当然,你熟悉完我上面所讲的那些知识点后,可能还会有更多的问题提出来,我们可以在讨论区里交流。另外,我也鼓励你自己动手调试这个文件,VS Code 为这个文件做了专门的自动补全,所以你可以通过提示来研究学习。由于篇幅所限,我可能无法将任务系统的每个知识点都覆盖到,比如说任务系统的配置支持预定义参数,但是这个知识点我们在介绍代码片段里涉及到了,如果你对代码片段的预定义参数很熟悉,这个也就不陌生了。