Home > Blockchain >  File not found when calling bash from Java
File not found when calling bash from Java

Time:12-03

I'm having the next error when execute a cmd command using Java. I'm working in a mac laptop. This is my code:

private static String exportContainerFromImage(String container) {
    //docker export mysql_dummy > ~/Documents/mysql_dummy.tar
    String errorMessage = "";
    String[] cmdArgs =
        {"docker export mysql_dummy > ~/Documents/mysql_dummy.tar", "bash"};
     Process process = Runtime.getRuntime().exec(cmdArgs);
  }

But I'm getting the error, error=2, No such file or directory, if I execute the command directly on the terminal it runs successfully, I tried also changing the directory to ~\\Documents\\mysql_dummy.tar and got the same result.

But if I run the command with the arguments:

{"docker create -ti --name mysql_dummy mysql", "bash"};

It runs properly

Any ideas?

CodePudding user response:

You're conflating 'shell magic' with 'an OS'. Also, you seem to be wildly confused about what the array form of cmdArgs does, because you've tagged a bash in there at the end. That array is supposed to contain the executable's full path at arr[0], and all arguments at arr[1] and up. docker create -ti ... is clearly not a filename, and bash is clearly not an argument.

Shell magic?

If you type:

docker create -ti --name mysql_dummy mysql

on the command line, bash (or cmd.exe if on windows, or whatever shell you are using) reads it and does a whole bunch of replacement magic and parsing on this. It's the shell that does this, not the OS, and java's processbuilder stuff is not a shell and therefore isn't going to do all that. What you're attempting to do? Run that entire line as if it's a single file name that is executable which it clearly isn't.

This is all shell magic - all things that you CANNOT do with exec. Fortunately, java is a programming language, so you can do all these things by, well, programming it.

  • Parsing out params by splitting on whitespace.
  • quoting to avoid that splitting, but then removing the quotes.
  • Treating ~ as a ref to a homedir.
  • Replacing * and ? in filename paths.
  • Variable substitution
  • Setting up redirects with > somefile.txt or 2> /dev/null or < file.in or whatnot.

You must do those things.

In addition, exec cannot be used to this, period. As usual, the only non-problematic way to run processes is to always use ProcessBuilder, no exceptions. Consider runtime.exec a known-broken method you must never call.

ProcessBuilder lets you redirect the output.

String[] cmdArgs = {
  "/bin/docker" // note, FULL PATH!
  "export",
  "mysql_dummy"
};

File out = new File(System.getProperty("user.home"), "Documents/mysql_dummy.tar");

ProcessBuilder pb = new ProcessBuilder(cmdArgs);
pb.redirectOutput(new File(out));
pb.start();

That does what you want, presumably.

The alternative is to make a script (script.sh or script.bat) and then start bash or cmd.exe and ask it to run that script.

String[] args = { "/bin/bash", "-c", "/fully/qualified/path/to/the/script.sh" }

and then exec that. Now you can pile *.txt, > foobar.txt, ~/homediref, and all the other shellisms in that script as much as you like.

CodePudding user response:

Overall @rzwiterloot's answer is good, but there are some alternatives it leaves out.

First, what I would consider the best solution to this problem: the -o option to docker export.

"docker", "export", "mysql_dummy", "-o", "Documents/mysql_dummy.tar"

Ignoring ~/ here, this set of command and arguments will achieve the same thing as ... > Documents/mysql_dummy.tar but doesn't rely on the shell for the redirection; docker export is perfectly capable of handling that operation itself.

Second, if you wanted to run a shell command from the program, you could. I would not recommend this. But in certain circumstances it might make sense.

The alternative is to make a script

You don't have to put the command in a separate file. Actually this is one inaccuracy in @rzwiterloot's answer; -c allows you to pass command(s) to bash, not the path to a file containing commands.

"bash", "-c", "docker export mysql_dummy > ~/Documents/mysql_dummy.tar"

However, I'd recommend you avoid invoking shells from any program you write. They're quirky and esoteric and there's almost always a simpler way to achieve what you want, such as docker export's -o optiopn, in this case.

  • Related