java runtime.getruntime(), получающий вывод от выполнения программы командной строки
Я использую среду выполнения для запуска команд командной строки из моей программы Java. Однако я не знаю, как я могу получить вывод, который возвращает команда.
Вот мой код:
Runtime rt = Runtime.getRuntime();
String[] commands = {"system.exe" , "-send" , argument};
Process proc = rt.exec(commands);
Я пытался делать System.out.print(proc);
но это ничего не вернуло. Выполнение этой команды должно вернуть два числа, разделенных точкой с запятой, как я могу получить это в переменной для печати?
Вот код, который я использую сейчас:
String[] commands = {"system.exe","-get t"};
Process proc = rt.exec(commands);
InputStream stdin = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stdin);
BufferedReader br = new BufferedReader(isr);
String line = null;
System.out.println("<OUTPUT>");
while ( (line = br.readLine()) != null)
System.out.println(line);
System.out.println("</OUTPUT>");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
Но я ничего не получаю в качестве вывода, но когда я сам запускаю эту команду, она работает нормально.
15 ответов
Вот путь:
Runtime rt = Runtime.getRuntime();
String[] commands = {"system.exe","-get t"};
Process proc = rt.exec(commands);
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(proc.getErrorStream()));
// read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
// read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
Лучше прочитать Javadoc для более подробной информации здесь. ProcessBuilder
будет хорошим выбором для использования
Более быстрый способ это:
public static String execCmd(String cmd) throws java.io.IOException {
java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
Который в основном является сжатой версией этого:
public static String execCmd(String cmd) throws java.io.IOException {
Process proc = Runtime.getRuntime().exec(cmd);
java.io.InputStream is = proc.getInputStream();
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
String val = "";
if (s.hasNext()) {
val = s.next();
}
else {
val = "";
}
return val;
}
Я знаю, что этот вопрос старый, но я публикую этот ответ, потому что я думаю, что это может быть быстрее.
Если вы уже используете Apache commons-io на classpath, вы можете использовать:
Process p = new ProcessBuilder("cat", "/etc/something").start();
String stderr = IOUtils.toString(p.getErrorStream(), Charset.defaultCharset());
String stdout = IOUtils.toString(p.getInputStream(), Charset.defaultCharset());
На момент написания все остальные ответы, содержащие код, могут привести к тупикам.
Процессы имеют ограниченный буфер для stdout
а также stderr
выход. Если вы не слушаете их одновременно, один из них заполнится, пока вы пытаетесь прочитать другой. Например, вы можете ждать, чтобы прочитатьstdout
пока процесс ожидает записи в stderr
. Вы не можете читать изstdout
буфер, потому что он пуст и процесс не может писать в stderr
буфер, потому что он заполнен. Вы ждете друг друга вечно.
Вот возможный способ чтения вывода процесса без риска возникновения взаимоблокировок:
public final class Processes
{
private static final String NEWLINE = System.getProperty("line.separator");
/**
* @param command the command to run
* @return the output of the command
* @throws IOException if an I/O error occurs
*/
public static String run(String... command) throws IOException
{
ProcessBuilder pb = new ProcessBuilder(command).redirectErrorStream(true);
Process process = pb.start();
StringBuilder result = new StringBuilder(80);
try (BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())))
{
while (true)
{
String line = in.readLine();
if (line == null)
break;
result.append(line).append(NEWLINE);
}
}
return result.toString();
}
/**
* Prevent construction.
*/
private Processes()
{
}
}
Ключ в том, чтобы использовать ProcessBuilder.redirectErrorStream(true)
который перенаправит stderr
в stdout
поток. Это позволяет вам читать один поток без необходимости переключаться междуstdout
а также stderr
. Если вы хотите реализовать это вручную, вам придется использовать потоки в двух разных потоках, чтобы никогда не блокироваться.
Помимо использования ProcessBuilder
как предложил Senthil, обязательно прочитайте и реализуйте все рекомендации, когда Runtime.exec() не будет.
Также мы можем использовать потоки для получения вывода команды:
public static void main(String[] args) throws IOException {
Runtime runtime = Runtime.getRuntime();
String[] commands = {"free", "-h"};
Process process = runtime.exec(commands);
BufferedReader lineReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
lineReader.lines().forEach(System.out::println);
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
errorReader.lines().forEach(System.out::println);
}
@Senthil и @Arend answer ( /questions/36186951/java-runtimegetruntime-poluchayuschij-vyivod-ot-vyipolneniya-programmyi-komandnoj-stroki/36186958#36186958) упомянули ProcessBuilder. Вот пример использования ProcessBuilder с указанием переменных среды и рабочей папки для команды:
ProcessBuilder pb = new ProcessBuilder("ls", "-a", "-l");
Map<String, String> env = pb.environment();
// If you want clean environment, call env.clear() first
// env.clear()
env.put("VAR1", "myValue");
env.remove("OTHERVAR");
env.put("VAR2", env.get("VAR1") + "suffix");
File workingFolder = new File("/home/user");
pb.directory(workingFolder);
Process proc = pb.start();
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
// read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null)
{
System.out.println(s);
}
// read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null)
{
System.out.println(s);
}
Создать класс:
public class Utils {
public static final String SHEL_EXECUTE_ERROR = "SHEL_EXECUTE_ERROR";
public static String shellExec(String cmdCommand) {
final StringBuilder stringBuilder = new StringBuilder();
try {
final Process process = Runtime.getRuntime().exec(cmdCommand);
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
}
} catch (Exception e) {
return SHEL_EXECUTE_ERROR;
}
return stringBuilder.toString();
}
}
и используйте:
final String shellExec = shellExec("cmd /c ver");
final String versionOS = shellExec.equals(SHEL_EXECUTE_ERROR) ? "empty" : shellExec;
Если вы пишете на Kotlin, вы можете использовать:
val firstProcess = ProcessBuilder("echo","hello world").start()
val firstError = firstProcess.errorStream.readBytes().decodeToString()
val firstResult = firstProcess.inputStream.readBytes().decodeToString()
Вы можете попробовать мойjaxec
библиотека:
import com.yegor256.Jaxec;
String stdout = new Jaxec("date", "+%Y").exec();
assert stdout.equals("2003");
Под капотом он используетRuntime
, но также регистрирует его вывод, проверяет значение кода выхода и ждет завершения процесса.
Process p = Runtime.getRuntime().exec("ping google.com");
p.getInputStream().transferTo(System.out);
p.getErrorStream().transferTo(System.out);
Чтобы надежно запустить подпроцесс, вам необходимо обрабатывать выходные потоки одновременно, иначе процесс заблокируется, когда STDOUT или STDERR не используются, когда они заполняются до предела буфера по умолчанию.
Вы можете продемонстрировать эту проблему с помощью этих тестовых команд, которые записывают большой объем данных в STDOUT и STDERR с одинаковой скоростью. Если ваше приложение не успевает за чтением из обоих этих потоков, подпроцесс зависнет/заблокируется:
// WINDOWS:
String[] commands = {"cmd.exe", "/c", "FOR /L %X IN (1, 1, 10000) DO echo Hello STDOUT %X && echo Hello STDERR %X 1>&2"};
// Linux / Unix style OS
String[] commands = {"/bin/bash", "-c", "for i in {1..10000} ; do echo Hello STDERR $i 1>&2 ; echo Hello STDOUT $i; done"};
Вы можете избежать этой проблемы, используяProcessBuilder
что дает лучший контроль над тем, куда идут выходные потоки, и предотвращает ситуацию взаимоблокировки, вызываяpb.redirectErrorStream(true)
илиpb.inheritIO()
или перенаправить любой из STDOUT/STDERR наFile
с использованиемpb.redirectOutput/Error(file)
/или использовать разные потоки для чтения из STDOUT и STDERR.
Вот простой пример того, как обрабатывать запуск, который можно использовать вместоRuntime.exec()
и отправляет STDOUT(/STDERR) в любой передаваемый вами поток, что позволяет избежать ситуации взаимоблокировки:
// Example:
start(command, null, System.out, null);
// or
start(command, null, System.out, System.err);
// Don't forget to close streams you pass in - if appropriate
public static int start(String[] cmd, byte[] stdin, OutputStream stdout, OutputStream stderr)
throws IOException, InterruptedException
{
Objects.requireNonNull(cmd);
Objects.requireNonNull(stdout);
System.out.println("start "+Arrays.toString(cmd));
// Launch and wait:
ProcessBuilder pb = new ProcessBuilder(cmd);
if (stderr == null) {
pb.redirectErrorStream(true); // No STDERR => merge to STDOUT
}
Process p = pb.start();
// Consumes STDERR at same time as STDOUT, not doing this large streams can block I/O in the sub-process
Thread bg = null;
if (stderr != null) {
Runnable task = () -> {
try(var from = p.getErrorStream()) {
from.transferTo(stderr);
} catch(IOException io) {
throw new UncheckedIOException(io);
}
};
bg = new Thread(task, "STDERR");
bg.start();
}
// Send STDIN if required, and close STDIN stream
// NOTE!!! a huge input stream can lock up STDOUT/STDERR readers, you may need a background thread here too
try(OutputStream os = p.getOutputStream()) {
if (stdin != null) os.write(stdin);
}
// Move STDOUT to the output stream
try(var stdo = p.getInputStream()) {
stdo.transferTo(stdout);
}
int rc = p.waitFor();
if (bg != null) {
bg.join();
}
System.out.println("start "+Arrays.toString(cmd));
System.out.println("Exit "+p.pid()+" CODE "+rc +' '+(rc == 0 ? "OK":"**** ERROR ****")+" "+(stderr == null ? "STDERR>OUT":""));
return rc;
}
Практически то же самое, что и другие фрагменты на этой странице, но просто упорядочивая функции по функциям, поехали...
String str=shell_exec("ls -l");
Функция класса:
public String shell_exec(String cmd)
{
String o=null;
try
{
Process p=Runtime.getRuntime().exec(cmd);
BufferedReader b=new BufferedReader(new InputStreamReader(p.getInputStream()));
String r;
while((r=b.readLine())!=null)o+=r;
}catch(Exception e){o="error";}
return o;
}
Адаптировано из предыдущего ответа
public static String execCmdSync(String cmd, CmdExecResult callback) throws java.io.IOException, InterruptedException {
RLog.i(TAG, "Running command:", cmd);
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmd);
//String[] commands = {"system.exe","-get t"};
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
StringBuffer stdout = new StringBuffer();
StringBuffer errout = new StringBuffer();
// read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
stdout.append(s);
}
// read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
System.out.println(s);
errout.append(s);
}
if (callback == null) {
return stdInput.toString();
}
int exitVal = proc.waitFor();
callback.onComplete(exitVal == 0, exitVal, errout.toString(), stdout.toString(), cmd);
return stdInput.toString();
}
public interface CmdExecResult{
void onComplete(boolean success, int exitVal, String error, String output, String originalCmd);
}
Попробуйте прочитать InputStream среды выполнения:
Runtime rt = Runtime.getRuntime();
String[] commands = {"system.exe","-send",argument};
Process proc = rt.exec(commands);
BufferedReader br = new BufferedReader(
new InputStreamReader(proc.getInputStream()));
String line;
while ((line = br.readLine()) != null)
System.out.println(line);
}
Вам также может понадобиться прочитать поток ошибок (proc.getErrorStream()
) если в процессе печати выводится ошибка. Вы можете перенаправить поток ошибок во входной поток, если вы используете ProcessBuilder
,