开发者

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 Thre开发者_StackOverflow中文版ad(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?)


Any program using the c standard library can tell if it is talking to a tty device (a.k.a command line) using the function isatty(). Bash probably detects that it is talking to a pipe instead of a tty and doesn't output a prompt.


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.


I'm more a Python guy than a Java guy (so everything I tell you is quick guesses from JavaDoc), but it looks like you're setting up a multi-process deadlock.

in.read(new byte[1024]); won't return until it's read 1024 bytes of data and bash.exe doesn't output a whole 1024 bytes before stopping to wait for input. (To do that, use proc.getOutputStream() and feed it some commands to respond to.)

As a result, you get Java waiting for bash to respond and bash waiting for Java to respond and both perfectly content to wait until the death of the universe without getting bored or tired.

My advice is to use in.available() before each call to in.read() to avoid blocking. That way, you can switch back and forth between feeding data in and pulling it out without getting stuck.

In fact, it'd probably be a lot simpler and saner to just wrap it in a BufferedReader.

Update from comment: Also, when tools like bash detect that stdin isn't a terminal (see the isatty system call), they buffer in huge (4K or more) chunks on the assumption that the input is non-interactive. I'm not sure if it'll help, but try starting bash with the -i flag.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