【问题标题】:How do command-line interpreters work?命令行解释器如何工作?
【发布时间】:2011-04-08 09:15:12
【问题描述】:

我一直认为操作系统上的进程具有三个标准流: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$ 

...但是当我使用以下 sn-p 在 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 操作系统中的处理方式有什么不同吗? )

【问题讨论】:

  • 写一个Java CLI交互程序会很有趣
  • 您需要在运行 java 程序之前关闭 shell 中的输入缓冲(然后再将其重新打开,以便您可以编辑命令)。一些晦涩的stty 命令或我不记得的东西。

标签: java shell command-line stream ansi


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    我更像一个 Python 人而不是 Java 人(所以我告诉你的一切都是从 JavaDoc 快速猜测的),但看起来你正在设置多进程死锁。

    in.read(new byte[1024]); 在读取 1024 字节数据之前不会返回,并且bash.exe 在停止等待输入之前不会输出整个 1024 字节。 (为此,请使用 proc.getOutputStream() 并向其提供一些命令以对其进行响应。)

    因此,您可以让 Java 等待 bash 响应,而 bash 等待 Java 响应,并且两者都非常满足于等到宇宙死亡而不会感到无聊或疲倦。

    我的建议是在每次调用in.read() 之前使用in.available() 以避免阻塞。这样,您就可以在输入数据和提取数据之间来回切换而不会卡住。

    事实上,将它包装在BufferedReader 中可能会更简单、更明智。

    评论更新:此外,当 bash 等工具检测到标准输入不是终端时(请参阅 isatty 系统调用),they buffer 假设为大块(4K 或更多)输入是非交互式的。我不确定它是否会有所帮助,但请尝试使用 -i 标志启动 bash。

    【讨论】:

    • 抱歉,我实际上是在一个新线程中执行 in.read,但为简洁起见,我将其省略了。我会进行编辑。
    • 啊。我的诊断在某种程度上仍然成立。您是否正在向 bash 发送命令? ...带线路终结器? ... Windows 上的 Cygwin 期望的那种? (不确定是要 \n 还是 \r\n)
    • 来自 javadoc:“实际读取的字节数以整数形式返回。此方法阻塞,直到输入数据可用、检测到文件结尾或引发异常。”我在其他程序上使用过这个命令,甚至是 cygwin 的 vim-nox.exe,而且我总是收到输出。对于您要解决的问题,请参阅我的另一个问题:stackoverflow.com/questions/3641407/… 对于这个问题,我实际上只是询问进程是否可以通过 stdout 和 stderr 以外的任何东西发送数据。
    • 啊,抱歉。据我所知(我在 Linux 上写过一些 shell 脚本),bash 从不通过 stdout/stderr 以外的东西输出,但如果它检测到 stdin 不是终端(参见 isatty 系统调用),它会缓冲假设输入是非交互式的,巨大的(显然是 4K 或更多)块。 mywiki.wooledge.org/BashFAQ/009 我不确定它是否有帮助,但尝试使用 -i 标志启动 bash。
    • 这正是问题所在。非常感谢!
    【解决方案3】:

    任何使用 c 标准库的程序都可以使用函数 isatty() 判断它是否正在与 tty 设备(也称为命令行)通信。 Bash 可能检测到它正在与管道而不是 tty 对话并且不输出提示。

    【讨论】:

    • aye: $ echo tty | bash 产生“不是 tty”
    猜你喜欢
    • 2014-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多