命令行解释器是如何工作的?

4

我一直以为操作系统上的进程有三个标准流: 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操作系统中是否被处理得有所不同?)


编写一个Java CLI交互式程序会很有趣。 - TheLQ
在运行Java程序之前,您需要在shell中关闭输入缓冲区(并在之后打开它以便编辑命令)。有些模糊的stty命令或其他我记不清的东西。 - Tom Hawtin - tackline
3个回答

4

任何使用C标准库的程序都可以使用函数isatty()来判断它是否正在与tty设备(也称为命令行)交互。Bash可能会检测到它正在与管道而不是tty交互,因此不会输出提示符。


1
aye: $ echo tty | bash 返回 "not a tty"。 - msw

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系统调用)时,它们会缓冲大量(4K或更多)的块,假设输入是非交互式的。我不确定它是否有帮助,但尝试使用-i标志启动bash。


抱歉,实际上我是在一个新线程中执行in.read操作,但为了简洁起见,我忘记将其包含在内。我会进行编辑。 - peskal
啊。我的诊断在某种程度上仍然成立。你是在发送命令到bash吗?...带有行终止符?...这是Windows上的Cygwin所期望的类型吗?(不确定它是否需要\n还是\r\n) - ssokolow
从javadoc中:“实际读取的字节数以整数形式返回。此方法会阻塞,直到输入数据可用、检测到文件结尾或抛出异常。”我已经在其他程序上使用了这个命令,甚至是cygwin的vim-nox.exe,我总是收到输出。关于您正在解决的问题,请参见我的另一个问题:http://stackoverflow.com/questions/3641407/runtime-getruntime-execc-cygwin-bin-bash-exe-has-no-input-to-read对于这个问题,我只是想问一下进程是否可以通过除stdout和stderr之外的任何东西发送数据。 - peskal
啊,对不起。据我所知(我经常在Linux上编写shell脚本),bash从未通过除stdout/stderr之外的其他方式输出,但是如果它检测到stdin不是终端(请参见isatty系统调用),它会在假定输入是非交互式的情况下缓冲大量(显然是4K或更多)的块。http://mywiki.wooledge.org/BashFAQ/009 我不确定它是否有帮助,但请尝试使用-i标志启动bash。 - ssokolow
那正是问题所在。非常感谢! - peskal

1

处于交互模式并不意味着标准流不被使用。但在这种情况下,Bash 很可能是以非交互模式运行的(它检测到它没有直接与终端应用程序通信,因此假定它是以编程方式使用的,因此不打印欢迎横幅)。在这种情况下,标准流仍然被使用,只是没有任何输出。

正如 ergosys 指出的那样,你不能真正依赖于 in.read(new byte[1024]) 在读取完整的 1024 字节之前返回,尽管假设它会这样做可能是可以的,但它肯定不会在读取至少一个字节之前返回,我认为这就是问题所在——你甚至没有得到一个字节的输出。

尝试传递 "-i" 给 bash 以使其在交互模式下运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接