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

简介: 谷歌 zx 脚手架模块中文文档

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

#!/usr/bin/env zx
await $`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<ProcessOutput>

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

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()

可以接受 ProcessOutputconsole.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 node
import {$} 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 = false
await $`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;
}
目录
相关文章
|
7月前
|
数据挖掘 API Go
《Go 简易速速上手小册》第7章:包管理与模块(2024 最新版)(下)
《Go 简易速速上手小册》第7章:包管理与模块(2024 最新版)
77 1
|
7月前
|
关系型数据库 Go API
《Go 简易速速上手小册》第1章:Go 语言基础(2024 最新版)(上)
《Go 简易速速上手小册》第1章:Go 语言基础(2024 最新版)(上)
98 1
|
JSON 缓存 前端开发
Go语学习笔记 - 环境安装、接口测试 | Web框架Gin(一)
Go语学习笔记 - 环境安装、接口测试 | Web框架Gin(一)
Go语学习笔记 - 环境安装、接口测试 | Web框架Gin(一)
|
4月前
|
存储 Prometheus 中间件
2020最佳人气项目之Go Web框架
2020最佳人气项目之Go Web框架
|
5月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的小程序国产动漫论坛的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的小程序国产动漫论坛的详细设计和实现(源码+lw+部署文档+讲解等)
|
6月前
|
JSON Rust JavaScript
🎉使用Tauri+vite+koa2+mysql开发了一款待办效率应用
这是一个基于tauri+vite的应用,它采用了一些最新的前端技术,包括 Tauri、Vue3、Vite5、koa2 和 mysql。它提供了丰富的效率管理工具。
|
5月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue的甘肃“印象”网站的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的甘肃“印象”网站的详细设计和实现(源码+lw+部署文档+讲解等)
25 0
|
7月前
|
存储 缓存 C++
GGML 非官方中文文档(1)
GGML 非官方中文文档
319 1
|
7月前
|
存储 算法 异构计算
GGML 非官方中文文档(5)
GGML 非官方中文文档
130 1
|
6月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的英语学习交流平台的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的英语学习交流平台的详细设计和实现(源码+lw+部署文档+讲解等)