3

I am using apache commons exec to run command: arp | wc -l Below is my code:

 private String runCommand(String cmd, String params) {
        CommandLine commandLine = new CommandLine(cmd);
        commandLine.addArguments(params);
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        ByteArrayOutputStream stderr = new ByteArrayOutputStream();
        PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(stdout, stderr);
        ExecuteWatchdog watchdog = new ExecuteWatchdog(30000);  // 30s timeout
        DefaultExecutor executor = new DefaultExecutor();
        executor.setStreamHandler(pumpStreamHandler);
        executor.setWatchdog(watchdog);

        try {
            int retCode = executor.execute(commandLine);
            System.out.println("Executed '" + cmd + "'\n"
                    + "returnCode: " + retCode + "\n"
                    + "stdout:\n" + stdout.toString() + "\n"
                    + "stderr:\n" + stderr.toString());

            if (retCode == 0) {
                return stdout.toString();
            } else {
                throw new NonZeroExitStatusReturnedException(commandLine.toString(), retCode);
            }

        } catch (IOException e) {
            throw new RuntimeException("Could not run command "+ commandLine.toString(), e);
        }
}

Here the cmd is /bin/sh and params are -c arp|wc-l The code is giving following output:

Executed '/bin/sh'
returnCode: 0
stdout:
       54       71       4321

stderr:
usage: arp [-n] [-i interface] hostname
       arp [-n] [-i interface] [-l] -a
       arp -d hostname [pub] [ifscope interface]
       arp -d [-i interface] -a
       arp -s hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]
       arp -S hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]
       arp -f filename

I have two questions here:
Question 1. I cannot understand that why there are three numbers in my output (54 71 4321). Isnt it supposed to be only one number?

Question2. Is there any better way to run the same command using apache commons exec?

ojas
  • 2,150
  • 5
  • 22
  • 37

2 Answers2

2

After reading the documentation carefully - https://commons.apache.org/proper/commons-exec/apidocs/org/apache/commons/exec/CommandLine.html.
Here's the answer for Question2:

private String runCommand(String cmd, String[] params) {
        CommandLine commandLine = new CommandLine(cmd);
        commandLine.addArguments(params, false);
        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
        ByteArrayOutputStream stderr = new ByteArrayOutputStream();
        PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(stdout, stderr);
        ExecuteWatchdog watchdog = new ExecuteWatchdog(30000);  // 30s timeout
        DefaultExecutor executor = new DefaultExecutor();
        executor.setStreamHandler(pumpStreamHandler);
        executor.setWatchdog(watchdog);

        try {
            int retCode = executor.execute(commandLine);
            System.out.println("Executed '" + cmd + "'\n"
                    + "returnCode: " + retCode + "\n"
                    + "stdout:\n" + stdout.toString() + "\n"
                    + "stderr:\n" + stderr.toString());

            if (retCode == 0) {
                return stdout.toString();
            } else {
                throw new NonZeroExitStatusReturnedException(commandLine.toString(), retCode);
            }

        } catch (IOException e) {
            throw new RuntimeException("Could not run command "+ commandLine.toString(), e);
        }
}

Here cmd is: /bin/sh params is: new String[] { "-c", "arp|wc -l" }

ojas
  • 2,150
  • 5
  • 22
  • 37
  • 2
    Would be nice, if you would note the difference between your problem and your solution code: `commandLine.addArgument(params, false);` Because it is really hard to get the difference otherwise. – Murmel Feb 26 '19 at 10:30
2

Regarding your second question, I built a pure Java solution:

/**
Run command $cmd1 and pipe its STDOUT to $cmd2, just like executing:

$ cmd1 | cmd2

from a Linux shell
*/
private String runCommand(String cmd1, String[] params1, 
                          String cmd2, String[] params2) {

    PipedOutputStream stdin = new PipedOutputStream();
    PipedInputStream stdout = new PipedInputStream();
    // - pipe STDOUT of first process to STDIN of second process
    stdin.connect(stdout);

    // - First process: arp
    CommandLine commandLine = new CommandLine(cmd1).addArgument(params1);

    DefaultExecutor executor = new DefaultExecutor();
    executor.setStreamHandler(new PumpStreamHandler(stdin));
    executor.execute(commandLine, new DefaultExecuteResultHandler());

    // - Second process: wc
    CommandLine commandLine2 = new CommandLine(cmd2).addArguments(params2);

    DefaultExecutor executor2 = new DefaultExecutor();
    executor2.setStreamHandler(new PumpStreamHandler(System.out, System.err,
                                                     stdout));
    executor2.execute(commandLine2, new DefaultExecuteResultHandler());
}

This omits the need for the pipe ('|'), which will enhance the portability and security (it makes it easier to perform sanitization checks) of your code.

Murmel
  • 5,402
  • 47
  • 53