Java Runtime.getRuntime (): การรับเอาต์พุตจากการเรียกใช้งานโปรแกรมบรรทัดคำสั่ง


155

ฉันใช้รันไทม์เพื่อเรียกใช้คำสั่งที่พร้อมรับคำสั่งจากโปรแกรม Java ของฉัน อย่างไรก็ตามฉันไม่ทราบว่าจะรับเอาต์พุตที่คำสั่งส่งคืนได้อย่างไร

นี่คือรหัสของฉัน:

Runtime rt = Runtime.getRuntime();

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

Process proc = rt.exec(commands);

ฉันพยายามทำSystem.out.println(proc);แต่ไม่ได้ส่งคืนอะไร การดำเนินการของคำสั่งนั้นควรคืนค่าสองตัวเลขโดยคั่นด้วยเครื่องหมายอัฒภาค ฉันจะรับสิ่งนี้ในตัวแปรเพื่อพิมพ์ออกมาได้อย่างไร

นี่คือรหัสที่ฉันใช้ตอนนี้:

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

แต่ฉันไม่ได้รับสิ่งใดเป็นผลลัพธ์ของฉัน แต่เมื่อฉันรันคำสั่งนั้นเองมันก็ใช้ได้ดี

คำตอบ:


244

นี่คือวิธีที่จะไป:

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

อ่าน Javadoc สำหรับรายละเอียดเพิ่มเติมที่นี่ ProcessBuilderจะเป็นทางเลือกที่ดีในการใช้


4
@AlbertChen pwd && lsไม่เพียงแค่เรียกใช้ไฟล์เดียวเมื่อคุณทำเช่นนั้นในเชลล์มันจะเรียกใช้ทั้งสอง/bin/pwdและ/bin/lsไฟล์ที่เรียกทำงานได้ หากคุณต้องการทำสิ่งต่าง ๆ ภายในจาวาคุณจะต้องทำสิ่ง{"/bin/bash","-c", "pwd && ls"}นั้น คุณอาจไม่มีคำถามอีกต่อไป แต่คนอื่นอาจจะคิดว่าฉันอาจตอบก็ได้
735 เทสลา

3
ฉันคิดว่าการอ่านทั้งสองสตรีมจะต้องเกิดขึ้นพร้อมกันเพราะถ้าเช่นในกรณีของคุณเอาต์พุตของ stdStream จะเติมบัฟเฟอร์คุณจะไม่สามารถอ่านสตรีมข้อผิดพลาดได้
Li3ro

3
Li3ro ถูกต้องเพียงบางส่วน โปรแกรมที่คุณกำลังฟังมีบัฟเฟอร์ที่ จำกัด สำหรับstdoutและstderrส่งออก หากคุณไม่ฟังพวกเขาพร้อมกันหนึ่งในนั้นจะเติมเต็มในขณะที่คุณกำลังอ่านอีก โปรแกรมที่คุณกำลังฟังแล้วจะปิดกั้นความพยายามที่จะเขียนไปยังบัฟเฟอร์เต็มไปด้วยขณะที่ในส่วนอื่น ๆ EOFของโปรแกรมจะป้องกันการพยายามที่จะอ่านจากบัฟเฟอร์ที่จะไม่กลับ คุณต้องอ่านจากสตรีมทั้งสองพร้อมกัน
Gili

1
@Gili แล้วทำไม Li3ro ถึงมีบางส่วน Li3ro นั้นสมบูรณ์และถูกต้องใช่ไหม? ในกรณีนี้ฉันไม่เข้าใจว่าทำไมคำตอบที่ไม่ถูกต้องถูกแขวนไว้ที่นี่ตั้งแต่ปี 2011 และทำไมมันถึงมีผู้โหวตมากกว่า 200 คน ... มันสับสน
Andrey Tyukin

2
@AndreyTyukin คุณพูดถูก คำตอบปัจจุบันทั้งหมดมีความเสี่ยงต่อการหยุดชะงัก ฉันแนะนำให้พวกเขาลงคะแนนพวกเขาเพื่อให้คำตอบอื่น ๆ สามารถมองเห็นได้ ผมโพสต์คำตอบใหม่สำหรับความคิดเห็นของคุณ: stackoverflow.com/a/57949752/14731 หวังว่าฉันจะได้รับสิทธินี้ ...
Gili

68

วิธีที่เร็วกว่าคือ:

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

ซึ่งโดยทั่วไปแล้วเป็นเวอร์ชันย่อของ:

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

ฉันรู้ว่าคำถามนี้เก่า แต่ฉันโพสต์คำตอบนี้เพราะฉันคิดว่าอาจเร็วกว่านี้


4
ขอบคุณสำหรับคำตอบที่ดี ทำไม "\\ A" ตัวคั่น
กอทฟริด

1
ฉันจำไม่ได้ว่าตรรกะของฉันคืออะไรตอนที่ฉันเขียนสิ่งนี้ ฉันใช้วิธีนี้มาระยะหนึ่งแล้ว แต่ฉันคิดว่ามันเป็นเพราะ\Aใน regex หมายถึงจุดเริ่มต้นของสตริงและฉันต้องหนีจากเครื่องหมายทับ
735 เทสลา

