Рубрики
Без рубрики

Написание приложений командной строки в Nodejs

Питер Бенджамин написание приложений командной строки в Nodejswith правильные пакеты, запись приложений командной строки в Nodejs – это ветер. Один из них, в частности, делает его чрезвычайно простым: командир. Давайте установим сцену и прогулку, как написать приложение интерфейса командной строки (CLI) в Nodejs с командиром. Наша цель буду

Автор оригинала: FreeCodeCamp Community Member.

Питер Бенджамин

Благодаря правильным пакетам, запись приложений командной строки в Nodejs – это бриз.

Один из пакетов, в частности, делает его чрезвычайно легко: Командир Отказ

Давайте установим сцену и прогулку, как написать приложение интерфейса командной строки (CLI) в Nodejs с командиром. Наша цель будет написать приложение CLI для списка файлов и каталогов.

Настраивать

Иды Я люблю онлайн удостоверение личности. Они абстрагируют много головных болей, когда речь идет о настройке среды Dev. Я лично использую Cloud9 по следующим причинам:

  • Макет интуитивно понятен
  • Редактор красивый и простой в использовании
  • Ресурсы свободных уровней были недавно были увеличены до 1 ГБ ОЗУ и дискового пространства 5 ГБ, что более чем достаточно для приличного приложения для приличного размера.
  • Неограниченное количество рабочих станций
  • Это идеальная среда для тестирования или эксперимента с новыми проектами/идеями, не опасаясь нарушения вашей среды

Узел/NPM версия На момент написания этой статьи узел находится в версии 5.3.0 и NPM – версия AD 3.3.12.

Инициализация

Начнем с инициализации нашего проекта, примите все NPM по умолчанию, а установка командир упаковка:

npm initnpm i --save commander

В результате чего:

Примечание:

  • Вам придется добавить Bin Вручную, который рассказывает Nodejs, как называется ваше приложение CLI и какова точка записи в ваше приложение.
  • Убедитесь, что вы не используете имя команды, которое уже существует в вашей системе.

Index.js.

Теперь, когда мы инициализировали наш проект и указали, что наша точка входа – index.js, давайте создадим index.js:

touch index.js

Теперь для фактической части кодировки:

Как правило, при выполнении файлов NODEJS мы скажем системе использовать соответствующий интерпретатор с помощью префикса Узел перед файлом. Однако мы хотим иметь возможность выполнить наше приложение CLI глобально из любой точки системы, и без необходимости указывать переводчик узла каждый раз.

Поэтому наша первая строка – Shebang выражение:

#!/usr/bin/env node

Это не только говорит нашей системе использовать соответствующий переводчик, но он также говорит о нашей системе использовать соответствующую Версия переводчика.

Отсюда, мы пишем чистый код JavaScript. Так как я буду писать в соответствии с ES6-совместимым кодом, начну с буквального выражения:

'use strict';

Это говорит компилятору использовать более строгий вариант JavaScript [ 1 ] И позволяет нам написать код ES6 на Cloud9.

Давайте начнем с нужды командир упаковка:

const program = require('commander');

Теперь, написание приложений CLI с командир это Просто, и документация отличная, но я боролся с несколькими понятиями, которые я буду пытаться прояснить здесь.

Кажется, что 2 дизайна для приложений CLI. Возьми обман и Гит Например.

С обман , вы проходите один или несколько флагов:

  • ls -l.
  • ОДНАЯ

С Гит Вы проходите под командования, но у вас также есть несколько флагов:

  • Git Compity -AM GE>
  • Git Remote Добавить начало RL>

Мы рассмотрим гибкость Командир Дает нам разработать оба типа интерфейсов командной строки.

Commander API

Командир API прямо вперед, и документация отличная.

Есть 3 основных способа, мы можем написать нашу программу:

Метод № 1: Флаг – только приложение командной строки

const program = require('commander');
program  .version('0.0.1')  .option('-o, --option','option description')  .option('-m, --more','we can have as many options as we want')  .option('-i, --input [optional]','optional user input')  .option('-I, --another-input ','required user input')  .parse(process.argv); // end with parse to parse through the input

Примечание:

  • Краткие и длинные варианты находятся в одной строке (см. Смелый текст на изображении выше)
  • -o и вернется логический Значения, когда пользователи передают их, потому что мы не указывали ?| по желанию или Требуется пользовательский ввод. -i
  • иЗахватите пользовательский ввод и передайте значения в наше приложение CLI. Любое значение, заключенное в квадратные кронштейны (например, []), считается необязательным. Пользователь может или не может предоставить значение.
  • Считается любое значение, заключенное в угловые кронштейны (E.g. <>). Пользователь должен предоставить значение.

Пример выше позволяет нам реализовать подход только к флагам к нашему приложению CLI. Ожидается, что пользователи будут взаимодействовать с нашим приложением, как так:

//Examples:$ cli-app -om -I hello$ cli-app --option -i optionalValue -I requiredValue

Способ № 2: подкоманда и Флаг на основе командной строки приложения

const program = require('commander');
program  .version('0.0.1')  .command('command  [optional]')  .description('command description')  .option('-o, --option','we can still have add'l options')  .action(function(req,optional){    console.log('.action() allows us to implement the command');    console.log('User passed %s', req);    if (optional) {      optional.forEach(function(opt){        console.log("User passed optional arguments: %s", opt);      });    }  });
program.parse(process.argv); // notice that we have to parse in a new statement.

Примечание:

  • Если мы используем .command («команда …»). Описание («Описание …») мы должны использовать .кция () Чтобы пройти функцию и выполнить наш код на основе ввода пользователя. (Я указываю на это, потому что есть альтернативный метод для использования .Command () Что мы рассмотрим дальше.)
  • Если мы используем .command («команда …») , мы больше не можем просто прикрепить .ary (process.argv) В конце концов, в предыдущем примере. Мы должны пройти Разбор () в новом заявлении

Ожидается, что пользователи будут взаимодействовать с нашим приложением CLI, как так:

//Example: $ cli-app command requiredValue -o

Метод № 3: То же, что и метод № 2 выше, но позволяет модулировать код

Наконец, нам не нужно раздувать наш файл JavaScript со всеми .command (). Описание (). Действие () логика. Мы можем модулировать наш проект CLI, как так:

// file: ./cli-app/index.jsconst program = require('commander');
program.version('0.0.1').command('command  [optional]','command description').command('command2','command2 description').command('command3','command3 description').parse(process.argv);

Примечание:

  • Если мы используем .Command («команда», «Описание») Передать в команду и описание, мы больше не можем иметь .действие(). Командир будет подразумевать, что у нас есть отдельные файлы с определенной конвенцией именования, где мы можем обрабатывать каждую команду. Конвенция об именах является index-command.js. , index-command2.js , index-command3.js. . Смотрите примеры этого на Github (конкретно: вечера , PM-install , PM-Publish файлы).
  • Если мы возьмем этот маршрут, мы можем просто придерживаться .ary () в конце.

Вернуться к нашему сценарию проекта …

Теперь, когда мы охватывали основы, это все вниз отсюда. Мы можем глубоко вздохнуть.

*** ВЗДОХ ***

Хорошо, теперь начинается веселье.

Если мы вспомним наш сценарий проекта, мы хотим написать приложение CLI для списка файлов и каталогов. Итак, начнем написать код.

Мы хотим дать пользователю возможность решить, хочет ли они видеть «все» файлы (включая скрытые) и/или если они хотят видеть формат длинного списка (включая права/разрешения файлов/папок).

Итак, мы начинаем с написания основной оболочки нашей программы, чтобы увидеть наш инкрементный прогресс (мы следим за подписью Метод № 2 для демонстрации):

#!/usr/bin/env node'use strict';
const program = require('commander');
program  .version('')  .command('')  .description('')  .option('','')  .option('','')  .action('');
program.parse(process.argv);

Давайте начнем заполнять пробелы:

#!/usr/bin/env node'use strict';
const program = require('commander');
program  .version('0.0.1')  .command('list [directory]')  .description('List files and folders')  .option('-a, --all','List all files and folders')  .option('-l, --long','')  .action();
program.parse(process.argv);

Примечание:

  • Мы решили назвать нашу команду список .
  • Каталог Аргумент не является обязательным, поэтому пользователь может просто игнорировать, чтобы пройти каталог, в этом случае мы будем перечислены файлы/папки в текущем каталоге.

Итак, в нашем сценарии действительны следующие звонки:

$ cli-app list $ cli-app list -al$ cli-app list --all$ cli-app list --long$ cli-app list /home/user -al

Теперь давайте начнем написать код для нашего .кция () Отказ

#!/usr/bin/env node'use strict';
const program = require('commander');
let listFunction = (directory,options) => {  //some code here}
program  .version('0.0.1')  ...  .action(listFunction);
program.parse(process.argv);

Мы собираемся обманывать здесь, используя встроенный Ls Команда, которая доступна во всех Unix-подобных операционным системам.

#!/usr/bin/env node'use strict';
const program = require('commander'),      exec = require('child_process').exec;
let listFunction = (directory,options) => {const cmd = 'ls';let params = [];if (options.all) params.push('a');if (options.long) params.push('l');let fullCommand = params.length                   ? cmd + ' -' + params.join('')                  : cmdif (directory) fullCommand += ' ' + directory;
};
program  .version('0.0.1')  ...  .action(listFunction);
program.parse(process.argv);

Давайте поговорим об этом коде.

  1. Во-первых, нам требуется Child_Process Библиотека для выполнения команд Shell * ( * Это открывает большой риск безопасности, который я буду обсуждать позже )
  2. Мы объявляем постоянную переменную, которая удерживает корню нашей команды
  3. Мы объявляем массив, который будет проводить какие-либо параметры, переданные пользователем (например, -a , -l )
  4. Мы проверяем, пройдет ли пользователь короткой рук ( -A ) или длинные флага (- все ). Если так, то Опции. Все и/или Опции. Пожалуйста, будет оценивать правда , в этом случае мы будем толкать соответствующую командный флаг в наш массив. Наша цель – создать команду оболочки, которую мы пройдем позже Child_Process Отказ
  5. Мы объявляем новую переменную для хранения команды полной оболочки. Если массив параметра содержит какие-либо флаги, мы объединяем флаги друг к другу и в корневую команду. В противном случае, если параметр Array пуст, то мы используем корневую команду как есть.
  6. Наконец, мы проверяем, пройдет ли пользователь любой необязательными каталог ценности. Если это так, мы объединяем его в полностью построенную команду.

Теперь мы хотим выполнить полностью построенную команду в оболочке. Child_process.exec () дает нам возможность сделать это и Nodejs Docs Дайте нам подпись:

child_process.exec(command, callback(error, stdout, stderr){  //"error" will be returned if exec encountered an error.  //"stdout" will be returned if exec is successful and data is returned.  //"stderr" will be returned if the shell command encountered an error.})

Итак, давайте будем использовать это:

#!/usr/bin/env node'use strict';
const program = require('commander'),      exec = require('child_process').exec;
let listFunction = (directory,options) => {  const cmd = 'ls';  let params = [];  if (options.all) params.push('a');  if (options.long) params.push('l');  let fullCommand = params.length                   ? cmd + ' -' + params.join('')                  : cmd  if (directory) fullCommand += ' ' + directory;
  let execCallback = (error, stdout, stderr) => {    if (error) console.log("exec error: " + error);    if (stdout) console.log("Result: " + stdout);    if (stderr) console.log("shell error: " + stderr);  };
  exec(fullCommand, execCallback);
};
program  .version('0.0.1')  ...  .action(listFunction);
program.parse(process.argv);

Вот и все!

Вот цик моего приложения CLI CLI Отказ

Конечно, мы можем добавить несколько ничути, как:

  • Раскраска вывода (я использую Мел Библиотека ниже)
  • Современные приложения CLI достаточно умны, чтобы показать текст справки, когда пользователь вызывает программу без параметров или аргументов (много как Git ), поэтому я добавил эту функциональность в самом нижней части.
#!/usr/bin/env node'use strict';/** * Require dependencies * */const program = require('commander'),    chalk = require("chalk"),    exec = require('child_process').exec,    pkg = require('./package.json');/** * list function definition * */let list = (directory,options)  => {    const cmd = 'ls';    let params = [];        if (options.all) params.push("a");    if (options.long) params.push("l");    let parameterizedCommand = params.length                                 ? cmd + ' -' + params.join('')                                 : cmd ;    if (directory) parameterizedCommand += ' ' + directory ;        let output = (error, stdout, stderr) => {        if (error) console.log(chalk.red.bold.underline("exec error:") + error);        if (stdout) console.log(chalk.green.bold.underline("Result:") + stdout);        if (stderr) console.log(chalk.red("Error: ") + stderr);    };        exec(parameterizedCommand,output);    };program    .version(pkg.version)    .command('list [directory]')    .option('-a, --all', 'List all')    .option('-l, --long','Long list format')    .action(list);program.parse(process.argv);// if program was called with no arguments, show help.if (program.args.length === 0) program.help();

Наконец, мы можем воспользоваться NPM к символической ссылке нашего приложения CLI, чтобы мы могли использовать его во всем мире в нашей системе. Просто, в терминале CD в корню нашего приложения CLI и типа:

npm link

Окончательные мысли и соображения

Код в этом проекте ни в коем случае не является лучшим кодом. Я полностью осознаю, что всегда есть комната для улучшения, поэтому обратная связь приветствуется!

Кроме того, я хочу указать на недостаток безопасности в нашем приложении. Наш код не Санатифицировать или проверить вход пользователей. Это нарушает лучшие практики безопасности. Рассмотрим следующие сценарии, где пользователи могут пройти от un-желаемого входа:

$ cli-app -al ; rm -rf /$ cli-app -al ; :(){ :|: & };:

Если вы хотите написать какой-нибудь код, который обрабатывает эту проблему или исправляет любые другие потенциальные проблемы, обязательно покажите нам свой код, оставив комментарий.

Оригинал: “https://www.freecodecamp.org/news/writing-command-line-applications-in-nodejs-2cf8327eee2/”