> 文档中心 > 谷歌 zx 脚手架模块中文文档

谷歌 zx 脚手架模块中文文档

谷歌 zx 脚手架模块中文文档


zx 是 2021 gibhub上的一个新的明星项目,它让我们可以便捷的使用 JavaScript / TypeScript(该项目包含TypeScript类型声明)替代 bash 搭建命令行脚本。该脚本对于掌握前端开发的人员来说,提供了搭建脚手架上的便利,最近关注了该项目,翻译了其使文档,作为笔记分享之。

zx 官方 Github 主页:https://github.com/google/zx

zx 官方 npm 主页:https://www.npmjs.com/package/zx

本文地址: https://blog.csdn.net/qq_28550263/article/details/123403575

翻译者:jcLee95


目 录

🐚 zx

  • 安装
  • 文档
    • $`command`
      • ProcessPromise
      • ProcessOutput
    • 函数
      • cd()
      • fetch()
      • question()
      • sleep()
      • nothrow()
      • quiet()
      • chalk
      • yaml
      • fs
      • globby
      • os
      • path
      • minimist
    • 配置
      • $.shell
      • $.prefix
      • $.quote
      • $.verbose
    • Polyfills
      • __filename & __dirname
      • require()
    • 实验性的
      • retry()
      • echo()
      • startSpinner()
    • FAQ
      • 传递环境变量
      • 传递值数组
      • 从其他脚本导入
      • 没有扩展名的脚本
      • Markdown 脚本
      • TypeScript 脚本
      • 执行远程脚本
      • 从stdin执行脚本
  • License

引号 Quotes

管道(Pipelines)

Markdown 脚本


zx


🐚 zx

#!/usr/bin/env zxawait $`cat package.json | grep name`let branch = await $`git branch --show-current`await $`dep deploy --branch=${branch}`await Promise.all([  $`sleep 1; echo 1`,  $`sleep 2; echo 2`,  $`sleep 3; echo 3`,])let name = 'foo bar'await $`mkdir /tmp/${name}`

Bash 很棒,但是在编写脚本时,人们通常会选择更方便的编程语言。
JavaScript 是一个完美的选择,但是标准Node.js库在使用之前需要额外的麻烦。zx包围绕child_process提供了有用的包装器,对参数进行转义并给出合理的默认值。

安装

npm i -g zx

要求: Node 版本 >= 16.0.0

文档

在扩展名为.mjs的文件中编写脚本,以便能够在顶层使用await。如果您喜欢使用. js扩展名,可以将您的脚本包装成类似于void async function () {...}()
将以下声明添加到您的 zx 脚本的开头:

#!/usr/bin/env zx

现在,您将能够像这样运行您的脚本:

chmod +x ./script.mjs./script.mjs

或者通过 zx 可执行文件:

zx ./script.mjs

所有函数($cdfetch等)都可以直接使用,无需任何导入。

或者显式导入全局变量(为了在VS代码中更好地自动完成)。

import 'zx/globals'

$`command`

使用child_process包中的 spawn函数执行给定字符串,并返回ProcessPromise

一切都通过 ${...} 将被自动转义并引用。

let name = 'foo & bar'await $`mkdir ${name}`

不需要额外添加引号。 在 quotes 中阅读更多相关信息

如果需要,您可以传递一组参数:

let flags = [  '--oneline',  '--decorate',  '--color',]await $`git log ${flags}`

如果执行的程序返回非零退出代码,将抛出 ProcessOutput

try {  await $`exit 1`} catch (p) {  console.log(`Exit code: ${p.exitCode}`)  console.log(`Error: ${p.stderr}`)}

ProcessPromise

class ProcessPromise<T> extends Promise<T> {  readonly stdin: Writable  readonly stdout: Readable  readonly stderr: Readable  readonly exitCode: Promise<number>  pipe(dest): ProcessPromise<T>  kill(signal = 'SIGTERM'): Promise<void>}

pipe() 方法可用于重定向stdout:

await $`cat file.txt`.pipe(process.stdout)

阅读更多关于 pipelines的信息。

ProcessOutput

class ProcessOutput {  readonly stdout: string  readonly stderr: string  readonly exitCode: number  readonly signal: 'SIGTERM' | 'SIGKILL' | ...  toString(): string}

函数

cd()

更改当前工作目录。