5
"\ A" เป็นตัวอักษรระฆัง "^" คือจุดเริ่มต้นของสตริงใน regex และ "$" เป็นจุดสิ้นสุดของสตริงใน regex นี่คือตัวละครที่คุณคาดหวังว่าจะไม่เห็น ตัวคั่นเริ่มต้นคือช่องว่างตามเอกสาร Java ดังนั้นการทำเช่นนี้อาจพ่นผลเต็มรูปแบบของคำสั่ง
Hank Schultz

11

นอกจากการใช้ProcessBuilderเป็น Senthil ที่แนะนำแล้วอย่าลืมอ่านและปฏิบัติตามคำแนะนำทั้งหมดของWhen Runtime.exec ()ด้วย


ข้อมูลโค้ดนั้นดูเหมือนจะไม่ใช้สตรีมข้อผิดพลาดมาตรฐาน (ตามที่แนะนำในบทความที่ลิงก์) นอกจากนี้ยังไม่ได้ใช้ a ProcessBuilderตามที่แนะนำตอนนี้สองครั้ง เมื่อใช้ a ProcessBuilderจะสามารถรวมเอาท์พุท & สตรีมข้อผิดพลาดเพื่อให้ง่ายต่อการใช้ทั้งสองพร้อมกัน
Andrew Thompson

11

หากการใช้งานมี Apache Commons-io อยู่แล้วใน classpath คุณสามารถใช้:

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

นอกจากนี้เราสามารถใช้สตรีมเพื่อรับเอาต์พุตคำสั่ง:

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 และ @Arend คำตอบ ( https://stackoverflow.com/a/5711150/2268559 ) ProcessBuilderกล่าวถึง นี่คือตัวอย่างการใช้ProcessBuilderกับการระบุตัวแปรสภาพแวดล้อมและโฟลเดอร์การทำงานสำหรับคำสั่ง:

    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

ในขณะที่เขียนนี้คำตอบอื่น ๆ ทั้งหมดที่มีรหัสอาจส่งผลให้เกิดการหยุดชะงัก

กระบวนการมีบัฟเฟอร์ จำกัด สำหรับstdoutและstderrส่งออก หากคุณไม่ฟังพวกเขาพร้อมกันหนึ่งในนั้นจะเติมเต็มในขณะที่คุณพยายามอ่านอีก ตัวอย่างเช่นคุณอาจจะรออ่านจากในขณะที่กระบวนการที่กำลังรอที่จะเขียนถึงstdout stderrคุณไม่สามารถอ่านจากstdoutบัฟเฟอร์เนื่องจากมันว่างเปล่าและกระบวนการไม่สามารถเขียนไปยังstderrบัฟเฟอร์ได้เนื่องจากเต็ม คุณแต่ละคนต่างรอคอยกันตลอดไป

นี่เป็นวิธีที่เป็นไปได้ในการอ่านผลลัพธ์ของกระบวนการโดยไม่มีความเสี่ยงของการหยุดชะงัก:

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

กุญแจสำคัญคือการใช้ProcessBuilder.redirectErrorStream(true)ซึ่งจะเปลี่ยนเส้นทางstderrในstdoutกระแส นี้จะช่วยให้คุณอ่านกระแสเดียวโดยไม่ต้องสลับกันระหว่างและstdout stderrหากคุณต้องการใช้งานด้วยตนเองคุณจะต้องใช้สตรีมในสองเธรดที่แตกต่างกันเพื่อให้แน่ใจว่าคุณจะไม่ปิดกั้น


โอ้ว้าว! ฉันไม่ได้คาดหวังว่าคุณจะตอบความคิดเห็นอย่างรวดเร็วปล่อยให้คนเดียวตอบคำถามเก่านี้ชั่วนิรันดร์! :) ตอนนี้ฉันกำลังพิจารณาที่จะเริ่มรับรางวัล จะดูคำตอบของคุณในภายหลัง ขอบคุณ!
Andrey Tyukin

1

ถ้าคุณเขียนบน Kotlin คุณสามารถใช้:

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

0

ดัดแปลงมาจากคำตอบก่อนหน้า:

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

ค่อนข้างคล้ายกับตัวอย่างอื่น ๆในหน้านี้ แต่เพียงจัดการสิ่งต่างๆผ่านฟังก์ชั่นเราไปกันเลย ...

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

ฟังก์ชั่นระดับ:

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

ลองอ่านInputStreamรันไทม์:

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

คุณอาจต้องอ่านสตรีมข้อผิดพลาด ( proc.getErrorStream()) หากกระบวนการกำลังพิมพ์เอาต์พุตข้อผิดพลาด ProcessBuilderคุณสามารถเปลี่ยนเส้นทางกระแสข้อผิดพลาดในการสตรีมใส่ถ้าคุณใช้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.