Как работают интерпретаторы командной строки? - PullRequest
4 голосов
/ 05 сентября 2010

У меня сложилось впечатление, что процессы в операционной системе имеют три стандартных потока: stdin, stdout, and stderr. Я также подумал, что текстовые редакторы, такие как vim, работают, принимая входные данные через stdin и отправляя управляющие символы ANSI через stdout. Однако мой взгляд на то, как интерпретаторы командной строки не выдерживают в этом одном случае:

Когда я запускаю команду 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-подобных операционных систем? )

Ответы [ 3 ]

4 голосов
/ 05 сентября 2010

Любая программа, использующая стандартную библиотеку c, может определить, обращается ли она к устройству tty (она же командная строка), используя функцию isatty ().Bash, вероятно, обнаруживает, что он обращается к каналу, а не к tty, и не выводит подсказку.

1 голос
/ 06 сентября 2010

Находясь в интерактивном режиме, не означает, что стандартные потоки не используются.Но в этом случае Bash, скорее всего, работает в не -интерактивном режиме (он обнаруживает, что не общается напрямую с терминальным приложением, поэтому он предполагает, что он используется программно, и, следовательно, не выводит приветствиебаннер).В этом случае стандартные потоки все еще используются, просто ничего не выводится.

Как указывал ergosys, вы не можете полагаться на возврат in.read(new byte[1024]) до того, как он прочитает полные 1024 байта, хотявероятно, можно предположить, что это произойдет - однако, безусловно, он не вернется, пока не прочитает хотя бы один байт, и я думаю, что здесь проблема - вы не получаете ни одного байта вывода.

Попробуйте передать "-i" bash, чтобы он работал в интерактивном режиме.

1 голос
/ 05 сентября 2010

Я скорее парень из 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 или больше) куски в предположении, что ввод не является интерактивным. Я не уверен, поможет ли это, но попробуйте запустить bash с флагом -i.

...