如何从 node.js 打开终端应用程序?
Posted
技术标签:
【中文标题】如何从 node.js 打开终端应用程序?【英文标题】:How do I open a terminal application from node.js? 【发布时间】:2012-02-25 16:40:46 【问题描述】:我希望能够从运行在终端的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()
行,那么在 Vim 中 TTY 是正常的,但我卡在插入模式,Esc
键不起作用。如果我点击Ctrl-C
,它将退出子进程和父进程。
【问题讨论】:
你能解释一下这个用例吗?你是想通过运行 Vim 命令来与 Vim 交互,还是只是将文件写入磁盘? 是否有意要求用户使用编辑器来创建内容?或者您打算完全从node.js
内部驱动vim
?
您可以尝试打开/关闭原始模式吗? nodejs.org/docs/latest/api/tty.html#tty.setRawMode
我似乎使用了错误的工具来完成这项工作:-) 这将是bash
中的一行。不过,将应用程序的其余部分放在 node.js 中,它工作得很好,而且我可以更轻松地在服务器 API 和这个客户端代码之间移动代码。我想这是一次很好的学习经历。
是的,在 node 中做这件事看起来确实很奇怪,但是弄清楚如何去做是很有趣的。我会看看能不能在一个小时内解决你的 readline 问题。
【参考方案1】:
你可以从主进程继承stdio。
const child_process = require('child_process')
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
【讨论】:
这似乎是一个更新且简单得多的答案! 这是截至 2015 年底当前 nodejs 版本的正确解决方案。当前标记为“正确”的其他答案将不再有效。 我认为这是一个关于如何在一个class
: gist.github.com/56c65f8608781dbd88ef 中执行此操作的示例
这对我有用,比接受的答案简单得多。
文档链接显示使用close
事件,但此答案显示使用exit
事件。哪个是正确的?【参考方案2】:
更新:我的答案在创建时应用,但对于现代版本的 Node,请查看 this other answer。
首先,您对 spawn 的使用不正确。这是文档。 http://nodejs.org/docs/latest/api/child_processes.html#child_process.spawn
您的示例代码看起来像是您希望 vim 自动弹出并接管终端,但它不会。需要记住的重要一点是,即使您可能会生成一个进程,您也必须确保来自该进程的数据能够传送到您的终端进行显示。
在这种情况下,你需要从标准输入中取出数据并发送给vim,并且你需要把vim输出的数据取出并设置到你的终端,否则你什么都看不到。您还需要将 tty 设置为 raw 模式,否则 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。
【讨论】:
这几乎可以工作了。第一个问题是 Vim 需要几秒钟才能出现,而且它在终端中只是半屏。其次,TTY 也混乱了。在输入iThis is a test of the editor
之后显示TThi i tes o th editor
。退出并显示文件内容会显示正确的字符串。
我在其中使用的程序是 API 的单用户命令行客户端,所以这就是我使用 readFileSync
的原因。通过删除回调来稍微简化代码。事实上,让所有代码同步可能会更简单......但这就是我现在所拥有的。
不过,readFileSync 是错误的形式。如果你要使用节点,你最好做对。我不知道为什么它对你来说如此奇怪。您可以尝试使用更简单的编辑器,例如 nano 吗?我的 vim 肯定比你的好,但我的退格键不起作用,我似乎无法让它工作,但它在 nano 中工作。您是否使用我发布的确切代码遇到了这个问题,或者您是否将其集成到您的代码中?也许发布更多您正在使用的代码?
好的,被指控有罪 ;-) 我之前只尝试过集成您的代码。我不知道为什么我不只是复制和粘贴并运行它。我现在这样做了,效果很好(这里没有退格问题)。这使我发现我对 readline 的使用显然导致了问题。我更新了原始问题。非常感谢您的帮助!
我没有阅读关于继承的较低答案,发现此代码 (1) 不再有效(节点 6.9.1)和 (2) 它可以通过删除 var tty = require('tty');
并替换来修复tty.setRawMode
和 process.stdin.setRawMode
。仅供参考,但继承解决方案更好。【参考方案3】:
我尝试使用 Node 的 repl 库 - https://nodejs.org/api/repl.html 做类似的事情 - 但没有任何效果。我尝试启动 vscode 和 TextEdit,但在 Mac 上似乎没有办法等待这些程序关闭。将 execSync 与 vim、nano 和 micro 一起使用都会出现异常或挂起终端。
最后,我使用此处给出的示例 https://nodejs.org/api/readline.html#readline_example_tiny_cli 切换到使用 readline 库 - 它使用 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,它会在结果中返回缓冲区内容。
【讨论】:
以上是关于如何从 node.js 打开终端应用程序?的主要内容,如果未能解决你的问题,请参考以下文章