Java Runtime.getRuntime (): bir komut satırı programını yürüterek çıktı alma


155

Java programımdan komut istemi komutlarını çalıştırmak için çalışma zamanını kullanıyorum. Ancak, komutun döndürdüğü çıktıyı nasıl alabileceğimin farkında değilim.

İşte benim kod:

Runtime rt = Runtime.getRuntime();

String[] commands = {"system.exe", "-send" , argument};

Process proc = rt.exec(commands);

Yapmaya çalıştım System.out.println(proc);ama bu bir şey döndürmedi. Bu komutun yürütülmesi, noktalı virgülle ayrılmış iki sayı döndürmelidir. Bunu çıktı almak için bir değişkende nasıl alabilirim?

İşte şimdi kullanıyorum kodu:

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);

Ama çıktı olarak hiçbir şey almıyorum, ama bu komutu kendim çalıştırdığımda iyi çalışıyor.

Yanıtlar:


244

İşte yol:

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);
}

Daha fazla ayrıntı için Javadoc'u buradan okuyun . ProcessBuilderkullanmak için iyi bir seçim olacaktır.


4
@ AlbertChen pwd && lssadece tek bir dosya yürütmekle kalmaz , bunu bir kabukta yaptığınızda hem /bin/pwdve hem de /bin/lsyürütülebilir dosyaları yürütür . Java içinde böyle şeyler yapmak istiyorsanız, böyle bir şey yapmanız gerekir {"/bin/bash","-c", "pwd && ls"}. Muhtemelen artık sorunuz yok ama başkaları da cevap verebilir diye düşündüm.
735Tesla

3
Bence senin gibi, stdStream çıkışı tampon dolduracak eğer, hata akışını okuyamayacak çünkü iki akışı okuma aynı anda olması gerektiğini düşünüyorum ..
Li3ro

3
Li3ro kısmen haklıdır. Dinlediğiniz program için sınırlı bir arabellek stdoutve stderrçıktı vardır. Onları aynı anda dinlemezseniz, diğerini okurken bunlardan biri dolacaktır. Dinlediğiniz program doldurulmuş ara belleğe yazmaya çalışmayı engellerken, diğer taraftan programınız asla geri dönmeyecek bir arabellekten okumaya çalışmayı engeller EOF. Sen gerekir eşzamanlı hem derelerden okuyun.
Gili

1
@Gili O zaman Li3ro neden "kısmen" haklı? Li3ro tamamen ve tamamen doğru değil mi? Bu durumda, 2011'den beri neden yanlış bir cevabın burada asılı olduğunu ve neden 200'den fazla oy hakkı olduğunu anlamıyorum ... Bu kafa karıştırıcı.
Andrey Tyukin

2
@AndreyTyukin Haklısın. Mevcut tüm cevaplar kilitlenmelere karşı savunmasızdır. Diğer cevapların görünürlük kazanmalarına izin vermek için onları küçümsemelerini teşvik ediyorum. İncelemeniz için yeni bir yanıt gönderdim: stackoverflow.com/a/57949752/14731 . Umarım bu haklıyım ...
Gili

68

Daha hızlı bir yol şudur:

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() : "";
}

Temel olarak bunun yoğun bir sürümü olan:

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;
}

Bu sorunun eski olduğunu biliyorum ama bu cevabı gönderiyorum çünkü bunun daha hızlı olabileceğini düşünüyorum.


4
Güzel cevap için teşekkürler. "\\ A" neden sınırlayıcıdır?
Gottfried

1
Bunu yazarken mantığımın ne olduğunu tam olarak hatırlamıyorum. Bir süredir bu çözümü kullanıyorum ama bence bunun nedeni \Adüzenli ifadede ipin başlaması anlamına geliyor ve eğik çizgiden kaçmak zorunda kaldım.
735Tesla

