如何从node.js打开终端应用程序?

时间:2012-02-03 00:51:28

标签: node.js vim terminal coffeescript readline

我希望能够从终端中运行的node.js程序中打开Vim,创建一些内容,保存并退出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功能被破坏(没有向上/向下箭头,没有Tab完成)。

如果我取消注释cli.pause()行,那么Tim在Vim中是正常的,但是我处于插入模式,并且Esc键不起作用。如果我点击Ctrl-C,它将退出子进程和父进程。

3 个答案:

答案 0 :(得分:37)

只需从主进程继承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

答案 1 :(得分:11)

更新:我的答案在创建时已应用,但对于现代版本的Node,请查看this other answer

首先,你对spawn的使用是不正确的。这是文档。 http://nodejs.org/docs/latest/api/child_processes.html#child_process.spawn

您的示例代码使您看起来希望vim自动弹出并接管终端,但它不会。需要记住的重要一点是,即使您可能会生成一个流程,也需要确保流程中的数据通过终端进行显示。

在这种情况下,您需要从stdin获取数据并将其发送到vim,您需要通过vim输出数据并将其设置到终端,否则您将看不到任何内容。您还需要将tty设置为原始模式,否则node将拦截某些键序列,因此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也在做同样的事情。

我看到的唯一解决方法是在启动vim之前手动禁用cli界面中的所有内容。

在您生成进程之前,我们需要关闭接口,不幸的是手动删除了按键侦听器,因为至少在此刻,节点不会自动删除它。

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

然后在进程'exit'回调中,你需要再次调用createInterface。

答案 2 :(得分:0)

我尝试使用 Node 的 repl 库 - https://nodejs.org/api/repl.html - 但没有任何效果。我尝试启动 vscode 和 TextEdit,但在 Mac 上似乎没有办法等待这些程序关闭。在 vim、nano 和 micro 中使用 execSync 都出现异常或挂起终端。

最后,我使用此处给出的示例切换到使用 readline 库 https://nodejs.org/api/readline.html#readline_example_tiny_cli - 它使用 micro 工作,例如

import { execSync } from 'child_process'
...
case 'edit':
  const cmd = `micro foo.txt`
  const result = execSync(cmd).toString()
  console.log({ result })
  break

它在 Scratch 缓冲区中切换到 micro - 完成后按 ctrl-q,并在结果中返回缓冲区内容。