У меня есть следующий пример кода ниже. Таким образом, вы можете ввести команду в оболочку bash, т. echo test
Е. И вернуть результат. Однако после первого прочтения. Другие выходные потоки не работают?
Почему это или я что-то не так делаю? Моя конечная цель - создать запланированную задачу Threaded, которая периодически выполняет команду для / bash, чтобы OutputStream
и InputStream
должны были работать в тандеме и не переставать работать. У меня тоже возникла ошибка. Какие- java.io.IOException: Broken pipe
нибудь идеи?
Благодарю.
String line;
Scanner scan = new Scanner(System.in);
Process process = Runtime.getRuntime ().exec ("/bin/bash");
OutputStream stdin = process.getOutputStream ();
InputStream stderr = process.getErrorStream ();
InputStream stdout = process.getInputStream ();
BufferedReader reader = new BufferedReader (new InputStreamReader(stdout));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
String input = scan.nextLine();
input += "\n";
writer.write(input);
writer.flush();
input = scan.nextLine();
input += "\n";
writer.write(input);
writer.flush();
while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}
input = scan.nextLine();
input += "\n";
writer.write(input);
writer.close();
while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}
Ответы:
Во-первых, я бы рекомендовал заменить строчку
Process process = Runtime.getRuntime ().exec ("/bin/bash");
с линиями
ProcessBuilder builder = new ProcessBuilder("/bin/bash"); builder.redirectErrorStream(true); Process process = builder.start();
ProcessBuilder - новинка Java 5, которая упрощает выполнение внешних процессов. На мой взгляд, его наиболее значительным улучшением
Runtime.getRuntime().exec()
является то, что он позволяет перенаправлять стандартную ошибку дочернего процесса в его стандартный вывод. Это означает, что вам нужноInputStream
читать только один . До этого вам нужно было иметь два отдельных потока, один для чтенияstdout
и один для чтенияstderr
, чтобы избежать заполнения стандартного буфера ошибок, когда стандартный выходной буфер был пуст (что приводило к зависанию дочернего процесса), или наоборот.Далее петли (их у вас две)
while ((line = reader.readLine ()) != null) { System.out.println ("Stdout: " + line); }
выходить только тогда
reader
, когда , который читает из стандартного вывода процесса, возвращает конец файла. Это происходит только тогда, когдаbash
процесс завершается. Он не вернет конец файла, если в настоящее время не будет вывода из процесса. Вместо этого он будет ждать следующей строки вывода из процесса и не вернется, пока не получит эту следующую строку.Поскольку вы отправляете в процесс две строки ввода до достижения этого цикла, первый из этих двух циклов зависнет, если процесс не завершился после этих двух строк ввода. Он будет ждать, пока будет прочитана следующая строка, но никогда не будет другой строки для чтения.
Я скомпилировал ваш исходный код (сейчас я использую Windows, поэтому я заменил его
/bin/bash
наcmd.exe
, но принципы должны быть такими же), и я обнаружил, что:echo test
а затемexit
, программа выходит из первого цикла с моментаcmd.exe
выхода из процесса. Затем программа запрашивает другую строку ввода (которая игнорируется), сразу пропускает второй цикл, поскольку дочерний процесс уже завершился, а затем завершает работу.exit
а затемecho test
, я получаю исключение IOException с жалобой на закрытие канала. Этого и следовало ожидать - первая строка ввода привела к завершению процесса, а вторую строку отправить некуда.Я видел трюк, который делает что-то похожее на то, что вам кажется, в программе, над которой я работал. Эта программа содержала несколько оболочек, запускала в них команды и считывала вывод этих команд. Используемый трюк состоял в том, чтобы всегда записывать «волшебную» строку, которая отмечает конец вывода команды оболочки, и использовать ее для определения того, когда вывод команды, отправленной в оболочку, завершился.
Я взял ваш код и заменил все после строки, которая
writer
относится к следующему циклу:while (scan.hasNext()) { String input = scan.nextLine(); if (input.trim().equals("exit")) { // Putting 'exit' amongst the echo --EOF--s below doesn't work. writer.write("exit\n"); } else { writer.write("((" + input + ") && echo --EOF--) || echo --EOF--\n"); } writer.flush(); line = reader.readLine(); while (line != null && ! line.trim().equals("--EOF--")) { System.out.println ("Stdout: " + line); line = reader.readLine(); } if (line == null) { break; } }
После этого я мог надежно запустить несколько команд, и результат каждой из них возвращался мне индивидуально.
Две
echo --EOF--
команды в строке, отправленной в оболочку, предназначены для того, чтобы гарантировать, что вывод команды завершается--EOF--
даже в результате ошибки команды.Конечно, у этого подхода есть свои ограничения. Эти ограничения включают:
--EOF--
.bash
сообщает о синтаксической ошибке и завершает работу, если вы вводите текст с несоответствующим)
.Эти моменты могут не иметь для вас значения, если то, что вы думаете о запуске в качестве запланированной задачи, будет ограничено командой или небольшим набором команд, которые никогда не будут вести себя таким патологическим образом.
РЕДАКТИРОВАТЬ : улучшить обработку выхода и другие незначительные изменения после запуска этого в Linux.
источник
Я думаю, вы можете использовать поток, такой как демон-поток, для чтения вашего ввода, и ваш выходной ридер уже будет в цикле while в основном потоке, поэтому вы можете читать и писать одновременно. Вы можете изменить свою программу следующим образом:
Thread T=new Thread(new Runnable() { @Override public void run() { while(true) { String input = scan.nextLine(); input += "\n"; try { writer.write(input); writer.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } ); T.start();
и вы можете читатель будет таким же, как указано выше, т.е.
while ((line = reader.readLine ()) != null) { System.out.println ("Stdout: " + line); }
сделайте свой писатель окончательным, иначе он не будет доступен внутреннему классу.
источник
У вас есть
writer.close();
в вашем коде. Итак, bash получает EOFstdin
и закрывается. Тогда вы получитеBroken pipe
при попытке прочитать изstdout
несуществующего bash.источник