Java Runtime.getR.78 (): nhận đầu ra từ việc thực hiện chương trình dòng lệnh


155

Tôi đang sử dụng thời gian chạy để chạy các lệnh nhắc lệnh từ chương trình Java của mình. Tuy nhiên, tôi không biết làm thế nào tôi có thể nhận được đầu ra mà lệnh trả về.

Đây là mã của tôi:

Runtime rt = Runtime.getRuntime();

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

Process proc = rt.exec(commands);

Tôi đã cố gắng làm System.out.println(proc);nhưng điều đó không trả lại bất cứ điều gì. Việc thực hiện lệnh đó sẽ trả về hai số được phân tách bằng dấu chấm phẩy. Làm thế nào tôi có thể nhận được điều này trong một biến để in ra?

Đây là mã tôi đang sử dụng bây giờ:

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

Nhưng tôi không nhận được bất cứ điều gì như đầu ra của mình, nhưng khi tôi tự chạy lệnh đó thì nó hoạt động tốt.

Câu trả lời:


244

Đây là con đường để đi:

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

Đọc Javadoc để biết thêm chi tiết tại đây . ProcessBuildersẽ là một lựa chọn tốt để sử dụng.


4
@AlbertChen pwd && lskhông chỉ thực thi một tệp duy nhất, khi bạn thực hiện điều đó trong một trình bao, nó thực thi cả tệp thực thi /bin/pwd/bin/lsthực thi. Nếu bạn muốn làm những thứ như thế trong java, bạn sẽ cần phải làm một cái gì đó như thế {"/bin/bash","-c", "pwd && ls"}. Bạn có thể không có câu hỏi nữa nhưng những người khác có thể vì vậy tôi nghĩ tôi có thể trả lời nó.
735Tesla

3
Tôi nghĩ rằng việc đọc hai luồng phải xảy ra đồng thời bởi vì, như trong trường hợp của bạn, đầu ra của stdStream sẽ lấp đầy bộ đệm, bạn sẽ không thể đọc được luồng lỗi ..
Li3ro

3
Li3ro đúng một phần. Chương trình bạn đang nghe có bộ đệm stdoutstderrđầu ra hạn chế . Nếu bạn không lắng nghe họ đồng thời, một trong số họ sẽ lấp đầy trong khi bạn đang đọc cái khác. Chương trình bạn đang nghe sau đó sẽ chặn việc cố ghi vào bộ đệm đã điền, trong khi ở đầu bên kia, chương trình của bạn sẽ chặn cố gắng đọc từ bộ đệm sẽ không bao giờ quay lại EOF. Bạn phải đọc từ cả hai luồng đồng thời.
Gili

1
@Gili Vậy tại sao Li3ro "một phần" phải không? Không phải Li3ro hoàn toàn và hoàn toàn đúng sao? Trong trường hợp này, tôi không hiểu tại sao một câu trả lời sai được treo ở đây kể từ năm 2011 và tại sao nó có hơn 200 câu trả lời ... Thật khó hiểu.
Andrey Tyukin

2
@AndreyTyukin Bạn nói đúng. Tất cả các câu trả lời hiện tại dễ bị bế tắc. Tôi khuyến khích họ hạ cấp họ để cho phép các câu trả lời khác đạt được khả năng hiển thị. Tôi đã đăng một câu trả lời mới cho đánh giá của bạn: stackoverflow.com/a/57949752/14731 . Hy vọng rằng tôi đã hiểu đúng ...
Gili

68

Một cách nhanh hơn là đây:

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

Về cơ bản, đây là phiên bản thu gọn của điều này:

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

Tôi biết câu hỏi này đã cũ nhưng tôi đang đăng câu trả lời này vì tôi nghĩ rằng câu hỏi này có thể nhanh hơn.


4
Cảm ơn câu trả lời tốt đẹp. Tại sao "\\ A" là dấu phân cách?
Gottfried

