Saya sedang membangun proses di Java menggunakan ProcessBuilder sebagai berikut:
ProcessBuilder pb = new ProcessBuilder()
.command("somecommand", "arg1", "arg2")
.redirectErrorStream(true);
Process p = pb.start();
InputStream stdOut = p.getInputStream();
Sekarang masalah saya adalah sebagai berikut: Saya ingin menangkap apa pun yang terjadi melalui stdout dan / atau stderr dari proses itu dan mengarahkannya ke System.out
asynchronous. Saya ingin proses dan pengalihan keluarannya berjalan di latar belakang. Sejauh ini, satu-satunya cara yang saya temukan untuk melakukan ini adalah dengan menelurkan utas baru secara manual yang akan terus membaca stdOut
dan kemudian memanggil write()
metode yang sesuai dari System.out
.
new Thread(new Runnable(){
public void run(){
byte[] buffer = new byte[8192];
int len = -1;
while((len = stdOut.read(buffer)) > 0){
System.out.write(buffer, 0, len);
}
}
}).start();
Sementara pendekatan semacam itu berhasil, rasanya agak kotor. Dan yang terpenting, ini memberi saya satu utas lagi untuk dikelola dan diakhiri dengan benar. Apakah ada cara yang lebih baik untuk melakukan ini?
sumber
org.apache.commons.io.IOUtils.copy(new ProcessBuilder().command(commandLine) .redirectErrorStream(true).start().getInputStream(), System.out);
Jawaban:
Untuk satu-satunya cara di Java 6 atau sebelumnya adalah dengan apa yang disebut
StreamGobbler
(yang Anda mulai buat):...
Untuk Java 7, lihat jawaban Evgeniy Dorofeev.
sumber
Gunakan
ProcessBuilder.inheritIO
, ini menetapkan sumber dan tujuan untuk subproses standar I / O menjadi sama dengan proses Java saat ini.Jika Java 7 bukan pilihan
Benang akan mati secara otomatis saat subproses selesai, karena
src
akan EOF.sumber
inheritIO()
atau salah satu dariredirect*(ProcessBuilder.Redirect)
metode praktis itu lain kali saya perlu melakukannya pada proyek java 7. Sayangnya proyek saya adalah java 6.sc
perlu ditutup?Solusi fleksibel dengan Java 8 lambda yang memungkinkan Anda menyediakan
Consumer
yang akan memproses keluaran (mis. Mencatatnya) baris demi baris.run()
adalah satu baris tanpa ada pengecualian yang dicentang. Sebagai alternatif untuk menerapkanRunnable
, itu dapat diperluasThread
sebagai gantinya seperti yang disarankan oleh jawaban lain.Anda kemudian dapat menggunakannya sebagai contoh seperti ini:
Di sini aliran keluaran diarahkan ke
System.out
dan aliran kesalahan dicatat pada tingkat kesalahan olehlogger
.sumber
forEach()
inrun()
akan memblokir hingga aliran terbuka, menunggu baris berikutnya. Ini akan keluar saat aliran ditutup.Sesederhana berikut:
oleh .redirectErrorStream (true) Anda memberi tahu proses untuk menggabungkan kesalahan dan aliran keluaran dan kemudian dengan .redirectOutput (file) Anda mengarahkan keluaran gabungan ke file.
Memperbarui:
Saya berhasil melakukan ini sebagai berikut:
Sekarang Anda dapat melihat kedua keluaran dari utas main dan asyncOut di System.out
sumber
Solusi java8 sederhana dengan menangkap keluaran dan pemrosesan reaktif menggunakan
CompletableFuture
:Dan penggunaannya:
sumber
Ada pustaka yang menyediakan ProcessBuilder yang lebih baik, zt-exec. Perpustakaan ini dapat melakukan apa yang Anda minta dan banyak lagi.
Inilah tampilan kode Anda dengan zt-exec dan bukan ProcessBuilder:
tambahkan ketergantungan:
Kode :
Dokumentasi perpustakaan ada di sini: https://github.com/zeroturnaround/zt-exec/
sumber
Saya juga hanya dapat menggunakan Java 6. Saya menggunakan implementasi pemindai thread @ EvgeniyDorofeev. Dalam kode saya, setelah proses selesai, saya harus segera menjalankan dua proses lain yang masing-masing membandingkan keluaran yang dialihkan (pengujian unit berbasis diff untuk memastikan stdout dan stderr sama dengan yang diberkati).
Untaian pemindai tidak segera selesai, bahkan jika saya menunggu () prosesnya selesai. Untuk membuat kode berfungsi dengan benar, saya harus memastikan utas bergabung setelah proses selesai.
sumber
Sebagai tambahan untuk jawaban msangel saya ingin menambahkan blok kode berikut:
Ini memungkinkan untuk mengarahkan aliran input (stdout, stderr) dari proses ke beberapa konsumen lain. Ini mungkin System.out :: println atau apa pun yang menggunakan string.
Pemakaian:
sumber
Kode khusus Anda bukan
...
sumber
Secara default, subproses yang dibuat tidak memiliki terminal atau konsolnya sendiri. Semua operasi I / O standarnya (yaitu stdin, stdout, stderr) akan diarahkan ke proses induk, di mana mereka dapat diakses melalui aliran yang diperoleh menggunakan metode getOutputStream (), getInputStream (), dan getErrorStream (). Proses induk menggunakan aliran ini untuk memberi masukan dan mendapatkan keluaran dari subproses. Karena beberapa platform asli hanya menyediakan ukuran buffer terbatas untuk aliran input dan output standar, kegagalan untuk segera menulis aliran input atau membaca aliran output dari subproses dapat menyebabkan subproses diblokir, atau bahkan kebuntuan.
https://www.securecoding.cert.org/confluence/display/java/FIO07-J.+Do+not+let+external+processes+block+on+IO+buffers
sumber