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

Weird behaviour when spawning bash scripts #29831

Closed
Choppel opened this issue Oct 3, 2019 · 2 comments
Closed

Weird behaviour when spawning bash scripts #29831

Choppel opened this issue Oct 3, 2019 · 2 comments

Comments

@Choppel
Copy link

Choppel commented Oct 3, 2019

There seem to be multiple issues when spawning bash scripts. Regular binaries (e.g. php, docker) seem to work fine at this point. I am running nodejs v10.16.3 on ubuntu.

Summary

When spawning a bash script with nodejs with stdin set to inherit, the output of the read command inside the bash script is not printed to stdout. It is printed to stderr although I think it doesn't belong there.
When setting stdin to pipe and piping process.stdin to this pipe, the output of the read command is not available anymore on stderr. Also, the app can only be exited by hitting enter after the child process exited.

Test cases

Consider the following bash script to be spawned via nodejs

#!/bin/sh

echo "Greeting"
read -r -p "Input: " varA

echo "Output ${varA}"

Case 1.) nodejs file spawning the bash script with stdin set to inherit

'use strict';

const {spawn} = require('child_process');
const path = require('path');

const child = spawn(path.join(__dirname, 'index.sh'), null, {
  'stdio': ['inherit']
});

child.stdout.on('data', (data) => {
  console.log('stdout: ' + data);
});

child.stderr.on('data', (data) => {
  console.log('stderr: ' + data);
});

child.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

Calling the script above in a terminal ("node index.js"), the output is the following

stdout: Greeting

stderr: Input: 
a
stdout: Output a

child process exited with code 0

The weird part here is that the "read -p" text "Input: " is written to stderr. Is that a bug with bash or its "read" command? Or is nodejs doing something wrong. Everything else seems to work right at this point.

Case 2.) nodejs file spawning the bash script with stdin set to pipe

'use strict';

const {spawn} = require('child_process');
const path = require('path');

const child = spawn(path.join(__dirname, 'index.sh'), null, {
  'stdio': ['pipe']
});

process.stdin.pipe(child.stdin);

child.stdout.on('data', (data) => {
  console.log('stdout: ' + data);
});

child.stderr.on('data', (data) => {
  console.log('stderr: ' + data);
});

child.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

Calling the script above in a terminal ("node index.js"), the output is the following

stdout: Greeting

a
stdout: Output a

child process exited with code 0

You can see the first echo ("Greeting") of the bash script and it is written to stdout. The text of the "read" command ("Input: ") is not visible anymore. The rest of the script is running as expected except that after the child process exits I have to hit enter a second time to exit the whole application.

Case 3.) nodejs file spawning the bash script with stdout, stderr and stdin set to pipe

'use strict';

const {spawn} = require('child_process');
const path = require('path');

const child = spawn(path.join(__dirname, 'index.sh'), null, {
  'stdio': 'pipe'
});

process.stdin.pipe(child.stdin);
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

child.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

The output and behaviour is basically the same as in case 2.

Greeting
d
Output d
child process exited with code 0

The "Input: " line is not visible and I have to hit enter after "child process exited..." to exit the application.

Addendum

For one of our products I need to automate different external applications (e.g. php scripts, docker commands, bash scripts). I want to pipe stdin, stderr and stdout so that I can read from stdout and write to stdin. If a specific line is written to stdout (e.g. "Do you really want to start the process now?") I 'answer' by writing "y\n" to stdin and the external process continues.
Because of the problems described above I am not able to do this.

@sam-github
Copy link
Contributor

              -p prompt
                     Display prompt on standard error, without a  trailing  newline,
                     before  attempting  to read any input.  The prompt is displayed
                     only if input is coming from a terminal.

The bash man page describes why prompt output goes to stderr, and why it doesn't appear at all when stdin is redirected.

I think you are needing a pty emulation library, pipes alone don't allow the kind of interaction with a shell you are attempting. Consider https://github.com/microsoft/node-pty

@Choppel
Copy link
Author

Choppel commented Oct 4, 2019

Thanks for the quick reply. node-pty seems to do the trick.

@Choppel Choppel closed this as completed Oct 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants