Đầu tiên, tôi khuyên bạn nên thay thế dòng
Process process = Runtime.getRuntime ().exec ("/bin/bash");
với những dòng
ProcessBuilder builder = new ProcessBuilder("/bin/bash");
builder.redirectErrorStream(true);
Process process = builder.start();
ProcessBuilder là tính năng mới trong Java 5 và giúp chạy các quy trình bên ngoài dễ dàng hơn. Theo tôi, cải tiến đáng kể nhất của nó Runtime.getRuntime().exec()
là nó cho phép bạn chuyển hướng lỗi tiêu chuẩn của quy trình con thành đầu ra tiêu chuẩn của nó. Điều này có nghĩa là bạn chỉ có một InputStream
để đọc. Trước đó, bạn cần có hai Luồng riêng biệt, một đọc từ stdout
và một đọc từ stderr
, để tránh lấp đầy bộ đệm lỗi tiêu chuẩn trong khi bộ đệm đầu ra tiêu chuẩn trống (gây treo tiến trình con) hoặc ngược lại.
Tiếp theo, các vòng lặp (trong đó bạn có hai vòng)
while ((line = reader.readLine ()) != null) {
System.out.println ("Stdout: " + line);
}
chỉ thoát khi reader
, được đọc từ đầu ra tiêu chuẩn của quy trình, trả về phần cuối của tệp. Điều này chỉ xảy ra khi bash
quá trình thoát. Nó sẽ không trả về end-of-file nếu hiện tại không có thêm đầu ra từ quy trình. Thay vào đó, nó sẽ đợi dòng đầu ra tiếp theo từ tiến trình và không quay lại cho đến khi nó có dòng tiếp theo này.
Vì bạn đang gửi hai dòng đầu vào cho quy trình trước khi đến vòng lặp này, nên vòng đầu tiên trong hai vòng này sẽ bị treo nếu quy trình chưa thoát sau hai dòng đầu vào này. Nó sẽ ngồi đó chờ đọc dòng khác, nhưng sẽ không bao giờ có dòng khác để đọc.
Tôi đã biên dịch mã nguồn của bạn (hiện tại tôi đang sử dụng Windows, vì vậy tôi đã thay thế /bin/bash
bằng cmd.exe
, nhưng các nguyên tắc sẽ giống nhau) và tôi thấy rằng:
- sau khi nhập hai dòng, đầu ra từ hai lệnh đầu tiên xuất hiện, nhưng sau đó chương trình bị treo,
- nếu tôi nhập, giả sử,
echo test
và sau đó exit
, chương trình sẽ thoát khỏi vòng lặp đầu tiên kể từ khi cmd.exe
quá trình thoát ra. Sau đó, chương trình yêu cầu một dòng đầu vào khác (bị bỏ qua), bỏ qua thẳng vòng lặp thứ hai vì tiến trình con đã thoát, rồi tự thoát.
- nếu tôi nhập
exit
và sau đó echo test
, tôi nhận được IOException phàn nàn về việc một đường ống bị đóng. Điều này được mong đợi - dòng đầu vào đầu tiên khiến quá trình thoát ra và không có nơi nào để gửi dòng thứ hai.
Tôi đã thấy một thủ thuật thực hiện điều gì đó tương tự như những gì bạn có vẻ muốn, trong một chương trình mà tôi đã từng làm việc. Chương trình này lưu giữ xung quanh một số shell, chạy các lệnh trong đó và đọc kết quả đầu ra từ các lệnh này. Thủ thuật được sử dụng là luôn viết ra một dòng 'ma thuật' đánh dấu sự kết thúc của đầu ra của lệnh shell và sử dụng nó để xác định thời điểm kết thúc đầu ra từ lệnh được gửi đến shell.
Tôi đã lấy mã của bạn và tôi đã thay thế mọi thứ sau dòng gán cho writer
bằng vòng lặp sau:
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;
}
}
Sau khi làm điều này, tôi có thể chạy một vài lệnh một cách đáng tin cậy và có kết quả đầu ra từ mỗi lệnh trở lại cho riêng tôi.
Hai echo --EOF--
lệnh trong dòng được gửi đến trình bao ở đó để đảm bảo rằng đầu ra từ lệnh được kết thúc --EOF--
ngay cả khi kết quả của một lỗi từ lệnh.
Tất nhiên, cách tiếp cận này có những hạn chế của nó. Những hạn chế này bao gồm:
- nếu tôi nhập một lệnh đang chờ người dùng nhập (ví dụ: một trình bao khác), chương trình sẽ bị treo,
- nó giả định rằng mỗi quá trình chạy bởi shell kết thúc đầu ra của nó bằng một dòng mới,
- nó sẽ hơi bối rối nếu lệnh đang được chạy bởi trình bao xảy ra để viết ra một dòng
--EOF--
.
bash
báo lỗi cú pháp và thoát ra nếu bạn nhập một số văn bản với một số không khớp )
.
Những điểm này có thể không quan trọng đối với bạn nếu bất cứ điều gì bạn đang nghĩ về việc chạy như một tác vụ đã lên lịch sẽ bị giới hạn trong một lệnh hoặc một nhóm lệnh nhỏ sẽ không bao giờ hoạt động theo những cách bệnh lý như vậy.
CHỈNH SỬA : cải thiện xử lý thoát và các thay đổi nhỏ khác sau khi chạy phần mềm này trên Linux.