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

时间:2021-07-15 20:54:22

I would like to be able to open Vim from node.js program running in the terminal, create some content, save and exit Vim, and then grab the contents of the file.

我希望能够从终端中运行的node.js程序打开Vim,创建一些内容,保存并退出Vim,然后获取文件的内容。

I'm trying to do something like this:

我正在尝试做这样的事情:

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

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

  text = fs.readFileSync filename
  console.log text

However, when this runs, it just hangs the terminal.

但是,当它运行时,它只是挂起终端。

I've also tried it with exec and got the same result.

我也尝试过exec并得到了相同的结果。

Update:

更新:

This is complicated by the fact that this process is launched from a command typed at a prompt with readline running. I completely extracted the relevant parts of my latest version out to a file. Here is it in its entirety:

由于在运行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()

The way it works as show above, is that it shows a prompt for a couple of seconds, and then launches in to Vim, but the TTY is messed up. I can edit, and save the file, and the contents are printed correctly. There is a bunch of junk printed to terminal on exit as well, and Readline functionality is broken afterward (no Up/Down arrow, no Tab completion).

它的工作方式如上所示,它显示一个几秒的提示,然后启动到Vim,但TTY搞砸了。我可以编辑和保存文件,并正确打印内容。退出时还有一堆垃圾打印到终端,之后Readline功能被破坏(没有向上/向下箭头,没有Tab完成)。

If I uncomment the cli.pause() line, then the TTY is OK in Vim, but I'm stuck in insert mode, and the Esc key doesn't work. If I hit Ctrl-C it quits the child and parent process.

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

2 个解决方案

#1


10  

Update: My answer applied at the time it was created, but for modern versions of Node, look at this other answer.

更新:我的答案在创建时应用,但对于现代版本的Node,请查看其他答案。

First off, your usage of spawn isn't correct. Here are the docs. http://nodejs.org/docs/latest/api/child_processes.html#child_process.spawn

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

Your sample code makes it seem like you expect vim to automatically pop up and take over the terminal, but it won't. The important thing to remember is that even though you may spawn a process, it is up to you to make sure that the data from the process makes it through to your terminal for display.

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

In this case, you need to take data from stdin and send it to vim, and you need to take data output by vim and set it to your terminal, otherwise you won't see anything. You also need to set the tty into raw mode, otherwise node will intercept some of the key sequences, so vim will not behave properly.

在这种情况下,您需要从stdin获取数据并将其发送到vim,您需要通过vim输出数据并将其设置到终端,否则您将看不到任何内容。您还需要将tty设置为原始模式,否则节点将拦截某些键序列,因此vim将无法正常运行。

Next, don't do readFileSync. If you come upon a case where you think you need to use a sync method, then chances are, you are doing something wrong.

接下来,不要执行readFileSync。如果你遇到一个你认为需要使用同步方法的情况,那么很可能,你做错了什么。

Here's a quick example I put together. I can't vouch for it working in every single case, but it should cover most cases.

这是我放在一起的一个简单例子。我无法保证它在每一个案例中都有效,但它应该涵盖大多数情况。

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());
      }
    });
  }
});

Update

更新

I seeee. I don't think readline is as compatible with all of this as you would like unfortunately. The issue is that when you createInterface, node kind of assumes that it will have full control over that stream from that point forward. When we redirect that data to vim, readline is still there processing keypresses, but vim is also doing the same thing.

我见。我不认为readline与你想要的所有这些兼容。问题在于,当您创建接口时,节点类型假定它将从该点向前完全控制该流。当我们将该数据重定向到vim时,readline仍然存在处理按键,但vim也在做同样的事情。

The only way around this that I see is to manually disable everything from the cli interface before you start vim.

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

Just before you spawn the process, we need to close the interface, and unfortunately manually remove the keypress listener because, at least at the moment, node does not remove it automatically.

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

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

Then in the process 'exit' callback, you will need to call createInterface again.

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

#2


27  

Just inherit stdio from main process.

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

more options: http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

更多选项:http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

#1


10  

Update: My answer applied at the time it was created, but for modern versions of Node, look at this other answer.

更新:我的答案在创建时应用,但对于现代版本的Node,请查看其他答案。

First off, your usage of spawn isn't correct. Here are the docs. http://nodejs.org/docs/latest/api/child_processes.html#child_process.spawn

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

Your sample code makes it seem like you expect vim to automatically pop up and take over the terminal, but it won't. The important thing to remember is that even though you may spawn a process, it is up to you to make sure that the data from the process makes it through to your terminal for display.

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

In this case, you need to take data from stdin and send it to vim, and you need to take data output by vim and set it to your terminal, otherwise you won't see anything. You also need to set the tty into raw mode, otherwise node will intercept some of the key sequences, so vim will not behave properly.

在这种情况下,您需要从stdin获取数据并将其发送到vim,您需要通过vim输出数据并将其设置到终端,否则您将看不到任何内容。您还需要将tty设置为原始模式,否则节点将拦截某些键序列,因此vim将无法正常运行。

Next, don't do readFileSync. If you come upon a case where you think you need to use a sync method, then chances are, you are doing something wrong.

接下来,不要执行readFileSync。如果你遇到一个你认为需要使用同步方法的情况,那么很可能,你做错了什么。

Here's a quick example I put together. I can't vouch for it working in every single case, but it should cover most cases.

这是我放在一起的一个简单例子。我无法保证它在每一个案例中都有效,但它应该涵盖大多数情况。

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());
      }
    });
  }
});

Update

更新

I seeee. I don't think readline is as compatible with all of this as you would like unfortunately. The issue is that when you createInterface, node kind of assumes that it will have full control over that stream from that point forward. When we redirect that data to vim, readline is still there processing keypresses, but vim is also doing the same thing.

我见。我不认为readline与你想要的所有这些兼容。问题在于,当您创建接口时,节点类型假定它将从该点向前完全控制该流。当我们将该数据重定向到vim时,readline仍然存在处理按键,但vim也在做同样的事情。

The only way around this that I see is to manually disable everything from the cli interface before you start vim.

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

Just before you spawn the process, we need to close the interface, and unfortunately manually remove the keypress listener because, at least at the moment, node does not remove it automatically.

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

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

Then in the process 'exit' callback, you will need to call createInterface again.

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

#2


27  

Just inherit stdio from main process.

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

more options: http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

更多选项:http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options