Java Runtime.getRuntime (): mendapatkan output dari menjalankan program baris perintah

155

Saya menggunakan runtime untuk menjalankan perintah command prompt dari program Java saya. Namun, saya tidak tahu bagaimana saya bisa mendapatkan output yang dikembalikan oleh perintah.

Ini kode saya:

Runtime rt = Runtime.getRuntime();

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

Process proc = rt.exec(commands);

Saya mencoba melakukan System.out.println(proc);tetapi itu tidak mengembalikan apa pun. Eksekusi perintah itu harus mengembalikan dua angka yang dipisahkan oleh tanda titik koma. Bagaimana saya bisa mendapatkan ini dalam variabel untuk dicetak?

Ini kode yang saya gunakan sekarang:

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

Tapi saya tidak mendapatkan apa pun sebagai output saya, tetapi ketika saya menjalankan sendiri perintah itu berfungsi dengan baik.

pengguna541597
sumber

Jawaban:

244

Inilah cara untuk pergi:

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

Baca Javadoc untuk lebih jelasnya di sini . ProcessBuilderakan menjadi pilihan yang baik untuk digunakan.

Senthil
sumber
4
@AlbertChen pwd && lstidak hanya mengeksekusi satu file, ketika Anda melakukannya dalam sebuah shell, ia mengeksekusi file /bin/pwddan /bin/lsexecutable. Jika Anda ingin melakukan hal-hal seperti itu dalam java Anda harus melakukan sesuatu seperti {"/bin/bash","-c", "pwd && ls"}. Anda mungkin tidak memiliki pertanyaan lagi tetapi orang lain mungkin jadi saya pikir saya akan menjawabnya.
735 Tela
3
Saya pikir membaca dua aliran harus terjadi bersamaan karena jika, seperti dalam kasus Anda, output stdStream akan mengisi buffer, Anda tidak akan dapat membaca aliran kesalahan ..
Li3ro
3
Li3ro sebagian benar. Program yang Anda dengarkan memiliki buffer stdoutdan stderroutput terbatas . Jika Anda tidak mendengarkannya secara bersamaan, salah satunya akan terisi saat Anda membaca yang lain. Program yang sedang Anda dengarkan kemudian akan memblokir mencoba menulis ke buffer yang diisi, sementara di sisi lain program Anda akan memblokir mencoba membaca dari buffer yang tidak akan pernah kembali EOF. Anda harus membaca dari kedua aliran secara bersamaan.
Gili
1
@ Geili Lalu mengapa Li3ro "sebagian" benar? Bukankah Li3ro sepenuhnya dan sepenuhnya benar? Dalam hal ini, saya tidak mengerti mengapa jawaban yang salah ada di sini sejak 2011 dan mengapa memiliki lebih dari 200 suara positif ... Itu membingungkan.
Andrey Tyukin
2
@AndreyTyukin Kamu benar. Semua jawaban saat ini rentan terhadap kebuntuan. Saya mendorong mereka untuk menurunkan mereka untuk memungkinkan jawaban lain mendapatkan visibilitas. Saya memposting jawaban baru untuk ulasan Anda: stackoverflow.com/a/57949752/14731 . Semoga ini benar ...
Gili
68

Cara yang lebih cepat adalah ini:

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

Yang pada dasarnya adalah versi kental ini:

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

Saya tahu pertanyaan ini sudah lama tetapi saya memposting jawaban ini karena saya pikir ini mungkin lebih cepat.

735 Tesla
sumber
4
Terima kasih atas jawaban yang bagus. Mengapa "\\ A" pembatas?
Gottfried
1
Saya tidak sepenuhnya ingat apa logika saya ketika saya awalnya menulis ini. Saya telah menggunakan solusi ini untuk sementara waktu tetapi saya pikir itu karena \Adalam regex berarti awal dari string dan saya harus lolos dari tebasan.
735 Tesla
5
"\ A" adalah karakter bel. "^" adalah awal dari sebuah string dalam regex, dan "$" adalah akhir dari sebuah string dalam regex. Ini adalah karakter yang Anda harapkan tidak terlihat. Pembatas default adalah spasi putih, menurut dokumentasi Java, jadi melakukan ini mungkin akan memuntahkan hasil lengkap dari perintah.
Hank Schultz
11

Selain menggunakan ProcessBuilderseperti yang disarankan Senthil, pastikan untuk membaca dan mengimplementasikan semua rekomendasi When Runtime.exec () tidak akan .

Andrew Thompson
sumber
Cuplikan itu tampaknya tidak menggunakan aliran kesalahan standar (seperti yang direkomendasikan dalam artikel yang ditautkan). Itu juga tidak menggunakan ProcessBuilderseperti yang sekarang direkomendasikan dua kali. Menggunakan a ProcessBuilder, dimungkinkan untuk menggabungkan aliran output & kesalahan untuk membuatnya lebih mudah untuk mengkonsumsi keduanya sekaligus.
Andrew Thompson
11

Jika penggunaan sudah memiliki Apache commons-io tersedia di classpath, Anda dapat menggunakan:

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

Kita juga dapat menggunakan stream untuk mendapatkan output perintah:

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

@Senthil dan @Arend jawaban ( https://stackoverflow.com/a/5711150/2268559 ) disebutkan ProcessBuilder. Berikut adalah contoh menggunakan ProcessBuilderdengan menentukan variabel lingkungan dan folder yang berfungsi untuk perintah:

    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);
nidalpres
sumber
6

Pada saat penulisan ini, semua jawaban lain yang menyertakan kode dapat mengakibatkan kebuntuan.

Proses memiliki buffer terbatas untuk stdoutdanstderr output . Jika Anda tidak mendengarkannya secara bersamaan, salah satunya akan terisi saat Anda mencoba membaca yang lain. Misalnya, Anda bisa menunggu untuk membaca dari stdoutsaat proses menunggu untuk menulis stderr. Anda tidak dapat membaca dari stdoutbuffer karena kosong dan proses tidak dapat menulis kestderr buffer karena penuh. Anda masing-masing saling menunggu selamanya.

Berikut adalah cara yang mungkin untuk membaca output dari suatu proses tanpa risiko kebuntuan:

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

Kuncinya adalah menggunakan ProcessBuilder.redirectErrorStream(true)yang akan mengarahkan ulang stderrke stdoutaliran. Ini memungkinkan Anda membaca aliran tunggal tanpa harus bergantian antara stdoutdan stderr. Jika Anda ingin menerapkan ini secara manual, Anda harus menggunakan stream di dua utas berbeda untuk memastikan Anda tidak pernah memblokir.

Gili
sumber
Oh wow! Saya tidak berharap bahwa Anda akan membalas komentar dengan begitu cepat, apalagi menjawab pertanyaan lama yang abadi ini! :) Saya sekarang mempertimbangkan untuk memulai hadiah. Akan melihat jawaban Anda nanti. Terima kasih!
Andrey Tyukin
1

Jika Anda menulis di Kotlin, Anda dapat menggunakan:

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

Diadaptasi dari jawaban sebelumnya:

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);
}
Derek Zhu
sumber
0

Hampir sama dengan cuplikan lainnya di laman ini tetapi hanya mengatur berbagai hal di atas suatu fungsi , ini dia ...

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

Fungsi kelas:

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;
       }
PYK
sumber
-1

Coba baca InputStreamruntime:

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

Anda mungkin juga perlu membaca aliran kesalahan ( proc.getErrorStream()) jika proses mencetak keluaran kesalahan. Anda dapat mengalihkan aliran kesalahan ke aliran input jika Anda menggunakan ProcessBuilder.

brahmananda Kar
sumber