nodejsWie führe ich ein Bash-Skript aus? In diesem Artikel werden Ihnen verschiedene Optionen für den Knoten zum Ausführen von Bash-Skripten vorgestellt.
Ich habe kürzlich die Skriptsyntax von bash
gelernt, aber wenn Sie mit der Bash-Syntax nicht vertraut sind, ist es sehr leicht, Fehler zu machen, zum Beispiel beim Anzeigen undefinierter Variablen shell Die Variablen in
sind nicht definiert und können weiterhin verwendet werden, aber die Ergebnisse entsprechen möglicherweise nicht Ihren Erwartungen. Beispiel: bash
脚本语法,但是如果对bash语法不是熟手的话,感觉非常容易出错,比如说:显示未定义的变量shell
中变量没有定义,仍然是可以使用的,但是它的结果可能不是你所预期的。举个例子:
#!/bin/bash # 这里是判断变量var是否等于字符串abc,但是var这个变量并没有声明 if [ "$var" = "abc" ] then # 如果if判断里是true就在控制台打印 “ not abc” echo " not abc" else # 如果if判断里是false就在控制台打印 “ abc” echo " abc " fi
结果是打印了abc,但问题是,这个脚本应该报错啊,变量并没有赋值算是错误吧。
为了弥补这些错误,我们学会在脚本开头加入:set -u
这句命令的意思是脚本在头部加上它,遇到不存在的变量就会报错,并停止执行。
再次运行就会提示:test.sh: 3: test.sh: num: parameter not set
再想象一下,你本来想删除:rm -rf $dir/*
然后dir是空的时候,变成了什么?rm -rf
是删除命令,$dir
是空的话,相当于执行 rm -rf /*
,这是删除所有文件和文件夹。。。然后,你的系统就没了,这就是传说中的删库跑路吗~~~~
如果是node
或者浏览器环境,我们直接var === 'abc'
肯定是会报错的,也就是说很多javascript编程经验无法复用到bash
来,如果能复用的话,该多好啊。
后来就开始探索,如果用node
脚本代替bash
该多好啊,经过一天折腾逐渐发现一个神器,Google
旗下的zx
库,先别着急,我先不介绍这个库,我们先看看目前主流用node
如何编写bash
脚本,就知道为啥它是神器了。
例如 child_process
的API里面exec
命令
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}`); });
这里需要注意的是,首先exec
是异步的,但是我们bash
脚本命令很多都是同步的。
而且注意:error
对象不同于stderr
. error
当child_process
模块无法执行命令时,该对象不为空。例如,查找一个文件找不到该文件,则error对象不为空。但是,如果命令成功运行并将消息写入标准错误流,则该stderr
对象不会为空。
当然我们可以使用同步的exec
命令,execSync
// 引入 exec 命令 from child_process 模块 const { execSync } = require("child_process"); // 同步创建了一个hello的文件夹 execSync("mkdir hello");
set -u
. Dieser Befehl bedeutet, dass das Skript ihn am Kopf hinzufügt und einen Fehler meldet Wenn Sie auf nicht vorhandene Variablen stoßen, stoppen Sie die Implementierung. rm -rf $dir/*
und dann dir Was wird daraus, wenn es leer ist? rm -rf
ist ein Löschbefehl. Wenn $dir
leer ist, entspricht dies der Ausführung von rm -rf /*
, der alle Dateien löscht und Dateien-Ordner. . . Dann ist Ihr System verschwunden. Ist dies das legendäre Löschen der Bibliothek und das Weglaufen? abc' wird definitiv einen Fehler melden, was bedeutet, dass ein Großteil der JavaScript-Programmiererfahrung nicht in bash
wiederverwendet werden kann. node
-Skript anstelle von bash
verwenden würde. Nach einem Tag harter Arbeit entdeckte ich nach und nach ein Artefakt, die der zx
-Bibliothek gehört, keine Sorge, ich werde diese Bibliothek zunächst nicht vorstellen. code>-Skripte mit node
, was derzeit der Mainstream ist, und Sie werden wissen, warum es ein Artefakt ist. Zum Beispiel der Befehl exec
in der API von child_process</ code></p> <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">const shell = require(&#39;shelljs&#39;);
# 删除文件命令
shell.rm(&#39;-rf&#39;, &#39;out/Release&#39;);
// 拷贝文件命令
shell.cp(&#39;-R&#39;, &#39;stuff/&#39;, &#39;out/Release&#39;);
# 切换到lib目录,并且列出目录下到.js结尾到文件,并替换文件内容(sed -i 是替换文字命令)
shell.cd(&#39;lib&#39;);
shell.ls(&#39;*.js&#39;).forEach(function (file) {
shell.sed(&#39;-i&#39;, &#39;BUILD_VERSION&#39;, &#39;v0.1.2&#39;, file);
shell.sed(&#39;-i&#39;, /^.*REMOVE_THIS_LINE.*$/, &#39;&#39;, file);
shell.sed(&#39;-i&#39;, /.*REPLACE_LINE_WITH_MACRO.*\n/, shell.cat(&#39;macro.js&#39;), file);
});
shell.cd(&#39;..&#39;);
# 除非另有说明,否则同步执行给定的命令。 在同步模式下,这将返回一个 ShellString
#(与 ShellJS v0.6.x 兼容,它返回一个形式为 { code:..., stdout:..., stderr:... } 的对象)。
# 否则,这将返回子进程对象,并且回调接收参数(代码、标准输出、标准错误)。
if (shell.exec(&#39;git commit -am "Auto-commit"&#39;).code !== 0) {
shell.echo(&#39;Error: Git commit failed&#39;);
shell.exit(1);
}</pre><div class="contentsignin">Nach dem Login kopieren</div></div><p>Hier muss beachtet werden, dass <code>exec
zunächst asynchron ist, viele unserer bash
-Skriptbefehle jedoch synchron sind. Und beachten Sie: Das error
-Objekt unterscheidet sich von stderr
Wenn das child_process
-Modul den Befehl nicht ausführen kann , dieses Objekt Nicht leer. Wenn beispielsweise bei der Suche nach einer Datei die Datei nicht gefunden werden kann, ist das Fehlerobjekt nicht leer. Wenn der Befehl jedoch erfolgreich ausgeführt wird und eine Meldung in den Standardfehlerstrom schreibt, ist das Objekt stderr
nicht leer.
exec
-Befehl execSync
verwenden. #!/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}
nodejs-Tutorial
“】Knotenausführungs-Bash-Skript: Erweiterte Lösung für Shelljs
#!/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}
npm i -g zx
Node.js >= 14.8.0
#!/usr/bin/env zx 现在您将能够像这样运行您的脚本: chmod +x ./script.mjs ./script.mjs
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
更多编程相关知识,请访问:编程视频!!
Das obige ist der detaillierte Inhalt vonEine kurze Diskussion über verschiedene Optionen für NodeJS zum Ausführen von Bash-Skripten. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!