3

I am encountering some weird behavior when calling a shell script from a Java process.

Process p = Runtime.getRuntime().exec("mybashscript.sh");
(new StreamGobblerThread(p.getInputStream())).start();
(new StreamGobblerThread(p.getErrorStream())).start();
p.waitFor();
returnValue = p.exitValue();

The StreamGobblerThread just has a run() method that does a

while(((inputStream.available>0) { inputStream.skip(available); }

About 20% of the time this works, but mostly the script fails with a return code of 141 right away.

From what I found on google, 141 is a return code when a SIGPIPE was received.

Any ideas?

Will
  • 1,622
  • 4
  • 25
  • 39
  • perhaps you should print out the errors, they might give you a meaningful error message. – Peter Lawrey Nov 17 '10 at 20:39
  • `SIGPIPE` may be caused by an early exit from `while` in `StreamGobblerThread`. To check it, just use empty `run()`. Add some debug printing in the loop to see what the `available` is. Try to read data instead of skipping. – khachik Nov 17 '10 at 20:45
  • it seems there is no 'error' other than the return code from the script, because it is not a java exception that occurred. all java seems to know is that the exit code of the script – Will Nov 17 '10 at 20:49
  • Does it do the same thing if you comment out the line where you start the thread for the error stream? – Alexis Dufrenoy Nov 17 '10 at 20:53
  • I will try commenting the error thread out. This code has apparently been running in production for along time without problems, but it could be that it works fine so long as nothing is going to stderr – Will Nov 17 '10 at 21:11
  • I commented out both of streamGobblerThread methods, as they weren't doing anything. No problems now! I forgot to mention the StreamGobbler itself closes the InputStream as soon as it skips all the bytes. But since more bytes may still come in, it broke the pipe! – Will Nov 17 '10 at 23:16

4 Answers4

2

I saw this several times in my life and found 2 workarounds.

  1. If you really want to read error and output stream separately, run command like "/bin/sh foo.sh 1>/tmp/out 2>/tmp/err" and then read from these files.
  2. If it is good for you to read a mixture of stdout and stderr, use ProcessBuilder as following:

ProcessBuilder b = new ProcessBuilder("foo.sh"); b.redirectErrorStream(true); Process p = b.start(); p.getInputStream(); //..... etc.

now read from the input stream that contains both stdout and stderr.

AlexR
  • 114,158
  • 16
  • 130
  • 208
2

I'm not 100% sure what the problem, but first of all:

while(((inputStream.available>0) { inputStream.skip(available); }

isn't valid.

This is because inputStream.available() isn't blocking, so if it doesn't have anything to read immediately, it's not going to read anything at all.

You're better off having something like this:

byte[] buf = new byte[8192];
int next;
try {
    while ((next = in.read(buf)) != -1) {}
} catch (IOException e) {
    throw new GroovyRuntimeException("exception while dumping process stream", e);
}

read() is blocking so, this way it actually will continue reading until the stream is properly closed.

(Note: This code is from Groovy's implementation of consumeProcessOutput()

Reverend Gonzo
  • 39,701
  • 6
  • 59
  • 77
  • good catch.. the StreamGobbler thread was actually closing the inputStream as soon as nothing more was available. but since the shell script may still be running, it was closing it prematurely. – Will Nov 17 '10 at 23:17
1

This probably means a "broken pipe" error. This might happen when one of the processes connected by the pipe exits before the another.

0

I was having a similar issue executing the process in a thread who's run method would read the process InputStream and then call the waitFor() method.

I moved the reading of the processes InputStream into its own thread as the OP has done and was no longer seeing the broken pipe exit code returned.

Dan675
  • 1,697
  • 15
  • 15