命令行解释器如何工作?
我一直认为操作系统上的进程具有三个标准流:stdin、stdout 和 stderr。我还认为像 vim 这样的文本编辑器通过 stdin
获取输入并通过 stdout
发送 ANSI 转义字符来工作。但是,我对命令行解释器的看法在这种情况下不起作用:
当我运行命令 C:\cygwin\bin\bash.exe
时,系统会提示我:
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$
..但是当我使用以下代码片段在 Java 中运行它时,标准输入流为空:
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();
这里发生了什么?我被告知 bash.exe 正在交互模式下运行。这是否意味着没有使用标准流?我如何仍然可以使用这些程序,最终如何实现我自己的 cmd.exe 版本?我认为我不了解有关命令行解释器如何工作的基本知识...
(非常感谢任何讨论相关主题的文章的链接。我没有太多的搜索运气。哦,最后一个问题是标准的Windows 中的流处理方式与大多数类 Unix 操作系统中的流处理方式有何不同?)
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?)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
任何使用 c 标准库的程序都可以使用 isatty() 函数判断它是否正在与 tty 设备(也称为命令行)通信。 Bash 可能检测到它正在与管道而不是 tty 通信,并且不会输出提示。
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.
处于交互模式并不意味着不使用标准流。但在这种情况下,Bash 很可能以非交互模式运行(它检测到它没有直接与终端应用程序对话,因此它假设它是以编程方式使用的,因此不会打印欢迎横幅)。在这种情况下,仍然使用标准流,只是没有输出任何内容。
正如 ergosys 指出的那样,您不能真正依赖
in.read(new byte[1024])
在读取完整的 1024 字节之前返回,尽管可以假设它会 - 然而,在读取至少一个字节之前它肯定不会返回,我认为这就是这里的问题 - 你甚至没有得到一个字节的输出。尝试将“-i”传递给 bash 以使其以交互模式运行。
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.
我更喜欢Python而不是Java(所以我告诉你的一切都是来自JavaDoc的快速猜测),但看起来你正在设置一个多进程死锁。
in.read(new byte[1024]);
在读取 1024 字节数据之前不会返回,并且bash.exe
在停止之前不会输出整个 1024 字节等待输入。 (为此,请使用 proc.getOutputStream() 并向其提供一些命令来响应。)因此,您会得到 Java 等待 bash 响应和 bash 等待 Java 响应,两者都完美满足于等到宇宙死亡而不感到无聊或疲倦。
我的建议是使用
in.available()
在每次调用in.read()
之前以避免阻塞。这样,您就可以在输入数据和拉出数据之间来回切换,而不会卡住。事实上,将其包装在
BufferedReader
。评论更新:此外,当 bash 等工具检测到 stdin 不是终端时(请参阅 isatty 系统调用),假设输入是非交互式的,它们会以巨大(4K 或更多)块的形式进行缓冲。我不确定它是否有帮助,但尝试使用 -i 标志启动 bash。
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 andbash.exe
doesn't output a whole 1024 bytes before stopping to wait for input. (To do that, useproc.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 toin.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.