Search code examples
javashellcommand-linestreamansi-escape

How do command-line interpreters work?


I have been under the impression that processes on the operating system have three standard streams: stdin, stdout, and stderr. I have also thought that text editors like vim work by taking input over stdin and sending ANSI escape characters over stdout. However, my view of how command-line interpreters isn't holding up in this one case:

When I run the command C:\cygwin\bin\bash.exe, I am prompted with:

Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Users\masson>C:\cygwin\bin\bash.exe
bash-3.2$ 

...but when I run it in Java with the following snippet, the stdin stream is empty:

ProcessBuilder pb = new ProcessBuilder("C:\\cygwin\\bin\\bash.exe");
pb.redirectErrorStream(true);
Process proc = pb.start();
final InputStream in = proc.getInputStream();

new Thread(new Runnable() {
  public void run() {
    // Blocks forever...
    in.read(new byte[1024]);
  }
}).start();

What is going on here? I have been told that bash.exe is running in interactive-mode. Does this mean the standard streams aren't being used? How can I still work work with these programs, and ultimately, how could I implement my own version of cmd.exe? I think I am not understanding something fundamental about how command-line interpreters work...

(Any links to articles discussing related subjects would be very much appreciated. I haven't had much luck searching. Oh, and one last question, are standard streams treated any differently in Windows than in most Unix-like operating systems?)


Solution

  • Being in interactive mode doesn't mean that the standard streams aren't being used. But in this case, Bash is most likely running in non-interactive mode (it's detecting that it's not talking directly to the terminal app, so it assumes it's being used programmatically, and therefore doesn't print the welcome banner). In this case the standard streams are still used, it's just that nothing is being output.

    As ergosys pointed out, you can't really rely on in.read(new byte[1024]) returning before it has read the full 1024 bytes, though it's probably ok to assume that it will - however, it certainly won't return before it's read at least one byte, and I think that's the problem here - you're not getting even one byte of output.

    Try passing "-i" to bash to get it to run in interactive mode.