2012-02-03 231 views
18

我想能够从运行在终端的node.js程序中打开Vim,创建一些内容,保存并退出Vim,然后抓取文件的内容。如何从node.js打开终端应用程序?

我试图做这样的事情:

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()行,那么TTY在Vim中是OK的,但我卡在插入模式下,并且Esc键不起作用。如果我点击Ctrl-C,它将退出子进程和父进程。

+0

你能摆脱对使用情况下,一些轻?你想通过运行它的命令与Vim进行交互还是只需要将文件写入磁盘? – 2012-02-03 00:55:47

+0

是否打算要求用户使用编辑器来创建内容?还是你打算从'node.js'内完全驱动'vim'? – sarnold 2012-02-03 00:59:15

+0

我希望能够打开'vim',以便我可以创建内容,并在完成后退出,并让节点获取临时文件的内容,然后将该文件作为来自节点的HTTP请求的一部分发送。 – mkopala 2012-02-03 01:11:48

回答

9

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

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

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

在这种情况下,你需要从标准输入的数据并将其发送到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()); 
     } 
    }); 
    } 
}); 

更新

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

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

就在你产卵的过程中,我们需要关闭接口,不幸的是手动删除按键监听器,因为至少目前节点不会自动删除它。

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

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

+1

这几乎工作。首先问题是Vim需要几秒钟才会出现,而且它只是终端中的一半。其次,TTY也很精彩。在输入'iThis是编辑器的测试'之后,它显示'TThi i tes or th editor'。退出并显示文件内容显示正确的字符串。 – mkopala 2012-02-03 07:54:30

+1

我正在使用它的程序是一个API的单用户命令行客户端,所以这就是我使用'readFileSync'的原因。通过删除回调来简化代码。事实上,将所有的代码同步,可能会更简单......但这是我现在得到的。 – mkopala 2012-02-03 08:01:52

+1

不过,readFileSync是不好的形式。如果你打算使用节点,你可能会做得对。我不知道为什么它会为你显得很奇怪。你可以尝试使用更简单的编辑器,比如nano吗?我的vim肯定比你的更好,但我的退格键不起作用,我似乎无法得到它的工作,但它在纳米工作。您是否使用我发布的确切代码来获取该问题,或者您是否将其集成到您的代码中?也许发布更多你使用的代码? – loganfsmyth 2012-02-03 14:14:24

26

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

+3

这似乎是一个更新,更简单的答案! – 2013-10-06 03:33:57

+0

截至2015年底,这是当前nodejs发布的正确解决方案。目前标记为“正确”的另一个答案将不再有效。 – 2016-02-14 23:47:57

+0

我认为这是一个例子,如何在一个'class'中做到这一点:https://gist.github.com/56c65f8608781dbd88ef – Sukima 2016-03-15 13:18:19