background

During this period, I was responsible for the production of project deployment upgrade package, so I have learned shell script writing, but I am not familiar with shell script writing. So I wanted to use a JS method to write shells, so I came across ZX.

Bash is great, but when it comes to writing scripts, people usually choose a more convenient programming language. JavaScript is a perfect choice, but standard Node.js library requires additional hassle before using. The zx package provides useful wrappers around child_process, escapes arguments and gives sensible defaults.

Bash is great, but when it comes to scripting, people often choose a more convenient programming language. JavaScript is a perfect choice, but the standard Node.js library requires extra hassle before it can be used. The ZX package provides a useful wrapper around child_process, escaping parameters and providing reasonable defaults.

// shell.js const shell = require('shelljs'); Rm ('-rf', 'out/Release'); / / copy files command shell. Cp (' -r ', 'stuff/',' the out/Release); # switch to lib and list the.js directory to the end of the file and replace the contents of the file (sed -i is to replace the text command) 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); }); Unless otherwise specified, execute the given command synchronously. In synchronous mode, this returns a ShellString # (compatible with ShellJS V0.6. x, which returns a ShellString of the form {code:... , stdout:... , stderr:... } object. Otherwise, this returns the child process object and the callback receives parameters (code, standard output, standard error). if (shell.exec('git commit -am "Auto-commit"').code ! == 0) { shell.echo('Error: Git commit failed'); shell.exit(1); } // 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}Copy the code

Installation and use

NPM WARN notsup Unsupported engine for [email protected]: wanted: node.js >= 14.13.1 {" node ", "^ 12.20.0 | | ^ 14.13.1 | | > = 16.0.0"} / / because I version version of the node is an odd number of NPM zx/I - g/script file End can with the MJS/js / /. Js Void async function () {... Shell commander}() // Add #! /usr/bin/env zx // declaration script run environment Scripts start with env because script interpreters can be installed in different directories on Linux, and env can be found in the system's PATH directory. Env also specifies some system environment variables. Process. env // Run the script $chmod +x./script.mjs $./script.mjs # or use the.js command only to use zx./script.js $zx./script.js $zx ./script.tsCopy the code

The zX script bash syntax can be ignored in many ways and can be used to write js directly. It also has some interesting features:

1, support ts, automatic compilation. Ts for.mjs file,.mjs file is the end of the node high version of the support es6 module file, that is, this file directly import module line, no other tools escape

2, built-in support pipe operation method

3. Fetch library, which can make network requests, Chalk library, which can print colored fonts, and nothrow method for error handling. If bash command fails, it can be wrapped in this method to ignore the error and write the script into the file with extension.mjs, so as to use await at the top level.

// The general shell command $' command 'uses the spawn function in the child_process package to execute the given string and return ProcessPromise<ProcessOutput> for example: Const currenBranch = await $' git branch --show-current 'console.log(currenBranch, "current") $git branch --show-current dev // shell ProcessOutput {stdout: 'dev\n', // shell stderr: ", // error message exitCode: 0, // script returns status code // toString() // built-in toString method stdout stderr} current // console printCopy the code

Based on the information returned by ProcessOutput above, we can catch exceptions in the following way

Const a:string = "a" const a:string = "a" const a:string = "a" const a:string = "a" ProcessOutput object console.log(' Exit code: ${p.estitcode} ') console.log(' Error: ${p.stderr} ')}Copy the code
// CD Switch to working directory CD ('/ TMP '); // node-fetch packet initiates network request const res = await fetch('https://wttr.in'); if (res.ok) { console.log(await res.text()); } // Pull data is mainly implemented through scriptFromHttp, which includes the following steps: // Node-fetch is used to fetch data from the corresponding URL // The data is named after the PATHname of the URL // Create a file in a temporary folder // Question is used to wrap the readline package const env = await question('Choose a env: ', { choices: Object.keys(process.env), }); // In the second argument, you can specify an array of options for the TAB auto-completion // interface definition: function question(query? : string, options? : QuestionOptions): Promise<string> type QuestionOptions = { choices: String []} // Sleep await sleep(1000) based on setTimeout; Chalk console.log(chalk. Blue ('This is blue')); // fs-extra import {promises as fs} from 'fs' const content = await fs.readFile('package.json'); // OS await $' CD ${os.homedir()} && mkdir example '/ / Specify the bash $. Shell = '/usr/bin/bash' // pass the environment variable process.env.FOO = 'bar' await $' echo $FOO '// pass array let files = [1,2,3] await $' tar cz ${files}' // compile.ts scripts to.mjs and execute them zx examples/typescript.tsCopy the code

Example:

#! /usr/bin/env zx const UI_PATH = "/mnt/d/WorkEngine/ui"; const INSTALL_PATH = `/mnt/d/WorkEngine/deployment/install`; Void async function () {try {// Put something in a file that needs to be pre-configured, Let configData = await fs.readfile ('./ installconfig.json '); let config = JSON.parse(configData.toString()); if (config.UI.isUpdate) { buildUI(INSTALL_PATH); }} catch (p) {ProcessOutput console.log(' Exit code: ${p.exitCode}`); }} (); Async function buildUI(INSTALL_PATH) {try {CD (UI_PATH); Await $' PNPM I '// $' command' is an asynchronous command. Note that most of the time we need to await $' PNPM build '; $ `mv dist/ ${INSTALL_PATH}`; } catch (p) { console.log(`Exit code: ${p.exitCode}`); }} // docker package async function buildBff(INSTALL_PATH, bffConfig) {CD (BFF_PATH); let currentBranch = await $ `git symbolic-ref --short -q HEAD`; / / $` ` returned data with \ n line breaks, so want to use a newline need to be removed currentBranch = currentBranch. Stdout. Replace (\ n/g, "); if (bffConfig.configUpdate) { $ `cp configs/config_release.toml ${INSTALL_PATH}/bff/config.toml`; } if (bffConfig.mappingUpdate){ $`cp es/mapping.json ${INSTALL_PATH}/bff/` } const GO_PATH = process.env.GOPATH; $' ${GO_PATH}/bin/swag init -g CMD /main.go '; await $ `go mod tidy`; await $ `go mod vendor`; await $ `docker build --network="host" --build-arg branch=${currentBranch} --tag ${bffConfig.HARBOR_IP}/bff:${bffConfig.VERSION} .`; await $ `docker save -o bff.tar ${bffConfig.HARBOR_IP}/bff:${bffConfig.VERSION}`; $ `mv bff.tar ${INSTALL_PATH}/`; } // installConfig.json { "UI": { "isUpdate": true }, "bff": { "isUpdate": true, "configUpdate": true, "mappingUpdate": True, "HARBOR_IP" : "update.zoomeye.org", "VERSION" : "2.5.0}}"Copy the code

Reference: Google Scripting tool zX tutorial

Nodejs bash script ultimate solution!