Is 7zip stdout broken? Is there a way to capture the progress in nodejs? [Windows]

I am trying to get the stdout of 7zip when it processes files and get the percentage in nodeJs, but it doesn't behave as expected. 7zip doesn't output anything to stdout until the very end of the execution. Which is not very helpful.. especially when I have large files being compressed and no feedback is shown for a very long time.

The code I am using (simplified):

// 7zip test, place the 7z.exe in the same dir, if it's not on %PATH%
var cp = require('child_process');
var inputFile = process.argv[2]; if(inputFile==null) return;
var regProgress = /(\d{1,3})%\s*$/; //get the last percentage of the string, 3 digits
var proc = cp.spawn("7z.exe",["a","-t7z" ,"-y" ,inputFile + ".7z",inputFile]);
proc.stdout.setEncoding("utf8");
proc.stdout.on("data",function(data){
    if(regProgress.test(data))
    console.log("Progress = " + regProgress.exec(data)[1] + "%");
});
proc.once("exit",function(exit,sig){ console.log("Complete"); });

I have used the same code to get the percentage with WinRar successfully and I am beginning to think that 7zip might be buggy? Or I am doing it wrong? Can I forcefully read the stdout of a process with a timer perhaps?

The same code above, with the exception of the following line replaced, works as expected with WinRar.

var proc = cp.spawn("Rar.exe",["a","-s","-ma5","-o+",inputFile+".rar",inputFile]);

If anyone knows why this happens and if it is fixable, I would be grateful! :-)

p.s. I have tried 7za.exe, the command line version of 7zip, also the stable, beta and alpha versions, they all have the same issue

7-zip only outputs progress when stdout is a terminal.

To trick 7-zip, you need to npm install pty.js (requires Visual Studio or VS Express with Windows SDK) and then use code like:

var pty = require('pty');

var inputFile = process.argv[2],
    pathTo7zip = 'c:\\Program Files\\7-Zip\\7z.exe';

if (inputFile == null)
  return;

var term = pty.spawn(process.env.ComSpec, [], {
  name: 'ansi',
  cols: 200,
  rows: 30,
  cwd: process.env.HOME,
  env: process.env
});

var rePrg = /(\d{1,3})%\r\n?/g,
    reEsc = /\u001b\[\w{2}/g,
    reCwd = new RegExp('^' + process.cwd().replace(/\\/g, '\\\\'), 'm');
    prompts = 0,
    buffer = '';

term.on('data', function(data) {
  var m, idx;

  buffer += data;

  // remove terminal escape sequences
  buffer = buffer.replace(reEsc, '');

  // check for multiple progress indicators in the current buffer
  while (m = rePrg.exec(buffer)) {
    idx = m.index + m[0].length;
    console.log(m[1] + ' percent done!');
  }

  // check for the cmd.exe prompt
  if (m = reCwd.exec(buffer)) {
    if (++prompts === 2) {
      // command is done
      return term.kill();
    } else {
      // first prompt is before we started the actual 7-zip process
      if (idx === undefined) {
        // we didn't see a progress indicator, so make sure to truncate the
        // prompt from our buffer so that we don't accidentally detect the same
        // prompt twice
        buffer = buffer.substring(m.index + m[0].length);
        return;
      }
    }
  }

  // truncate the part of our buffer that we're done processing
  if (idx !== undefined)
    buffer = buffer.substring(idx);
});
term.write('"'
           + pathTo7zip
           + '" a -t7z -y "'
           + inputFile
           + '.7z" "'
           + inputFile
           + '"\r');

It should be noted that 7-zip does not always output 100% at finish. If the file compresses quickly, you may just see only a single 57% for example, so you will have to handle that however you want.