cd('/tmp')await $`pwd` // outputs /tmp

fetch()

node-fetch 包的包装。

let resp = await fetch('https://wttr.in')if (resp.ok) {  console.log(await resp.text())}

question()

readline 包的包装器。

用法:

let bear = await question('What kind of bear is best? ')let token = await question('Choose env variable: ', {  choices: Object.keys(process.env)})

在第二个参数中,可以指定制表符自动完成的选项数组。

function question(query?: string, options?: QuestionOptions): Promise<string>type QuestionOptions = { choices: string[] }

sleep()

setTimeout函数的包装。

await sleep(1000)

nothrow()

$ 的行为更改为不在非零退出代码上引发异常。

function nothrow<P>(p: P): P

用法:

await nothrow($`grep something from-file`)// 一个 pipe() 内部:await $`find ./examples -type f -print0`  .pipe(nothrow($`xargs -0 grep something`))  .pipe($`wc -l`)

如果只需要exitCode,可以使用下一个代码:

if (await $`[[ -d path ]]`.exitCode == 0) {  ...}// 相当于:if ((await nothrow($`[[ -d path ]]`)).exitCode == 0) {  ...}

quiet()

更改 的行为以禁用详细输出。

function quiet<P>(p: P): P

用法:

await quiet($`grep something from-file`)// 不会显示命令和输出。

以下软件包无需导入内部脚本即可使用。

chalk

chalk 包。

console.log(chalk.blue('Hello world!'))

yaml

yaml 包。

console.log(YAML.parse('foo: bar').foo)

fs

fs-extra 包。

let content = await fs.readFile('./package.json')

globby

globby 包

let packages = await globby(['package.json', 'packages/*/package.json'])let pictures = globby.globbySync('content/*.(jpg|png)')

此外,globby可通过 glob 快捷方式获得:

await $`svgo ${await glob('*.svg')}`

os

os 包

await $`cd ${os.homedir()} && mkdir example`

path

path 包。

await $`mkdir ${path.join(basedir, 'output')}`

minimist

minimist 包。

作为全局常量 argv 提供。

配置

$.shell

指定使用什么 shell 。默认为 which bash

$.shell = '/usr/bin/bash'

或者使用一个 CLI 参数: --shell=/bin/bash

$.prefix

指定将作为所有运行命令的前缀的命令。

默认为: set -euo pipefail;.

或者使用一个 CLI 参数:--prefix='set -e;'

$.quote

指定在命令替换期间转义特殊字符的函数。

$.verbose

指定详细程度。
默认值为 true.

在详细模式下, zx打印所有执行的命令及其输出。

或者使用一个 CLI 参数:--quiet 来设置 $.verbose = false.

Polyfills

__filename & __dirname

在 ESM 模块中,Node.js不提供 __filename__dirname 全局变量。因为这样的全局变量在脚本中非常方便,所以 zx 提供了这些用于 .mjs 文件中(当使用 zx 可执行文件时)。

require()

在 ESM 模块中, require() 函数没有被定义。
zx 提供require() 函数,因此它可以与 .mjs 文件中的导入一起使用(当使用 zx 可执行文件时)。

let {version} = require('./package.json')

实验性的

zx还提供了一些实验功能。请在讨论中留下关于这些功能的反馈。

retry()

重试命令几次。将在第一次成功尝试后返回,或将在指定的尝试次数后引发。

import {retry} from 'zx/experimental'let {stdout} = await retry(5)`curl localhost`

echo()

可以接受 ProcessOutput 的console.log()替代项。

import {echo} from 'zx/experimental'let branch = await $`git branch --show-current`echo`Current branch is ${branch}.`// 或者echo('Current branch is', branch)

startSpinner()

启动一个简单的CLI微调器,并返回 stop() 函数。

import {startSpinner} from 'zx/experimental'let stop = startSpinner()await $`long-running command`stop()

FAQ

传递环境变量

process.env.FOO = 'bar'await $`echo $FOO`

传递值数组

如果值的数组作为参数传递给 $,数组的项将被单独转义并通过空格连接。

例如:

let files = [...]await $`tar cz ${files}`

从其他脚本导入

通过显式导入可以使用 $和其他函数:

#!/usr/bin/env nodeimport {$} from 'zx'await $`date`

没有扩展名的脚本

如果脚本没有文件扩展名 (例如 .git/hooks/pre-commit), zx 将假定它是一个 ESM 模块。

