Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running Bash commands in Java

I have the following class. It allows me to execute commands through java.

public class ExecuteShellCommand {  public String executeCommand(String command) {      StringBuffer output = new StringBuffer();      Process p;     try {         p = Runtime.getRuntime().exec(command);         p.waitFor();         BufferedReader reader =                          new BufferedReader(new InputStreamReader(p.getInputStream()));          String line = "";                    while ((line = reader.readLine())!= null) {             output.append(line + "\n");         }      } catch (Exception e) {         e.printStackTrace();     }      return output.toString();  }  } 

When I run commands, the result of the previous command isn't saved. For example:

public static void main(String args[]) {      ExecuteShellCommand com = new ExecuteShellCommand();     System.out.println(com.executeCommand("ls"));     System.out.println(com.executeCommand("cd bin"));     System.out.println(com.executeCommand("ls"));  } 

Gives the output:

bin src   bin src 

Why doesn't the second 'ls' command show the contents of the 'bin' directory?

like image 565
mickzer Avatar asked Nov 09 '14 16:11

mickzer


People also ask

Can we run Linux command in Java?

You can use java. lang. Runtime. exec to run simple code.

Can we run shell script from Java?

If you have a shell script say test.sh then you can run it from a Java program using RunTime class or ProcessBuilder (Note ProcessBuilder is added in Java 5).

What is Bash in Java?

BASH is an acronym for Bourne Again Shell, a punning name, which is a tribute to Bourne Shell (i.e., invented by Steven Bourne). Bash is a shell program written by Brian Fox as an upgraded version of Bourne Shell program 'sh'. It is an open source GNU project.


2 Answers

You start a new process with Runtime.exec(command). Each process has a working directory. This is normally the directory in which the parent process was started, but you can change the directory in which your process is started.

I would recommend to use ProcessBuilder

ProcessBuilder pb = new ProcessBuilder("ls"); pb.inheritIO(); pb.directory(new File("bin")); pb.start(); 

If you want to run multiple commands in a shell it would be better to create a temporary shell script and run this.

public void executeCommands() throws IOException {      File tempScript = createTempScript();      try {         ProcessBuilder pb = new ProcessBuilder("bash", tempScript.toString());         pb.inheritIO();         Process process = pb.start();         process.waitFor();     } finally {         tempScript.delete();     } }  public File createTempScript() throws IOException {     File tempScript = File.createTempFile("script", null);      Writer streamWriter = new OutputStreamWriter(new FileOutputStream(             tempScript));     PrintWriter printWriter = new PrintWriter(streamWriter);      printWriter.println("#!/bin/bash");     printWriter.println("cd bin");     printWriter.println("ls");      printWriter.close();      return tempScript; } 

Of course you can also use any other script on your system. Generating a script at runtime makes sometimes sense, e.g. if the commands that are executed have to change. But you should first try to create one script that you can call with arguments instead of generating it dynamically at runtime.

It might also be reasonable to use a template engine like velocity if the script generation is complex.

EDIT

You should also consider to hide the complexity of the process builder behind a simple interface.

Separate what you need (the interface) from how it is done (the implementation).

public interface FileUtils {     public String[] listFiles(String dirpath); } 

You can then provide implementations that use the process builder or maybe native methods to do the job and you can provide different implementations for different environments like linux or windows.

Finally such an interface is also easier to mock in unit tests.

like image 95
René Link Avatar answered Sep 22 '22 12:09

René Link


You can form one complex bash command that does everything: "ls; cd bin; ls". To make this work you need to explicitly invoke bash. This approach should give you all the power of the bash command line (quote handling, $ expansion, pipes, etc.).

/**  * Execute a bash command. We can handle complex bash commands including  * multiple executions (; | && ||), quotes, expansions ($), escapes (\), e.g.:  *     "cd /abc/def; mv ghi 'older ghi '$(whoami)"  * @param command  * @return true if bash got started, but your command may have failed.  */ public static boolean executeBashCommand(String command) {     boolean success = false;     System.out.println("Executing BASH command:\n   " + command);     Runtime r = Runtime.getRuntime();     // Use bash -c so we can handle things like multi commands separated by ; and     // things like quotes, $, |, and \. My tests show that command comes as     // one argument to bash, so we do not need to quote it to make it one thing.     // Also, exec may object if it does not have an executable file as the first thing,     // so having bash here makes it happy provided bash is installed and in path.     String[] commands = {"bash", "-c", command};     try {         Process p = r.exec(commands);          p.waitFor();         BufferedReader b = new BufferedReader(new InputStreamReader(p.getInputStream()));         String line = "";          while ((line = b.readLine()) != null) {             System.out.println(line);         }          b.close();         success = true;     } catch (Exception e) {         System.err.println("Failed to execute bash with command: " + command);         e.printStackTrace();     }     return success; } 
like image 44
Steve Zobell Avatar answered Sep 23 '22 12:09

Steve Zobell