我希望能够从终端中运行的 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
尝试过并得到了相同的结果。
更新:
由于此过程是通过在提示符下键入命令 正在运行。我将最新版本的相关部分完全提取到一个文件中。完整内容如下:
{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()
行,那么 Vim 中的 TTY 正常,但我陷入插入模式,并且 Esc
键不起作用。如果我按 Ctrl-C
它会退出子进程和父进程。
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.
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.
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:
{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).
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.
发布评论
评论(3)
您可以从主进程继承stdio。
更多选项:http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
You can inherit stdio from the main process.
More options here: http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
更新:我的答案在创建时应用,但对于现代版本的 Node,请查看 另一个回答。
首先,您对 spawn 的使用不正确。这是文档。 http://nodejs.org/docs/latest/api/child_processes。 html#child_process.spawn
您的示例代码看起来好像您希望 vim 自动弹出并接管终端,但事实并非如此。需要记住的重要一点是,即使您可能生成一个进程,您也需要确保该进程中的数据能够到达您的终端进行显示。
在这种情况下,您需要从 stdin 获取数据并将其发送到 vim,并且需要获取 vim 输出的数据并将其设置到您的终端,否则您将看不到任何内容。您还需要将 tty 设置为原始模式,否则 Node 会拦截一些按键序列,因此 vim 将无法正常运行。
接下来,不要执行 readFileSync。如果您遇到认为需要使用同步方法的情况,那么很可能您做错了什么。
这是我整理的一个简单示例。我不能保证它在每种情况下都有效,但它应该涵盖大多数情况。
更新
我明白了。不幸的是,我认为 readline 并不像您希望的那样与所有这些兼容。问题是,当您创建接口时,节点会假设从该点开始它将完全控制该流。当我们将数据重定向到 vim 时,readline 仍然在那里处理按键,但 vim 也在做同样的事情。
我认为解决这个问题的唯一方法是在启动 vim 之前手动禁用 cli 界面中的所有内容。
在生成进程之前,我们需要关闭界面,不幸的是手动删除按键侦听器,因为至少目前,节点不会自动删除它。
然后在进程“退出”回调中,您将需要再次调用createInterface。
Update: My answer applied at the time it was created, but for modern versions of Node, look at this other answer.
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
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.
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.
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.
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.
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.
The only way around this that I see is to manually disable everything from the
cli
interface before you start vim.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.
Then in the process 'exit' callback, you will need to call createInterface again.
我尝试使用 Node 的 repl 库来做这样的事情 - https://nodejs.org/api/repl。 html - 但没有任何效果。我尝试启动 vscode 和 TextEdit,但在 Mac 上似乎没有办法等待这些程序关闭。将 execSync 与 vim、nano 和 micro 一起使用都会表现得很奇怪或挂起终端。
最后,我使用此处给出的示例切换到使用 readline 库 https://nodejs.org/api /readline.html#readline_example_tiny_cli - 它使用 micro,例如,
它在 Scratch 缓冲区中切换到 micro - 完成后按 ctrl-q,并在结果中返回缓冲区内容。
I tried to do something like this using Node's repl library - https://nodejs.org/api/repl.html - but nothing worked. I tried launching vscode and TextEdit, but on the Mac there didn't seem to be a way to wait for those programs to close. Using execSync with vim, nano, and micro all acted strangely or hung the terminal.
Finally I switched to using the readline library using the example given here https://nodejs.org/api/readline.html#readline_example_tiny_cli - and it worked using micro, e.g.
It switches to micro in a Scratch buffer - hit ctrl-q when done, and it returns the buffer contents in result.