Javascript 处理带回车符的标准输出流的子\u进程(\r)
我正在编写一个简单的(ish)应用程序,它允许工作的内部系统通过REST调用请求从远程服务器到另一个远程服务器的复制进程(使用rsync) 我已经对express框架非常熟悉,刚刚开始尝试child_流程库,偶然发现了一个小问题 我正在使用节点的Javascript 处理带回车符的标准输出流的子\u进程(\r),javascript,node.js,stream,rsync,child-process,Javascript,Node.js,Stream,Rsync,Child Process,我正在编写一个简单的(ish)应用程序,它允许工作的内部系统通过REST调用请求从远程服务器到另一个远程服务器的复制进程(使用rsync) 我已经对express框架非常熟悉,刚刚开始尝试child_流程库,偶然发现了一个小问题 我正在使用节点的childProcess.spawn()成功启动rsync进程,我的问题是rsync输出的进度线使用回车符(\r)而不是换行符(\n)进行缓冲。因此,STDOUT事件,process.STDOUT.on('data',{})在设置传输之前只调用一次,然后
childProcess.spawn()
成功启动rsync进程,我的问题是rsync输出的进度线使用回车符(\r)而不是换行符(\n)进行缓冲。因此,STDOUT事件,process.STDOUT.on('data',{})
在设置传输之前只调用一次,然后在复制完成之后调用一次,因为在回车时不会刷新STDOUT数据,而在作业完成时,随着进度的更新,只会更新一行新行
在最新版本的rsync(3.1.0)中有一个开关,用于将输出缓冲区结尾更改为\n而不是\r,但不幸的是,我所在的公司在很长一段时间内不会采用此版本
我正在以通常的方式繁殖和阅读child_过程
var doCopy = function (onFinish) {
var process = childProcess.spawn('ssh', [
source.user + "@" + source.host,
"rsync",
"-avz",
"--progress",
source.path + source.file,
"-e ssh",
dest.user + "@" + dest.host + ":" + dest.path
]);
process.on('error', function (error) {
console.log("ERR: " + error.code);
})
process.stdout.on('data', function (data) {
console.log("OUT: " + data);
});
process.stderr.on('data', function (data) {
console.log("ERR: " + data);
});
process.on('close', function (code) {
console.log("FIN: " + code);
if(onFinish){
onFinish(code);
}
});
}
..且控制台输出为
OUT: building file list ...
OUT:
1 file to consider
OUT: test4.mp4
32,768 0% 0.00kB/s 0:00:00
169,738,240 32% 161.84MB/s 0:00:02
338,165,760 64% 161.32MB/s 0:00:01
504,692,736 96% 160.53MB/s 0:00:00
524,288,000 100% 160.35MB/s 0:00:03 (xfr#1, to-chk=0/1)
OUT:
sent 509,959 bytes received 46 bytes 113,334.44 bytes/sec
total size is 524,288,000 speedup is 1,028.01
FIN: 0
因此您可以看到,只有当rsync输出新行(其中有一个“OUT:”)时,才会调用stdout.on('data,)
我的问题是,我能改变这个吗?可能会对流进行转换,以便在发生\r\n错误时进行刷新?然后我可以对该行进行正则化,并再次提供进度更新
如果做不到这一点,我想我唯一的其他选择是生成另一个进程来监视不断增长的文件
非常感谢您提供的任何帮助/建议。我发现了一个非常好的模块,它完全符合我的目标。允许我用任何字符(在我的例子中是“\r”)分隔标准输出缓冲区,并触发一个新的标准输出事件来处理数据。像
var splitter = process.stdout.pipe(StreamSplitter("\r"));
splitter.on('token', function (data) {
console.log("OUT: " + data);
});
splitter.on('done', function (data) {
console.log("DONE: " + data);
});
为此,我制作了一个自定义流转换子类。专业的做法是,您还可以用您的子进程最喜爱的名称标记行,它以正确的方式处理给定的
\r
请随时尝试我的实现:
(在typescript中,但您可以轻松删除所有类型内容)
从“child_进程”导入{ChildProcessWithoutNullStreams};
从“流”导入{Transform};
导出默认类LineTagTransform扩展转换{
lastLineData='';
标签=“”;
构造函数(标记?:字符串){
超级({objectMode:true});
this.tag=tag | |“”;
如果(tag&&!tag.endsWith(“”))this.tag+='';
}
_转换(块:缓冲区|字符串|任意,编码:字符串,回调:函数){
let data:string=chunk.toString().replace(/\r(?!\n)/,'\n\r');
如果(this.lastLineData)data=this.lastLineData+data;
让line=data.split(/\r?\n/);
this.lastLineData=lines.splice(lines.length-1,1)[0];
用于(行的常量行){
if(行.startsWith('\r')){
this.push(`r${this.tag}${line.substring(1)}`);
}否则{
this.push(`\n${this.tag}${line}`)
}
}
回调();
}
_刷新(回调:函数){
if(此.lastLineData){
if(this.lastLineData.startsWith('\r')){
this.push(`r${this.tag}${this.lastLineData.substring(1)}`);
}否则{
this.push(`\n${this.tag}${this.lastLineData}`)
}
}
this.lastLineData='';
回调();
}
静态wrapStreams(child:ChildProcessWithoutNullStreams,标记?:字符串,stdout:NodeJS.WriteStream=process.stdout,stderr:NodeJS.WriteStream=process.stderr){
子.stdout.pipe(新的LineTagTransform(标记)).pipe(stdout);
stderr.pipe(新的LineTagTransform(tag)).pipe(stderr);
}
}
那么最简单的使用方法是:
const child=spawn('./DownloadUnicorn.exe',选项);
LineTagTransform.wrapStreams(子,“[unicorn]”);
产出:
[unicorn] Start DownloadUnicorn!
[unicorn] Downloading [=====-----] 50%
...
在一行上使用下载栏动画\o/
嗯!;)