我希望能够从终端中运行的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
,它将退出子进程和父进程。
答案 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,并在结果中返回缓冲区内容。