是否有办法从我的Node.js衍生的shell stdout文本中挽救这些破碎的bash转义序列工件?



我编写了一个类,用于生成和与shell交互(在电子应用程序中),如下所示:

const { spawn } = require('child_process')
class Shell {
constructor() {
this.process = spawn('/bin/bash', []);
this.process.stdout.on('data', (data) => {
const out = data.toString()
console.log('stdout:', out)
if (this.res) {
this.res(out)
}
});
this.process.stderr.on('data', function (data) {
const err = data.toString()
console.log('stderr:', err)
if (this.rej) this.rej(err)
});
}
send(command, throwErr = false) {
return new Promise((resolve, reject) => {
this.res = resolve
if (throwErr) this.rej = reject
else this.rej = resolve
this.process.stdin.write(command + 'n')
})
}
}

我得到的输出是:

stdout: ]0;student@linux-opstation-jyrf: ~[01;32mstudent@linux-opstation-jyrf[00m:[01;34m~[00m$

这里是一个版本,我用JSON将输出字符串化以查看转义字符:stdout: "u001b]0;student@linux-opstation-jyrf: ~u0007u001b[01;32mstudent@linux-opstation-jyrfu001b[00m:u001b[01;34m~u001b[00m$ ssh -t -t -oStrictHostKeyChecking=no student@10.5r50.30.231rn"

我意识到这些是bash转义序列用于格式化的工件,我很难弄清楚如何摆脱它,特别是因为转义字符不被打印。

编辑:所以我将原始标准输出Buffer(代码中的data)写入二进制文件:
fs.createWriteStream(path, { encoding: 'binary'}).write(data);

,发现似乎没有损失发生在.toString()方法(我认为?),所以我留下抓耳挠头,其余的stdout标记被截断。

00000000: 1b5d 303b 7374 7564 656e 7440 6c69 6e75  .]0;student@linu
00000010: 782d 6f70 7374 6174 696f 6e2d 6a79 7266  x-opstation-jyrf
00000020: 3a20 7e07 1b5b 3031 3b33 326d 7374 7564  : ~..[01;32mstud
00000030: 656e 7440 6c69 6e75 782d 6f70 7374 6174  ent@linux-opstat
00000040: 696f 6e2d 6a79 7266 1b5b 3030 6d3a 1b5b  ion-jyrf.[00m:.[
00000050: 3031 3b33 346d 7e1b 5b30 306d 2420 7373  01;34m~.[00m$ ss
00000060: 6820 2d74 202d 7420 2d6f 5374 7269 6374  h -t -t -oStrict
00000070: 486f 7374 4b65 7943 6865 636b 696e 673d  HostKeyChecking=
00000080: 6e6f 2073 7475 6465 6e74 4031 302e 350d  no student@10.5.
00000090: 3530 2e33 302e 3233 310d 0a              50.30.231..

但是当我保存文件时,也许我没有得到正确的编码,因为我认为原始缓冲区应该(?)输出文本,如u001b[00m:u001b[01;34muFFFF十六进制字符不存在。编辑:啊,uFFFF显然是unicode。仍然在弄清楚如何将缓冲区正确地保存为二进制,我认为unicode正在丢失{encoding: 'binary'}设置为选项。或者十六进制转储只显示utf8,这听起来更有可能。

假设这些工件确实来自自定义的提示字符串,那么最简单的事情就是更改提示字符串。有多种替代方法,这在一定程度上取决于提示字符串的设置位置。

如果你得到一个提示,那么bash是作为一个交互式shell运行的。它不会以登录shell的形式运行,并使用您用来启动它的命令。相应的初始化步骤如下:

当一个非登录shell的交互式shell被启动时,bash~/.bashrc(如果该文件存在)读取并执行命令。这可以使用——norc选择。——rcfilefile选项将强制bash从文件中读取和执行命令,而不是~/.bashrc.

(Bash手册)

因此,您可以将--norc选项传递给bash,以抑制读取任何shell初始化文件,从而获得默认提示(以及默认的其他一切)。但是,对于您的需求来说,该环境可能过于稀疏,因此,作为一种替代方法,您可以创建一个专门的shell配置文件来设置您想要的确切配置,并使用--rcfile选项使程序启动的bash实例使用该配置。这可能是你最好的选择,但是可能需要一些工作来设置你需要的

作为一种更快更脏的替代方法,您可以修改相关的~/.bashrc来更改提示字符串(删除PS1PS2的定义,或者将它们重新定义为默认值:PS1='s-v$ ' PS2='> ')。您还可以将前面两个配置文件与一个自定义配置文件结合使用,该文件读取默认配置文件并覆盖提示字符串:

electron.bashrc

. ~/.bashrc
PS1='s-v$ '
PS2='> '

这是一个粗糙、丑陋、快速的解决方案。我欢迎更优雅的方法:

const { spawn } = require('child_process')
class Shell {
constructor() {
// I just threw a bunch of output into a regex tester 
// and wrote `|` joined matches until all markup in my
// sample input was detected. That's all there is to it.
// This will almost certainly not work across various
// machines depending on how the markup is structured.
const re = /\u[0-9 a-f]{4}[dd;ddm|\u[0-9 a-f]{4}]0;|\u[0-9 a-f]{4}[ddm:\u[0-9 a-f]{4}[dd;ddm|\u[0-9 a-f]{4}[ddm|\u[0-9 a-f]{4}/g
this.process = spawn('/bin/bash', []);
this.process.stdout.on('data', (data) => {
const out = data.toString()
const stringified = JSON.stringify(out)
console.log('stdout:', stringified)
const trimmed = stringified.replace(re, "")
.split('"').join('')
.split('\r').join('')
.split('\n').join('')
console.log('parsed stdout:', trimmed)
if (this.res) {
this.res(out)
}
});
this.process.stderr.on('data', function (data) {
const err = data.toString()
console.log('stderr:', err)
if (this.rej) this.rej(err)
});
}
send(command, throwErr = false) {
return new Promise((resolve, reject) => {
this.res = resolve
if (throwErr) this.rej = reject
else this.rej = resolve
this.process.stdin.write(command + 'n')
})
}
}