我一直认为操作系统上的进程有三个标准流:stdin, stdout, and 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中运行它时,stdin流是空的:
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操作系统中的处理方式不同? )
答案 0 :(得分:4)
任何使用c标准库的程序都可以使用isatty()函数判断它是否正在与tty设备(a.k.a命令行)通信。 Bash可能检测到它正在与管道而不是tty通话,并且不会输出提示。
答案 1 :(得分:1)
我更像是一个Python人而不是Java人(所以我告诉你的一切都是来自JavaDoc的快速猜测),但看起来你正在设置一个多进程死锁。
in.read(new byte[1024]);
在读取1024字节数据之前不会返回,bash.exe
在停止等待输入之前不输出整个1024字节。 (为此,请使用proc.getOutputStream()
并提供一些命令来响应。)
结果,你得到Java等待bash响应和bash等待Java响应,并且两者完全满足于等到宇宙死亡而不会感到厌倦或疲倦。
我的建议是在每次调用in.read()
之前使用in.available()
以避免阻止。这样,您可以在输入数据和拉出数据之间来回切换而不会卡住。
事实上,将它包装在BufferedReader
中可能要简单得多,而且更加精明。
从评论更新:此外,当像bash这样的工具检测到stdin不是终端时(请参阅isatty系统调用),they buffer处于巨大(4K或更多)块状态假设输入是非交互式的。我不确定它是否有帮助,但尝试使用-i标志启动bash。
答案 2 :(得分:1)
处于交互模式并不意味着没有使用标准流。但在这种情况下,Bash最有可能以非 - 交互模式运行(它检测到它不是直接与终端应用程序对话,因此它假设它是以编程方式使用的,因此不打印欢迎横幅)。在这种情况下,仍然使用标准流,只是没有输出任何东西。
正如ergosys指出的那样,在读取完整的1024字节之前你不能真正依赖in.read(new byte[1024])
返回,虽然它可能会假设它会 - 但是,它肯定不会在它之前返回读取至少一个字节,我认为这就是问题 - 你甚至没有得到一个字节的输出。
尝试将“-i”传递给bash以使其以交互模式运行。