предисловие
недавно учусь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: параметр не установлен
Представьте еще раз, что вы изначально хотели удалить:rm -rf $dir/*
Тогда, когда каталог пуст, что становится?rm -rf
это команда удаления,$dir
Если он пуст, это эквивалентно выполнениюrm -rf /*
, то есть удалить все файлы и папки. . . Значит, ваша система пропала, это легендарное удаление библиотеки?~~~
еслиnode
Или среду браузера, мы напрямуюvar === 'abc'
Он обязательно сообщит об ошибке, а это означает, что большой опыт программирования на javascript нельзя использовать повторно.bash
Да ладно, было бы здорово, если бы его можно было использовать повторно.
Позже я начал исследовать, если вы используетеnode
сценарий вместо этогоbash
Как хорошо, после суток метания и метания я постепенно нашел артефакт,Google
этоzx
Библиотека, не волнуйтесь, я не буду сначала представлять эту библиотеку, давайте посмотрим на текущее массовое использование.node
как писатьbash
Скрипт, вы знаете, почему это артефакт.
node выполнить скрипт bash: неохотное решение: API child_process
Напримерchild_process
внутри APIexec
Заказ
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
Когда модуль не может выполнить команду, объект не является нулевым. Например, поиск файла не может найти файл, объект ошибки не пуст. Однако, если команда выполняется успешно и записывает сообщения в стандартный поток ошибок,stderr
Объект не будет нулевым.
Конечно, мы можем использовать синхронныйexec
Заказ,execSync
// 引入 exec 命令 from child_process 模块
const { execSync } = require("child_process");
// 同步创建了一个hello的文件夹
execSync("mkdir hello");
Давайте кратко представим другие API-интерфейсы child_process, которые могут выполнять команды bash.
- spawn: запустить дочерний процесс для выполнения команды
- exec: запускает дочерний процесс для выполнения команд.В отличие от spawn, он имеет функцию обратного вызова, чтобы узнать статус дочернего процесса.
- execFile: запустить дочерний процесс для выполнения исполняемого файла
- fork: похож на spawn, разница в том, что он должен указать файл javascript, который должен выполнить дочерний процесс.
Разница между exec и ececFile в том, что exec подходит для выполнения команд, а eexecFile подходит для выполнения файлов.
Node выполняет bash-скрипты: передовые решения shelljs
const 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);
}
Судя по приведенному выше коду, shelljs действительно является очень хорошим решением для nodejs для написания сценариев bash.Если ваша среда узла не может быть случайно обновлена, я думаю, что shelljs действительно достаточно.
Тогда давайте взглянем на сегодняшнюю протагонистку zx, старт уже 17,4к.
zx-библиотека
Официальный сайт:www.npmjs.com/package/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}
Как вы думаете? Вы просто пишете команды для Linux? Вы можете игнорировать большую часть синтаксиса bash и просто использовать js напрямую, и его преимущества не ограничиваются этим. Есть несколько интересных особенностей:
1. Поддержка ts, автоматическая компиляция .ts в файлы .mjs.Файл .mjs является концом файла, который поддерживает модуль es6, который поставляется с более высокой версией узла.То есть этот файл может напрямую импортировать модуль без экранирования другие инструменты.
2. Встроенная поддержка метода трубопровода трубопровода
3. У него есть собственная библиотека выборки, которая может делать сетевые запросы, собственная библиотека мела, которая может печатать цветные шрифты, и собственный метод обработки ошибок nothrow.Если команда bash делает ошибку, ее можно обернуть в этот метод, чтобы игнорировать ошибку
Полный китайский документ (уровень перевода ниже средний, пожалуйста, простите меня)
#!/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.js >= 14.8.0
Напишите скрипт с расширением.mjs
файл, который можно использовать на верхнем уровнеawait
.
поставить следующееshebang
добавить вzx
Начало скрипта:
#!/usr/bin/env zx
现在您将能够像这样运行您的脚本:
chmod +x ./script.mjs
./script.mjs
или черезzx
запускаемый файл:
zx ./script.mjs
все функции($、cd、fetch 等)
можно использовать напрямую без импорта.
$`command`
использовать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, ниже приведено определение интерфейса машинописного текста обещания.
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)
Подробнее о трубопроводахGitHub.com/Google/these/no…
ProcessOutput
изtypescript
Определение интерфейса
class ProcessOutput {
readonly stdout: string
readonly stderr: string
readonly exitCode: number
toString(): string
}
функция:
cd()
Изменить текущий рабочий каталог
cd('/tmp')
await $`pwd` // outputs /tmp
fetch()
пакет извлечения узла.
let resp = await fetch('http://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()
Измените поведение $, чтобы не создавать исключение, если код выхода не равен 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`)
Следующие пакеты не нужно импортировать, их можно использовать напрямую.
chalk
console.log(chalk.blue('Hello world!'))
fs
Подобно следующему использованию
import {promises as fs} from 'fs'
let content = await fs.readFile('./package.json')
os
await $`cd ${os.homedir()} && mkdir example`
Конфигурация:
$.shell
Указывает используемый bash.
$.shell = '/usr/bin/bash'
$.quote
Указывает функцию, используемую для экранирования специальных символов во время подстановки команд.
Пакет 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`
передать массив
Если массив значений передается в качестве аргумента $, элементы массива будут экранированы по отдельности и соединены пробелами. Пример:
let files = [1,2,3]
await $`tar cz ${files}`
$ и другие функции могут использоваться с явным импортом
#!/usr/bin/env node
import {$} from 'zx'
await $`date`
zx может компилировать сценарии .ts в .mjs и выполнять их
zx examples/typescript.ts