Мы видели, как создать Приветствие CLI в другом руководстве. Теперь мы расширим наши знания и создадим full-fledged
CLI, который будет частично клонировать Mac / Unix find
.
Для создания интерфейса командной строки необходимы следующие функции:
- Парсер аргументов входной команды
- Просматривать файлы и деревья каталогов
- Фильтровать файлы / каталог на основе аргументов
- Регистратор, лучшая информация для регистрации
1. Парсер аргументов входной команды
async function main(args: string[]) { console.log(args); } main(Deno.args);
Принимая аргументы в Deno
, это очень просто. У каждого процесса есть Deno.args
, который возвращает аргументы, переданные программе.
Выполнить:
deno run examples/minifind.ts param1 param2
Вывод:
[ “param1”, “param2” ]
Deno.args
возвращает массив строки, переданной программе (examples / minifind.ts).
Наш CLI ожидает такие параметры, как type
, name
и help
. Чтобы получить значение этих параметров. Нам нужно разобрать аргументы. В Deno есть flags
модуль, который помогает анализировать и собирать параметры. Добавим parser
.
import { parse } from "https://deno.land/std/flags/mod.ts"; async function main(args: string[]) { const { type, name, not, help, _: [dir = "."], } = parse(args); console.log({ type, name, not, help, dir, }); } main(Deno.args);
Выполнить:
deno run examples/minifind.ts --help --type=f --type=d --name=".*\.ts" examples
Вывод:
{ type: [ "f", "d" ], name: ".*\.ts", not: undefined, help: true, dir: "examples" }
Когда вы запустите программу с данным примером, вы увидите результат, как указано выше. Deno parse помогает собрать все аргументы.
Я использовал функцию деструктуризации ES6, чтобы присвоить значения по умолчанию.
Deno parse автоматически пытается собрать и объединить параметры на основе шаблонов. Любой аргумент, переданный с префиксом (-), считается аргументом со значением. Если вы не передаете значение рядом с ним. Он станет логическим.
Пример 1:
console.log(parse(["--test", "t"])); // { _: [], test: "t" } console.log(parse(["--test"])); // { _: [], test: true }
На что следует обратить внимание: если вы передаете аргумент с одним и тем же параметром более одного раза. parse
объедините их в array
. В приведенном выше примере type передается дважды. Вот почему type
имеет значение [ "f", "d" ]
.
Пример 2:
console.log(parse(["--test", "t", "--test", "t2"])); // { _: [], test: [ "t", "t2" ] }
подчеркивание (_
) здесь похоже на набор параметров отдыха. Если аргументы не соответствуют стандартному префиксу --
или -
. Все аргументы собраны в _
как массив данных. Мы извлекаем dir
как имя каталога из остальных _
.
Пример 3:
const { _ } = parse(["--test", "t", "examples"]); console.log(_); // _ == [ "examples" ] const [dir = "."] = _; console.log(dir); // examples
Подробнее читайте: https://deno.land/std/flags
2. Просматривайте файлы и деревья каталогов.
Поскольку теперь у нас есть проанализированные аргументы, давайте добавим логику чтения каталога.
Первое, что мы можем сделать, это разрешить path
или directory
, в которых нужно искать файлы. Мы можем использовать метод разрешения из модуля пути.
import { parse } from "https://deno.land/std/flags/mod.ts"; import { resolve } from "https://deno.land/std/path/mod.ts"; async function main(args: string[]) { const { type, name, not, help, _: [dir = "."], } = parse(args); const dirFullPath = resolve(Deno.cwd(), String(dir)); console.log(dirFullPath); } main(Deno.args);
Выполнить:
deno run -A examples/minifind.ts examples
Вывод:
/Users/xdeepakv/github/deno-by-example/examples
resolve
требуется --allow-read
разрешение. На данный момент я предоставил всем разрешение на передачу флага -A
. вы можете узнать больше о разрешениях
Deno.cwd()
используется для получения текущего рабочего пути. Нам пришлось преобразовать dir
в строку. Поскольку parse
может преобразовать его в string | number
в зависимости от типа ввода.
Чтение каталога может быть выполнено с помощью Deno.readDir
. Но мы обходим все дерево каталогов и файлов. Написание метода траверса может быть непростым делом. Вы можете попробовать сами.
Здесь я воспользуюсь функцией walk
из https://deno.land/std/fs/mod.ts.
import { parse } from "https://deno.land/std/flags/mod.ts"; import { resolve } from "https://deno.land/std/path/mod.ts"; import { walk } from "https://deno.land/std/fs/mod.ts"; async function main(args: string[]) { const { type, name, not, help, _: [dir = "."], } = parse(args); const dirFullPath = resolve(Deno.cwd(), String(dir)); for await (let entry of walk(dirFullPath)) { console.log(entry); } } main(Deno.args);
Выполнить:
deno run -A --unstable examples/minifind.ts examples
Вывод :
{ path: "/Users/xdeepakv/github/deno-by-example/examples/sample_employee.csv", name: "sample_employee.csv", isFile: true, isDirectory: false, isSymlink: false } { path: "/Users/xdeepakv/github/deno-by-example/examples/06_readfile_chunk.ts", name: "06_readfile_chunk.ts", isFile: true, isDirectory: false, isSymlink: false }
Поскольку walk
функция не является стабильной функцией. Мы должны использовать флаг --unstable
при запуске примера.
Функция Walk возвращает асинхронный генератор entries
. У каждой записи есть name
и path
вместе с другими флагами, такими как isDirectory
и isFile
.
Хорошо: самая сложная часть уже сделана. Теперь мы можем читать целые каталоги вместе с файлами в них.
3. Отфильтруйте файлы / каталог на основе аргументов.
Функция Walk принимает в качестве второго аргумента WalkOptions
. Мы можем использовать эту опцию, чтобы добавить нашу логику.
Интерфейс:
export interface WalkOptions { maxDepth?: number; includeFiles?: boolean; includeDirs?: boolean; followSymlinks?: boolean; exts?: string[]; match?: RegExp[]; s
Код:
// rest of the code async function main(args: string[]) { // rest of the code const dirFullPath = resolve(Deno.cwd(), String(dir)); let includeFiles = true; let includeDirs = true; let types = type ? (Array.isArray(type) ? type : [type]) : ["f", "d"]; if (!types.includes("f")) { includeFiles = false; } if (!types.includes("d")) { includeDirs = false; } const options = { maxDepth: 2, includeFiles, includeDirs, followSymlinks: false, skip: [/node_modules/g], }; for await (const entry of walk(dirFullPath, options)) { console.log(entry.path); } } main(Deno.args);
Выполнить:
deno run -A --unstable examples/minifind.ts examples
Вывод:
/Users/xdeepakv/github/deno-by-example/examples /Users/xdeepakv/github/deno-by-example/examples/subfolder /Users/xdeepakv/github/deno-by-example/examples/subfolder/dummy.ts
Тип по умолчанию будет включать как file
, так и dir
['f', 'd']. Пользователи могут передать флаг --type=f
и --type=d
, чтобы переопределить поведение.
Только Run- Dirs:
deno run -A --unstable examples/minifind.ts --type=d examples
Только файлы запуска:
deno run -A --unstable examples/minifind.ts --type=f examples
WalkOptions
поддерживает регулярное выражение для включения и исключения шаблонов. Мы можем использовать это для фильтрации записей по имени.
/// rest of the code async function main(args: string[]) { /// rest of the code let matchRegexps: RegExp[] | undefined = name ? (Array.isArray(name) ? name : [name]).map( (reg: string) => new RegExp(reg) ) : undefined; const options = { maxDepth: 2, includeFiles, includeDirs, followSymlinks: false, match: matchRegexps, skip: [/node_modules/g], }; for await (const entry of walk(dirFullPath, options)) { console.log(entry.path); } } main(Deno.args);
Выполнить - получить все имена файлов, в которых есть регистратор:
deno run -A --unstable examples/minifind.ts --type=f --name=”.*logger.*” examples
Теперь у нас работает minifind
. Шум!
4. Регистратор, лучшая информация для регистрации
/// rest of the code import { Logger } from "https://deno.land/x/deno_util/logger.ts"; const usesFormat = `Uses:\n\n minifind %s`; const logger = new Logger(); function printHelp(command: string) { logger.info(`Welcome to minifind [v%s]`, "1.0.0"); logger.warn(usesFormat, command); } async function main(args: string[]) { /// rest of the code if (help) { printHelp(`--type=f --name=".*logger.*" --help examples`); Deno.exit(0); } /// rest of the code for await (const entry of walk(dirFullPath, options)) { logger.inverse(entry.path); } } main(Deno.args);
Последний недостающий элемент - сообщить пользователю о вашем интерфейсе командной строки. Для этого мы добавили сообщения помощи для пользователей. Я использую созданные мной logger-util
. Подробнее читайте здесь https://deno.land/x/deno_util.
Беги с помощью:
deno run -A --unstable examples/minifind.ts --help
Вывод:
Выполнить с другими параметрами:
deno run -A --unstable examples/minifind.ts --help
Вывод:
ТаДа! 👏👏 Теперь вы знаете, как создать интерфейс командной строки.
Бонус:
Теперь у нас есть рабочий minifind
CLI. Однако нам пришлось использовать deno run
и filename
для выполнения команды, что не предназначено / выполнимо. Deno предоставляет install
команду. Используя это, мы можем преобразовать любую программу в executable
инструмент.
Давайте преобразуем наш мини-поиск в executable
. Все очень просто.
deno install -f --allow-read --unstable examples/minifind.ts
После того, как вы запустите указанную выше команду, вы увидите следующий результат:
Add /Users/xdeepakv/.deno/bin to PATH export PATH="/Users/xdeepakv/.deno/bin:$PATH"
Если вы это видите, просто добавьте export PATH="/Users/xdeepakv/.deno/bin:$PATH"
эту строку в свой .bashrc
или .bash_profile
(в зависимости от типа вашей ОС). Как только вы добавите .deno/bin
в PATH. Откройте новый терминал и попробуйте следующую команду.
minifind --type=f --name=".*logger.*" examples
Теперь ваш minifind готов к использованию в рабочей среде (CLI). :-)
Надеюсь, вам понравился этот урок. Пожалуйста, ознакомьтесь с некоторыми другими руководствами на нашем веб-сайте: https://deepakshrma.github.io/deno-by-example/
Все рабочие примеры можно найти в моем Github: https://github.com/deepakshrma/deno-by-example/tree/master/examples