Markdown 脚本

zx 可以执行用 markdown 编写的脚本。

zx docs/markdown.md

TypeScript 脚本

import {$} from 'zx'// Or import 'zx/globals'void async function () {  await $`ls -la`}()

使用 ts-node 作为一个 esm node loader.

node --loader ts-node/esm script.ts

你必须i在 package.json 中设置 "type": "module" 以及在 tsconfig.json 中设置"module": "ESNext"

{  "type": "module"}
{  "compilerOptions": {    "module": "ESNext"  }}

执行远程脚本

如果 zx 可执行文件的参数以 https:// 开头,则文件将被下载并执行。

zx https://medv.io/example-script.mjs
zx https://medv.io/game-of-life.mjs

从stdin执行脚本

zx支持从stdin执行脚本。

zx <<'EOF'await $`pwd`EOF

License

Apache-2.0

免责声明: 这不是官方支持的谷歌产品。


Quotes(引号)


将参数传递给 ${...} 不需要加引号。如果需要,将自动添加。

let name = 'foo & bar'await $`mkdir ${name}`

对于引号,zx 使用特殊的bash语法(接下来的命令是有效的bash):

mkdir $'foo & bar'$'ls' $'-la'

如果添加引号 "${name}",将会产生错误的命令。

如果你需要添加额外的东西,考虑把它放在花括号里。

await $`mkdir ${'path/to-dir/' + name}`

这也将正常工作:

await $`mkdir path/to-dir/${name}`

参数数组

zx 也可以在 ${...} 中接受数组或参数。数组中的项将被单独引用,并通过空格连接起来。

不要添加 .join(' ')

let flags = [  '--oneline',  '--decorate',  '--color',]await $`git log ${flags}`

如果你已经有了一个带有数组的字符串,那么正确解析它并区分不同的参数就是你的责任了。比如像这样:

await $`git log ${'--oneline --decorate --color'.split(' ')}`

globbing 和 ~

当一切都通过${...} 将被转义,不能使用 ~glob 语法。

为了实现这个目的,zx提供了 globby package.

而不能这样:

let files = '~/dev/**/*.md' // 错await $`ls ${files}`

使用 glob 函数和 os”包:

let files = await glob(os.homedir() + '/dev/**/*.md')await $`ls ${files}`

管道(Pipelines)


zx 支持 Node.js 流(stream),特殊的 pipe() 方法可用于重定向标准输出。

await $`echo "Hello, stdout!"`  .pipe(fs.createWriteStream('/tmp/output.txt'))await $`cat /tmp/output.txt`

$ 创建的进程从 process.stdin 获取stdin,但是我们也可以写入子进程:

let p = $`read var; echo "$var";`p.stdin.write('Hello, stdin!\n')let {stdout} = await p

管道可用于显示程序的实时输出:

$.verbose = falseawait $`echo 1; sleep 1; echo 2; sleep 1; echo 3;`  .pipe(process.stdout)

通过管道传输stdout和stderr:

let echo = $`echo stdout; echo stderr 1>&2`echo.stdout.pipe(process.stdout)echo.stderr.pipe(process.stdout)await echo

此外,pipe() 方法可以组合 $ 程序。与bash中的 | 相同:

let greeting = await $`printf "hello"`  .pipe($`awk '{printf $1", world!"}'`)  .pipe($`tr '[a-z]' '[A-Z]'`)console.log(greeting.stdout)

使用pipe()nothrow():

await $`find ./examples -type f -print0`  .pipe(nothrow($`xargs -0 grep ${'missing' + 'part'}`))  .pipe($`wc -l`)

Markdown 脚本


使用markdown编写脚本是可能的。zx只执行代码块。

您可以运行这个markdown 文件:

zx docs/markdown.md
await $`whoami`await $`echo ${__dirname}`

__filename 将指向 markdown.md:

console.log(chalk.yellowBright(__filename))

我们也可以在这里使用导入:

await import('chalk')

bash代码(带有 bashsh语言标签)也将被执行:

VAR=$(date)echo "$VAR" | wc -c

其他代码块被忽略:

body .hero {    margin: 42px;}

谷歌 zx 脚手架模块中文文档 阅读世界,共赴山海 谷歌 zx 脚手架模块中文文档 423全民读书节,邀你共读