Home > Article > Web Front-end > A brief discussion on several options for nodejs to execute bash scripts
nodejsHow to execute bash script? This article will introduce to you several options for node to execute bash scripts.
Recently I am learning bash
script syntax, but if you are not familiar with bash syntax, It feels very error-prone, for example: Display undefined variables shell
The variables in are not defined and can still be used, but the results may not be what you expected. For example:
#!/bin/bash # 这里是判断变量var是否等于字符串abc,但是var这个变量并没有声明 if [ "$var" = "abc" ] then # 如果if判断里是true就在控制台打印 “ not abc” echo " not abc" else # 如果if判断里是false就在控制台打印 “ abc” echo " abc " fi
The result is that abc is printed, but the problem is that this script should report an error. The variable is not assigned a value, which is an error.
In order to make up for these errors, we learn to add at the beginning of the script: set -u
This command means that the script adds it to the head, and an error will be reported when encountering non-existent variables. , and stop execution.
When you run it again, you will be prompted: test.sh: 3: test.sh: num: parameter not set
Imagine again that you originally wanted to delete:rm -rf $dir /*
Then when dir is empty, what does it become? rm -rf
is a delete command. If $dir
is empty, it is equivalent to executing rm -rf /*
, which is to delete all files and folders. . . Then, your system will be gone. Is this the legendary deletion of the database and running away~~~~
If it is node
or browser environment, we will directly var = == 'abc'
will definitely report an error, which means that a lot of javascript programming experience cannot be reused in bash
. It would be great if it could be reused.
Later I started to explore, how great it would be if node
script was used instead of bash
. After a day of tossing, I gradually discovered an artifact, owned by Google
The zx
library, don’t worry, I won’t introduce this library first. Let’s first look at how the current mainstream node
is used to write bash
scripts, and you will know why. It's an artifact.
For example, exec
command## in the API of child_process
#
const { exec } = require("child_process"); exec("ls -la", (error, stdout, stderr) => { if (error) { console.log(`error: ${error.message}`); return; } if (stderr) { console.log(`stderr: ${stderr}`); return; } console.log(`stdout: ${stdout}`); });What needs to be noted here is that first
exec is asynchronous, but many of our
bash script commands are synchronous.
error object is different from
stderr.
errorWhen the
child_process module cannot execute the command, this object not null. For example, if searching for a file cannot find the file, the error object is not empty. However, if the command runs successfully and writes a message to the standard error stream, the
stderr object will not be empty.
exec command,
execSync
// 引入 exec 命令 from child_process 模块 const { execSync } = require("child_process"); // 同步创建了一个hello的文件夹 execSync("mkdir hello");Let’s briefly introduce the other APIs of child_process that can execute bash commands
[Recommended learning: "
nodejs Tutorialnode executes bash script: advanced solution shelljsconst shell = require('shelljs'); # 删除文件命令 shell.rm('-rf', 'out/Release'); // 拷贝文件命令 shell.cp('-R', 'stuff/', 'out/Release'); # 切换到lib目录,并且列出目录下到.js结尾到文件,并替换文件内容(sed -i 是替换文字命令) shell.cd('lib'); shell.ls('*.js').forEach(function (file) { shell.sed('-i', 'BUILD_VERSION', 'v0.1.2', file); shell.sed('-i', /^.*REMOVE_THIS_LINE.*$/, '', file); shell.sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, shell.cat('macro.js'), file); }); shell.cd('..'); # 除非另有说明,否则同步执行给定的命令。 在同步模式下,这将返回一个 ShellString #(与 ShellJS v0.6.x 兼容,它返回一个形式为 { code:..., stdout:..., stderr:... } 的对象)。 # 否则,这将返回子进程对象,并且回调接收参数(代码、标准输出、标准错误)。 if (shell.exec('git commit -am "Auto-commit"').code !== 0) { shell.echo('Error: Git commit failed'); shell.exit(1); }from From the above code, shelljs is really a very good solution for writing bash scripts in nodejs. If your node environment cannot be upgraded at will, I think shelljs is indeed enough.
Then let’s take a look at today’s protagonist zx. The start is already 17.4k.
zx libraryLet’s see how to use it first
#!/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}
What do you think? Are you just writing Linux commands? You can ignore a lot of bash syntax and just use js directly. Moreover, its advantages are not limited to these. There are some interesting features:
1. Support ts, and automatically compile .ts into .mjs files. The .mjs file is the end of the file that comes with higher versions of node and supports es6 module. That is, this file can directly import the module without escaping it with other tools
2. Comes with support for pipeline operation pipe method
3. Comes with fetch library, which can make network requests. Comes with chalk library, which can print colored fonts. Comes with error handling nothrow method. If the bash command makes an error, you can wrap it in this method and ignore the error
Complete Chinese document (the translation below is average, please forgive me)#!/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 is great, but in When writing scripts, people often choose a programming language that is more convenient. JavaScript is a perfect choice, but the standard Node.js library requires a few extra things before you can use it. zx is based on child_process , escaping arguments and providing sensible defaults.
Installation
npm i -g zx
Required environment
Node.js >= 14.8.0
将脚本写入扩展名为 .mjs
的文件中,以便能够在顶层使用await
。
将以下 shebang
添加到 zx
脚本的开头:
#!/usr/bin/env zx 现在您将能够像这样运行您的脚本: chmod +x ./script.mjs ./script.mjs
或者通过 zx
可执行文件:
zx ./script.mjs
所有函数($、cd、fetch 等)
都可以直接使用,无需任何导入。
使用 child_process
包中的 spawn 函数执行给定的字符串, 并返回 ProcessPromise.
let count = parseInt(await $`ls -1 | wc -l`) console.log(`Files count: ${count}`)
例如,要并行上传文件:
如果执行的程序返回非零退出代码,ProcessOutput
将被抛出
try { await $`exit 1` } catch (p) { console.log(`Exit code: ${p.exitCode}`) console.log(`Error: ${p.stderr}`) }
ProcessPromise,以下是promise typescript的接口定义
class ProcessPromise<T> extends Promise<T> { readonly stdin: Writable readonly stdout: Readable readonly stderr: Readable readonly exitCode: Promise<number> pipe(dest): ProcessPromise<T> }
pipe()
方法可用于重定向标准输出:
await $`cat file.txt`.pipe(process.stdout)
阅读更多的关于管道的信息:https://github.com/google/zx/blob/HEAD/examples/pipelines.md
ProcessOutput
的typescript
接口定义
class ProcessOutput { readonly stdout: string readonly stderr: string readonly exitCode: number toString(): string }
函数:
更改当前工作目录
cd('/tmp') await $`pwd` // outputs /tmp
node-fetch 包。
let resp = await fetch('http://wttr.in') if (resp.ok) { console.log(await resp.text()) }
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[] }
基于setTimeout 函数
await sleep(1000)
将 $ 的行为更改, 如果退出码不是0,不跑出异常.
ts接口定义
function nothrow<P>(p: P): P
await nothrow($`grep something from-file`) // 在管道内: await $`find ./examples -type f -print0` .pipe(nothrow($`xargs -0 grep something`)) .pipe($`wc -l`)
以下的包,无需导入,直接使用
console.log(chalk.blue('Hello world!'))
类似于如下的使用方式
import {promises as fs} from 'fs' let content = await fs.readFile('./package.json')
await $`cd ${os.homedir()} && mkdir example`
配置:
指定要用的bash.
$.shell = '/usr/bin/bash'
指定用于在命令替换期间转义特殊字符的函数
默认用的是 shq 包.
注意:
__filename & __dirname
这两个变量是在commonjs
中的。我们用的是.mjs
结尾的es6
模块。
在ESM
模块中,Node.js
不提供__filename
和 __dirname
全局变量。 由于此类全局变量在脚本中非常方便,因此 zx
提供了这些以在 .mjs
文件中使用(当使用 zx
可执行文件时)
require
也是commonjs
中的导入模块方法,
在 ESM
模块中,没有定义 require()
函数。zx
提供了 require()
函数,因此它可以与 .mjs
文件中的导入一起使用(当使用 zx
可执行文件时)
process.env.FOO = 'bar'await $`echo $FOO`
如果值数组作为参数传递给 $,数组的项目将被单独转义并通过空格连接 Example:
let files = [1,2,3]await $`tar cz ${files}`
可以通过显式导入来使用 $ 和其他函数
#!/usr/bin/env nodeimport {$} from 'zx'await $`date`复制代码
zx 可以将 .ts 脚本编译为 .mjs 并执行它们
zx examples/typescript.ts
更多编程相关知识,请访问:编程视频!!
The above is the detailed content of A brief discussion on several options for nodejs to execute bash scripts. For more information, please follow other related articles on the PHP Chinese website!