Как открыть терминальное приложение из node.js? - PullRequest
23 голосов
/ 03 февраля 2012

Я хотел бы иметь возможность открыть Vim из программы node.js, запущенной в терминале, создать некоторое содержимое, сохранить и выйти из Vim, а затем получить содержимое файла.

Я пытаюсь сделать что-то вроде этого:

filename = '/tmp/tmpfile-' + process.pid

editor = process.env['EDITOR'] ? 'vi'
spawn editor, [filename], (err, stdout, stderr) ->

  text = fs.readFileSync filename
  console.log text

Однако, когда он запускается, он просто вешает терминал.

Я также попробовал это с exec и получилтот же результат.

Обновление:

Это осложняется тем фактом, что этот процесс запускается из команды, набранной по приглашению с запущенной readline ,Я полностью извлек соответствующие части моей последней версии в файл.Вот он полностью:

{spawn} = require 'child_process'
fs = require 'fs'
tty = require 'tty'
rl = require 'readline'

cli = rl.createInterface process.stdin, process.stdout, null
cli.prompt()

filename = '/tmp/tmpfile-' + process.pid

proc = spawn 'vim', [filename]

#cli.pause()
process.stdin.resume()

indata = (c) ->
    proc.stdin.write c
process.stdin.on 'data', indata

proc.stdout.on 'data', (c) ->
    process.stdout.write c

proc.on 'exit', () ->
    tty.setRawMode false
    process.stdin.removeListener 'data', indata

    # Grab content from the temporary file and display it
    text = fs.readFile filename, (err, data) ->
        throw err if err?  
        console.log data.toString()

        # Try to resume readline prompt
        cli.prompt()

Способ, который работает, как показано выше, заключается в том, что он показывает приглашение в течение нескольких секунд, а затем запускает Vim, но TTY не работает.Я могу отредактировать и сохранить файл, и содержимое будет напечатано правильно.При выходе на терминал также выводится много мусора, и функциональность Readline впоследствии нарушается (без стрелок вверх / вниз, без завершения табуляции).

Если я раскомментирую строку cli.pause(), то TTYВ Vim все в порядке, но я застрял в режиме вставки, и клавиша Esc не работает.Если я нажму Ctrl-C, он завершит дочерний и родительский процесс.

Ответы [ 2 ]

40 голосов
/ 14 июня 2013

Просто наследуйте stdio от основного процесса.

var editor = process.env.EDITOR || 'vi';

var child = child_process.spawn(editor, ['/tmp/somefile.txt'], {
    stdio: 'inherit'
});

child.on('exit', function (e, code) {
    console.log("finished");
});

больше опций: http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

11 голосов
/ 03 февраля 2012

Обновление: Мой ответ применялся во время его создания, но для современных версий Node посмотрите этот другой ответ .

Прежде всего, вашиспользование spawn не правильно.Вот документы.http://nodejs.org/docs/latest/api/child_processes.html#child_process.spawn

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

В этом случае вам нужновзять данные из stdin и отправить их в vim, и вам нужно взять вывод данных через vim и установить его на свой терминал, иначе вы ничего не увидите.Вам также необходимо установить tty в сырой режим, иначе узел будет перехватывать некоторые последовательности клавиш, поэтому vim не будет работать должным образом.

Далее, не делайте readFileSync.Если вы натолкнетесь на случай, когда считаете, что вам нужно использовать метод синхронизации, то, скорее всего, вы делаете что-то не так.

Вот краткий пример, который я собрал.Я не могу поручиться за то, чтобы это работало в каждом отдельном случае, но оно должно охватывать большинство случаев.

var tty = require('tty');
var child_process = require('child_process');
var fs = require('fs');

function spawnVim(file, cb) {
  var vim = child_process.spawn( 'vim', [file])

  function indata(c) {
    vim.stdin.write(c);
  }
  function outdata(c) {
    process.stdout.write(c);
  }

  process.stdin.resume();
  process.stdin.on('data', indata);
  vim.stdout.on('data', outdata);
  tty.setRawMode(true);

  vim.on('exit', function(code) {
    tty.setRawMode(false);
    process.stdin.pause();
    process.stdin.removeListener('data', indata);
    vim.stdout.removeListener('data', outdata);

    cb(code);
  });
}

var filename = '/tmp/somefile.txt';

spawnVim(filename, function(code) {
  if (code == 0) {
    fs.readFile(filename, function(err, data) {
      if (!err) {
        console.log(data.toString());
      }
    });
  }
});

Обновление

Я вижу.Я не думаю, что readline совместим со всем этим, как вам хотелось бы, к сожалению.Проблема в том, что когда вы создаете интерфейс, вид узла предполагает, что он будет иметь полный контроль над этим потоком с этого момента.Когда мы перенаправляем эти данные в vim, readline все еще там обрабатывает нажатия клавиш, но vim также делает то же самое.

Единственный способ, который я вижу, это вручную отключить все из интерфейса cli перед тем, каквы запускаете vim.

Непосредственно перед тем, как запустить процесс, нам нужно закрыть интерфейс и, к сожалению, вручную удалить прослушиватель нажатия клавиш, потому что, по крайней мере, на данный момент узел не удаляет его автоматически.

process.stdin.removeAllListeners 'keypress'
cli.close()
tty.setRawMode true

Затем в обратном вызове процесса 'exit' вам нужно будет снова вызвать createInterface.

...