1
Tôi hoàn toàn không nhớ logic của tôi là gì khi tôi viết bài này. Tôi đã sử dụng giải pháp này được một thời gian nhưng tôi nghĩ rằng đó là vì \Atrong một biểu thức chính quy có nghĩa là bắt đầu chuỗi và tôi phải thoát khỏi dấu gạch chéo.
735Tesla

5
"\ A" là ký tự chuông. "^" là bắt đầu của một chuỗi trong regex và "$" là kết thúc của một chuỗi trong regex. Đây là một nhân vật bạn sẽ không thấy. Dấu phân cách mặc định là khoảng trắng, theo tài liệu Java, do đó, việc này có thể sẽ tạo ra kết quả đầy đủ của lệnh.
Hank Schultz

11

Bên cạnh việc sử dụng ProcessBuildernhư Senthil được đề xuất, hãy nhớ đọc và thực hiện tất cả các đề xuất của Khi Runtime.exec () sẽ không .


Đoạn mã đó dường như không tiêu thụ luồng lỗi tiêu chuẩn (như được đề xuất trong bài viết được liên kết). Nó cũng không sử dụng ProcessBuildernhư được đề nghị hai lần. Sử dụng a ProcessBuilder, có thể hợp nhất các luồng đầu ra & lỗi để dễ dàng tiêu thụ cả hai cùng một lúc.
Andrew Thompson

11

Nếu việc sử dụng đã có sẵn Apache commons-io trên đường dẫn lớp, bạn có thể sử dụng:

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

Ngoài ra chúng ta có thể sử dụng các luồng để có được đầu ra lệnh:

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 và @Arend trả lời ( https://stackoverflow.com/a/5711150/2268559 ) đã đề cập ProcessBuilder. Dưới đây là ví dụ sử dụng ProcessBuildervới chỉ định biến môi trường và thư mục làm việc cho lệnh:

    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

Tại thời điểm viết bài này, tất cả các câu trả lời khác bao gồm mã có thể dẫn đến bế tắc.

Các quy trình có một bộ đệm giới hạn cho stdoutstderrđầu ra. Nếu bạn không lắng nghe họ đồng thời, một trong số họ sẽ lấp đầy trong khi bạn đang cố đọc phần còn lại. Ví dụ, bạn có thể chờ để đọc từ stdouttrong khi quá trình đang chờ để ghi stderr. Bạn không thể đọc từ stdoutbộ đệm vì nó trống và quá trình không thể ghi vào stderrbộ đệm vì nó đã đầy. Bạn đang chờ đợi nhau mãi mãi.

Đây là một cách có thể để đọc đầu ra của một quá trình mà không có nguy cơ bế tắc:

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

Điều quan trọng là sử dụng ProcessBuilder.redirectErrorStream(true)sẽ chuyển hướng stderrvào stdoutluồng. Điều này cho phép bạn đọc một luồng mà không phải xen kẽ giữastdoutstderr. Nếu bạn muốn thực hiện điều này bằng tay, bạn sẽ phải sử dụng các luồng trong hai luồng khác nhau để đảm bảo bạn không bao giờ chặn.


Tuyệt vời! Tôi không mong đợi rằng bạn sẽ trả lời bình luận nhanh như vậy, chứ đừng nói đến câu trả lời cũ này! :) Bây giờ tôi đang xem xét bắt đầu một tiền thưởng. Sẽ xem xét câu trả lời của bạn sau. Cảm ơn!
Andrey Tyukin

1

Nếu bạn viết trên Kotlin, bạn có thể sử dụng:

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

0

Chuyển thể từ câu trả lời trước:

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

Khá giống với các đoạn khác trên trang này nhưng chỉ tổ chức mọi thứ qua một chức năng , ở đây chúng tôi đi ...

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

Hàm Class:

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

Hãy thử đọc InputStreamthời gian chạy:

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

Bạn cũng có thể cần đọc luồng lỗi ( proc.getErrorStream()) nếu quá trình đang in đầu ra lỗi. Bạn có thể chuyển hướng luồng lỗi sang luồng đầu vào nếu bạn sử dụng ProcessBuilder.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.