5
"\ A" zil karakteridir. "^" normal ifadedeki bir dizenin başlangıcı ve "$" normal ifadedeki bir dizenin sonudur. Bu görmemeyi umduğunuz bir karakter. Java belgelerine göre varsayılan sınırlayıcı boşluktur, bu nedenle bunu yapmak muhtemelen komutun tüm sonucunu tükürür.
Hank Schultz

11

ProcessBuilderÖnerilen Senthil'i kullanmanın yanı sıra , When Runtime.exec () 'in yapmayacağı tüm önerileri okuduğunuzdan ve uyguladığınızdan emin olun .


Bu snippet, standart hata akışını tüketmiyor gibi görünüyor (bağlantılı makalede önerildiği gibi). Ayrıca şu ProcessBuilderanda iki kez tavsiye edildiği gibi bir kullanmıyor . A kullanarak ProcessBuilder, her ikisini birden tüketmeyi kolaylaştırmak için çıkış ve hata akışlarını birleştirmek mümkündür.
Andrew Thompson

11

Kullanım zaten sınıf yolunda Apache ortakları varsa, şunları kullanabilirsiniz:

Process p = new ProcessBuilder("cat", "/etc/something").start();
String stderr = IOUtils.toString(p.getErrorStream(), Charset.defaultCharset());
String stdout = IOUtils.toString(p.getInputStream(), Charset.defaultCharset());

7

Ayrıca komut çıktısını almak için akışları kullanabiliriz:

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);
    }

7

@Senthil ve @Arend yanıtı ( https://stackoverflow.com/a/5711150/2268559 ) belirtildi ProcessBuilder. ProcessBuilderKomut için ortam değişkenlerini ve çalışma klasörünü belirterek kullanılan örnek :

    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);

6

Bu yazının yazıldığı sırada, kod içeren diğer tüm yanıtlar kilitlenmelere neden olabilir.

İşlemler için sınırlı bir tampon stdoutve stderrçıktıya sahiptir. Onları aynı anda dinlemezseniz, diğerini okumaya çalıştığınızda bunlardan biri dolacaktır. Örneğin stdout, işlem yazmak için beklerken okumayı bekleyebilirsiniz stderr. stdoutBoş olduğundan ve dolu olduğundan işlem arabelleğe yazamadığı için arabellekten okuyamazsınız stderr. Hepiniz sonsuza dek birbirinizi bekliyorsunuz.

Kilitlenme riski olmadan bir işlemin çıktısını okumanın olası bir yolu:

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()
    {
    }
}

Anahtar kullanım için ProcessBuilder.redirectErrorStream(true)yönlendirir stderriçine stdoutakışı. Bu, stdoutve arasında geçiş yapmak zorunda kalmadan tek bir akışı okumanızı sağlar stderr. Bunu manuel olarak uygulamak istiyorsanız, asla engellemediğinizden emin olmak için akışları iki farklı iş parçacığında tüketmeniz gerekir .


Vay canına! Bu ebediyen eski soruya cevap vermek yerine, yoruma bu kadar çabuk cevap vermenizi beklemiyordum! :) Şimdi bir ödül kazanmayı düşünüyorum. Cevabınıza daha sonra bir göz atacağım. Teşekkürler!
Andrey Tyukin

1

Kotlin'e yazarsanız şunları kullanabilirsiniz:

val firstProcess = ProcessBuilder("echo","hello world").start()
val firstError = firstProcess.errorStream.readBytes().decodeToString()
val firstResult = firstProcess.inputStream.readBytes().decodeToString()

0

Önceki yanıttan uyarlanmıştır:

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);
}

0

Bu sayfadaki diğer snippet'lerle hemen hemen aynı, ancak sadece bir işlev üzerinde bir şeyler düzenliyoruz , işte başlıyoruz ...

String str=shell_exec("ls -l");

Sınıf işlevi:

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;
       }

-1

Çalışma InputStreamzamanını okumayı deneyin :

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()İşlem hata çıktısını yazdırıyorsa hata akışını ( ) da okumanız gerekebilir . Kullanıyorsanız, hata akışını giriş akışına yönlendirebilirsiniz ProcessBuilder.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.