Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No event on child-process stdout #2926

Closed
ctag opened this issue Sep 17, 2015 · 3 comments
Closed

No event on child-process stdout #2926

ctag opened this issue Sep 17, 2015 · 3 comments
Labels
child_process Issues and PRs related to the child_process subsystem.

Comments

@ctag
Copy link

ctag commented Sep 17, 2015

I'd like to have a nodejs script interact with a child process via standard input and output. I've tried setting this up with what the documentation provides, but I've arrived at a deadlock where what's happening doesn't match what's I believe is documented.

What I see in the documentation:

The ChildProcess class is not intended to be used directly. Use the spawn(), exec(), execFile(), or fork() methods to create a Child Process instance.

Reading each section leads me to:

  • spawn: Create a child process from any normal terminal program, and have access to stdio.
  • exec: Create a child process from any normal terminal program, and receive output on exit.
  • execFile: Similar to exec but for an explicit executable file, receive output on exit.
  • fork: Create a new node instance from any proper nodejs file, interact using messages.

The section for spawn describes access to an stdio option. Looking further up the page, I read that the child's stdin and stdout both default to a writeable or readable stream.

OK, I don't entirely know what a stream is in the context of Node, so looking at the stream doc page:

Readable streams have two "modes": a flowing mode and a paused mode. When in flowing mode, data is read from the underlying system and provided to your program as fast as possible.

Cool, and then since I've already looked at the child-process.spawn examples, I know that .on('data'... is important. It's listed right below:

Attaching a data event listener to a stream that has not been explicitly paused will switch the stream into flowing mode. Data will then be passed as soon as it is available.
If you just want to get all the data out of the stream as fast as possible, this is the best way to do so.

This looks pretty straightforward, I want spawn. This child process of mine will persist for a while, and I want access to the output as it appears. Sounds like a good match.

Running the provided example:

Great, time to become familiar with the .spawn operator. To do so, I'll run the provided ps ax | grep ssh example:

var spawn = require('child_process').spawn,
    ps    = spawn('ps', ['ax']),
    grep  = spawn('grep', ['ssh']);

ps.stdout.on('data', function (data) {
  grep.stdin.write(data);
});

ps.stderr.on('data', function (data) {
  console.log('ps stderr: ' + data);
});

ps.on('close', function (code) {
  if (code !== 0) {
    console.log('ps process exited with code ' + code);
  }
  grep.stdin.end();
});

grep.stdout.on('data', function (data) {
  console.log('' + data);
});

grep.stderr.on('data', function (data) {
  console.log('grep stderr: ' + data);
});

grep.on('close', function (code) {
  if (code !== 0) {
    console.log('grep process exited with code ' + code);
  }
});

Output:

$ node grep.js
6364 pts/0 S+ 0:00 ssh bns-xwing-local

It works!

Running a modified example:

But I notice that grep exits pretty quickly. I want my program to persist, so I'm going to modify the example to leave grep running for a while longer.

var spawn = require('child_process').spawn,
    ps    = spawn('ps', ['ax']),
    grep  = spawn('grep', ['ssh']);

ps.stdout.on('data', function (data) {
  grep.stdin.write(data);
});

ps.stderr.on('data', function (data) {
  console.log('ps stderr: ' + data);
});

ps.on('close', function (code) {
  if (code !== 0) {
    console.log('ps process exited with code ' + code);
  }
  /* ---------------------------------------- */
  //grep.stdin.end(); /* <- Only line changed */
  /* ---------------------------------------- */
});

grep.stdout.on('data', function (data) {
  console.log('' + data);
});

grep.stderr.on('data', function (data) {
  console.log('grep stderr: ' + data);
});

grep.on('close', function (code) {
  if (code !== 0) {
    console.log('grep process exited with code ' + code);
  }
});

Output:

$ node grep.js

No output? Why would that be the case? Closing grep's stdin will cause it to exit, leaving it open keeps me from seeing the output I want. All in all, how is this different from the functionality of .exec?

I tried to link pretty closely to the documentation because I'm confident that Node is working as expected and that I've misread something that brought me here.

@ctag ctag changed the title No event on child-process stdout pipe No event on child-process stdout Sep 17, 2015
@brendanashworth brendanashworth added the child_process Issues and PRs related to the child_process subsystem. label Sep 17, 2015
@Trott
Copy link
Member

Trott commented Sep 17, 2015

STDOUT from grep is being buffered. If you use --line-buffered with grep, things work as you expect:

    grep  = spawn('grep', ['--line-buffered', 'ssh']);

@ctag
Copy link
Author

ctag commented Sep 17, 2015

That is the solution for me, thank you!

Since I don't intend to use grep in my end script, I took this and tried to find a more general solution. The unbuffer program, on my distro bundled with expect, appears to do this:

    grep  = spawn('unbuffer', ['grep', 'ssh']);

And for a small set of programs I tried it on, output arrives at each newline.

@mykeels
Copy link

mykeels commented Jun 14, 2022

For me, I had set an environment variable in the spawn options, like:

spawn(command, {
  env: {
    BROWSER: "none"
  }
}

In Windows, other environment variables seemed to be carried along. But in WSL, they were not, so the program was not working. The solution was to spread the other environment variables such as:

spawn(command, {
  env: {
    ...process.env,
    BROWSER: "none"
  }
}

which worked in both Windows and WSL envs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
child_process Issues and PRs related to the child_process subsystem.
Projects
None yet
Development

No branches or pull requests

4 participants