Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/35.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 处理带回车符的标准输出流的子\u进程(\r)_Javascript_Node.js_Stream_Rsync_Child Process - Fatal编程技术网

Javascript 处理带回车符的标准输出流的子\u进程(\r)

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',{})在设置传输之前只调用一次,然后

我正在编写一个简单的(ish)应用程序,它允许工作的内部系统通过REST调用请求从远程服务器到另一个远程服务器的复制进程(使用rsync)

我已经对express框架非常熟悉,刚刚开始尝试child_流程库,偶然发现了一个小问题

我正在使用节点的
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/

嗯!;)