Saya memiliki contoh kode berikut di bawah ini. Dimana Anda bisa memasukkan perintah ke bash shell ie echo test
dan hasilnya akan di-echo kembali. Namun, setelah dibaca dulu. Aliran keluaran lainnya tidak berfungsi?
Mengapa ini atau saya melakukan sesuatu yang salah? Tujuan akhir saya adalah untuk membuat tugas terjadwal Berulir yang menjalankan perintah secara berkala ke / bash sehingga OutputStream
dan InputStream
harus bekerja bersama-sama dan tidak berhenti bekerja. Saya juga telah mengalami kesalahan java.io.IOException: Broken pipe
ide?
Terima kasih.
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);
}
Jawaban:
Pertama, saya akan merekomendasikan mengganti baris
Process process = Runtime.getRuntime ().exec ("/bin/bash");
dengan garis
ProcessBuilder builder = new ProcessBuilder("/bin/bash"); builder.redirectErrorStream(true); Process process = builder.start();
ProcessBuilder baru di Java 5 dan membuat proses eksternal berjalan lebih mudah. Menurut pendapat saya, peningkatan paling signifikannya
Runtime.getRuntime().exec()
adalah memungkinkan Anda untuk mengarahkan kesalahan standar dari proses anak ke dalam keluaran standarnya. Ini berarti Anda hanya memiliki satuInputStream
untuk dibaca. Sebelum ini, Anda perlu memiliki dua Thread terpisah, satu membaca daristdout
dan satu membaca daristderr
, untuk menghindari pengisian buffer kesalahan standar sementara buffer keluaran standar kosong (menyebabkan proses anak hang), atau sebaliknya.Selanjutnya, loop (yang Anda miliki dua)
while ((line = reader.readLine ()) != null) { System.out.println ("Stdout: " + line); }
hanya keluar ketika
reader
, yang membaca dari keluaran standar proses, mengembalikan akhir file. Ini hanya terjadi ketikabash
proses keluar. Ini tidak akan mengembalikan akhir file jika saat ini tidak ada lagi output dari proses. Sebaliknya, ia akan menunggu baris keluaran berikutnya dari proses dan tidak kembali sampai ia memiliki baris berikutnya ini.Karena Anda mengirim dua baris input ke proses sebelum mencapai loop ini, loop pertama dari dua loop ini akan hang jika proses belum keluar setelah dua baris input ini. Itu akan duduk di sana menunggu baris lain untuk dibaca, tetapi tidak akan pernah ada baris lain untuk dibaca.
Saya mengkompilasi kode sumber Anda (saya menggunakan Windows saat ini, jadi saya menggantinya
/bin/bash
dengancmd.exe
, tetapi prinsipnya harus sama), dan saya menemukan bahwa:echo test
dan kemudianexit
, program membuatnya keluar dari loop pertama sejakcmd.exe
proses telah keluar. Program kemudian meminta baris input lain (yang diabaikan), langsung melewati loop kedua karena proses turunan sudah keluar, dan kemudian keluar sendiri.exit
dan kemudianecho test
, saya mendapatkan IOException yang mengeluh tentang pipa yang ditutup. Ini sudah biasa - baris pertama input menyebabkan proses keluar, dan tidak ada tempat untuk mengirim baris kedua.Saya telah melihat trik yang melakukan sesuatu yang mirip dengan apa yang tampaknya Anda inginkan, dalam program yang dulu saya kerjakan. Program ini menyimpan sejumlah shell, menjalankan perintah di dalamnya dan membaca keluaran dari perintah ini. Trik yang digunakan adalah selalu menulis baris 'ajaib' yang menandai akhir keluaran perintah shell, dan menggunakannya untuk menentukan kapan keluaran dari perintah yang dikirim ke shell telah selesai.
Saya mengambil kode Anda dan saya mengganti semuanya setelah baris yang ditetapkan
writer
dengan loop berikut: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; } }
Setelah melakukan ini, saya dapat dengan andal menjalankan beberapa perintah dan keluaran dari masing-masing perintah kembali kepada saya secara individual.
Dua
echo --EOF--
perintah dalam baris yang dikirim ke shell ada di sana untuk memastikan bahwa keluaran dari perintah diakhiri dengan--EOF--
bahkan akibat kesalahan dari perintah.Tentu saja, pendekatan ini memiliki keterbatasan. Batasan ini meliputi:
--EOF--
.bash
melaporkan kesalahan sintaks dan keluar jika Anda memasukkan beberapa teks dengan yang tidak cocok)
.Poin-poin ini mungkin tidak masalah bagi Anda jika apa pun yang Anda pikirkan untuk dijalankan sebagai tugas terjadwal akan dibatasi pada sebuah perintah atau sekumpulan kecil perintah yang tidak akan pernah berperilaku patologis seperti itu.
EDIT : tingkatkan penanganan keluar dan perubahan kecil lainnya setelah menjalankan ini di Linux.
sumber
Saya rasa Anda dapat menggunakan thread seperti demon-thread untuk membaca masukan Anda dan pembaca keluaran Anda sudah akan berada dalam loop sementara di thread utama sehingga Anda dapat membaca dan menulis pada saat yang sama. Anda dapat memodifikasi program Anda seperti ini:
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();
dan Anda bisa pembaca akan sama seperti di atas yaitu
while ((line = reader.readLine ()) != null) { System.out.println ("Stdout: " + line); }
Jadikan penulis Anda final jika tidak maka tidak akan dapat diakses oleh kelas dalam.
sumber
Anda memiliki
writer.close();
kode Anda. Jadi bash menerima EOF saatstdin
keluar. Kemudian Anda dapatkanBroken pipe
saat mencoba membaca daristdout
pesta yang sudah tidak berfungsi.sumber