1

While trying to copy file that I create in a .jar script that I run in Windows Subsystems for Linux (WSL) Ubuntu 16.04, I receive the following error:

Bad execution: cp: cannot stat '"/mnt/e/18-09-19': No such file or directory

Bad execution: cp: cannot stat 'Document': No such file or directory

Bad execution: cp: cannot stat 'something/something/SomeThing/PublicCodeLibrary/Java/mweCopy0/vars"': No such file or directory

Or, if the command is executed without quotation marks in the source path:

Bad execution: cp: cannot open '/mnt/e/18-09-19' for reading: Permission denied

Bad execution: cp: cannot stat 'Document': No such file or directory

Bad execution: cp: cannot stat 'something/something/Something/PublicCodeLibrary/Java/mweCopy0/vars': No such file or directory

However, to verify it works, I also print the command to terminal, and when I copy paste it/manually execute it, it does work, as it changes the /usr/share/taskd/pki/vars file content.

So I built a minimal working example (MWE) and I run the script/MWE with the command: java -jar mweCopy0.jar.

package mweCopy0;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.util.ArrayList;

public class mweCopy0 {

    public static void main(String[] args) {
        String vars = "vars";
        char quotation = (char)34; // quotation mark "
        String serverName = "exampleName";

        //get the path of this file
        String linuxPath = getJarLocation()[0];

        // create the vars file
        createVars(vars,serverName);

        // execute commands
        generateCommand(false,linuxPath,vars);
    }

    /**
     * Generates the copying command and executes it.
     * @param testRun
     * @param linuxPath
     * @param vars
     */
    private static void generateCommand(boolean testRun,String linuxPath,String vars) {
        //get commands
        String[] commands = new String[24];
        char quotation = (char)34; // quotation mark "

        //commands[8] = "cp "+quotation+quotation+linuxPath+vars+quotation+" "+quotation+"/usr/share/taskd/pki/"+quotation+quotation;
        //commands[8] = "cp "+quotation+linuxPath+vars+quotation+" "+quotation+"/usr/share/taskd/pki/"+quotation;
        //commands[8] = "cp "+quotation+linuxPath+vars+quotation+" "+quotation+"/usr/share/taskd/pki"+quotation;
        //commands[8] = quotation+"cp "+quotation+linuxPath+vars+quotation+" "+quotation+"/usr/share/taskd/pki"+quotation+quotation;
        //commands[8] = "cp "+quotation+quotation+linuxPath+vars+quotation+" "+quotation+"/usr/share/taskd/pki"+quotation+quotation;
        //commands[8] = "cp "+quotation+quotation+linuxPath+vars+" "+"/usr/share/taskd/pki"+quotation+quotation;
        //commands[8] = "cp "+quotation+quotation+linuxPath+vars+" "+"~"+quotation+quotation;
        //commands[8] = "cp "+quotation+linuxPath+vars+quotation+" "+"~";
        //commands[8] = "cp "+quotation+linuxPath+vars+quotation+" "+quotation+"/usr/share/taskd/pki/vars"+quotation;
        //commands[8] = "cp "+quotation+linuxPath+vars+quotation+" "+"/usr/share/taskd/pki/vars";
        commands[8] = "cp "+quotation+linuxPath+vars+quotation+" "+"/usr/share/taskd/pki/";

        runCommands(commands[8], false);
        System.out.println("Ran:"+commands[8]);

    }

    /**
     * This creates the Vars file required in command 8
     * @param serverName
     */
    private static void createVars(String fileName, String serverName) {
        char quotation = (char)34; // quotation mark "

        deleteFile(fileName);
        PrintWriter writer;
        try {
            writer = new PrintWriter("vars", "UTF-8");
            writer.println("BITS=4096");
            writer.println("EXPIRATION_DAYS=365");
            writer.println("ORGANIZATION="+quotation+"Göteborg Bit Factory"+quotation);
            writer.println(serverName);
            writer.println("COUNTRY=SE");
            writer.println("STATE="+quotation+"Västra Götaland"+quotation);
            writer.println("LOCALITY="+quotation+"Göteborg"+quotation);
            writer.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * Delete a file that is located in the same folder as the src folder of this project
     * is located.
     * @param fileName
     */
    private static  void deleteFile(String fileName) {
        File file = new File(fileName);
        try {
            boolean result = Files.deleteIfExists(file.toPath());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } //surround it in try catch block
    }

    /**
     * This runs the command for the scenario where you are not prompted for yes.
     * Source: https://github.com/AlvinFDK/FDK/blob/18d61bcc2121b13ae1b02345930f6f2264feb813/src/main/java/blackflames/alvin/bar/io/TerminalUnix.java
     */
    public static ArrayList<ArrayList<String>> runCommands(String command,boolean ignoreOutput) {

        String s = null;
        String outputLines=null;
        ArrayList<String> goodExecutionOutput=new ArrayList<String>();
        ArrayList<String> errorExecutionOutput=new ArrayList<String>();
        ArrayList<ArrayList<String>> returnLists = new ArrayList<ArrayList<String>>();

        try {

            // run the Unix "task nice0" command
            Process p = Runtime.getRuntime().exec(command);

            BufferedReader brGood = new BufferedReader(new InputStreamReader(p.getInputStream()));

            BufferedReader brError = new BufferedReader(new 
                    InputStreamReader(p.getErrorStream()));

            // get output
            if (!ignoreOutput) {
                while ((s = brGood.readLine()) != null) {
                    //System.out.println("Adding:"+s);
                    goodExecutionOutput.add(s);
                    System.out.println("Good execution: "+s);
                }

                // get the error message
                while ((s = brError.readLine()) != null) {
                    errorExecutionOutput.add(s);
                    System.out.println("Bad execution: "+s);
                }   
            }

            //System.exit(0);
        }
        catch (IOException e) {
            System.out.println("Error: ");
            e.printStackTrace();
            System.exit(-1);
        }

        //Merge outputLists and return
        returnLists.add(goodExecutionOutput);
        returnLists.add(errorExecutionOutput);
        return returnLists;
    }

    /**
     * This gets the current location of the compiled.jar file
     * @return
     */
    public static String[] getJarLocation() {
        String[] paths= new String[2];

        // get path of location of compiled .jar file of this project in windows format 
        File f = new File(System.getProperty("java.class.path"));
        File dir = f.getAbsoluteFile().getParentFile();
        paths[0] = dir.toString()+"/";  
        return paths;
    }
}

Question:

How can I copy a file in WSL Ubuntu 16.04 from within the .jar file?

Additional attempts:

I am aware that there is a difference in how terminal handles commands when a user is typing, and when you execute commands from for example a .jar file like this. Specifically Some syntax won't work as you are required to pipe for example the output of a command to the input, or vice versa, also the cd command functions differently. However, I have not yet been able to determine why the cp command would be affected by these 2 phenomena, as there is no output relevant for this command to function, and the only relative environment is the Unix environment.

Update based on comments:* Indeed the explicit quotation marks rendered the path invalid. Furthermore, in the end path, the output file should not be contained, so the vars at the end of command[8]=... is removed. However since the source path might contain spaces within the first 8 characters, I am currently researching how I can include the spaces in the command without adding the additional explicit quotation marks.

a.t.
  • 2,002
  • 3
  • 26
  • 66
  • 1
    Note that it is complaining about `"/usr/share/taskd/pki/"` **with double quotes** - I'm sure that one doesn't exist. Try to run it on the command line as `'"/usr/share/taskd/pki/"'` and you will get the same problem. Instead of creating command lines, use the version of `Runtime.exec` that accepts an array. Or use a `ProcessBuilder`. – RealSkeptic Apr 02 '19 at 13:13
  • Thank you very much for your response, that was indeed an invalid path due to the quotation marks, furthermore, the output file `vars` should not be included in the destination path. However the difficulty I am still facing concerns including (potential) spaces (with)in (the first 8 characters of) my source path, without adding/including the quotation marks. I am looking into solutions. If that does not work out, I will look into the `ProcessBuilder` and include my attempt with the `Runtime.getRuntime().exec(command)` approach. – a.t. Apr 02 '19 at 13:29
  • In the version with an array, you pass the command name in the first element, the first argument in the next element etc. Each argument may contain spaces or special characters or whatever. Since it doesn't go through a shell, it matters not. – RealSkeptic Apr 02 '19 at 13:40

1 Answers1

1

Solution:

A solution was found by separating the command and arguments each in a separate string and passing the array of separated strings into the Runtime.getRuntime().exec(command) as suggested in the comments.

Notes:

  1. Whether the arguments contain any spaces or not, is irrelevant.
  2. Whether or not the arguments contain any spaces or not, the arguments should not be surrounded by quotation marks.

Implementation:

An implementation was found using two extra classes. For this application I just added the two classes in the project src folder (the same folder as the main class is located). The extra classes runCommandsWithArgs.java and SyncPipe.java are listed below the main class (named mweCopy0.java).

mweCopy0.java:

package mweCopy0;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.util.ArrayList;

public class Main {

    /**
     * First the main gets the location for the file that it wants to copy somewhere.
     * Next it creates the file it wants to copy somewhere.
     * Then it generates the command to copy the file somewhere.
     * In method generateCommand, the command is passed through
     * to the module that actually executes the command in WSL Ubuntu 16.04.
     * 
     * You can quickly compile this script in Eclipse by:
     * Selecting Main.java in the Package Explorer,
     * press: alt+f>o>
     * Then select Java>Runnable JAR file>next
     * In launch configuration, select the Main.java
     * Select Package required libraries into generated JAR
     * press finish.
     * @param args
     */
    public static void main(String[] args) {
        String vars = "vars";
        char quotation = (char)34; // quotation mark "
        String serverName = "exampleName";

        //get the path of this file
        String linuxPath = getJarLocation()[0];

        // create the vars file
        createVars(vars,serverName);

        // execute commands
        generateCommand(false,linuxPath,vars);
    }

    /**
     * Generates the copying command and calls the class that executes it.
     * @param testRun
     * @param linuxPath
     * @param vars
     */
    private static void generateCommand(boolean testRun,String linuxPath,String vars) {
        String[] commands = new String[24];
        char quotation = (char)34; // quotation mark "

        // attempt 1: Original command 
        // cp /mnt/e/18-09-19 Document structure/personal/Programming/PublicCodeLibrary/Java/mweCopy0/vars /usr/share/taskd/pki/

        // separate the command from its arguments with a space between the arguments:
        // note it is irrelevant whether the arguments themselves contain spaces.
        String[] commandAndArgs = new String[3];
        commandAndArgs[0] = "cp";
        commandAndArgs[1] = "/mnt/e/18-09-19 Document structure/personal/Programming/PublicCodeLibrary/Java/mweCopy0/vars"; 
        commandAndArgs[2] = "/usr/share/taskd/pki/";

        RunCommandsWithArgs.runCommands(commandAndArgs);
        System.out.println("Ran:"+commandAndArgs);
    }

    /**
     * This creates the Vars file required in command 8
     * @param serverName
     */
    private static void createVars(String fileName, String serverName) {
        char quotation = (char)34; // quotation mark "

        deleteFile(fileName);
        PrintWriter writer;
        try {
            writer = new PrintWriter("vars", "UTF-8");
            writer.println("BITS=4096");
            writer.println("EXPIRATION_DAYS=365");
            writer.println("ORGANIZATION="+quotation+"Göteborg Bit Factory"+quotation);
            writer.println(serverName);
            writer.println("COUNTRY=SE");
            writer.println("STATE="+quotation+"Västra Götaland"+quotation);
            writer.println("LOCALITY="+quotation+"Göteborg Verification"+quotation);
            writer.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * Delete a file that is located in the same folder as the src folder of this project
     * is located.
     * @param fileName
     */
    private static  void deleteFile(String fileName) {
        File file = new File(fileName);
        try {
            boolean result = Files.deleteIfExists(file.toPath());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } //surround it in try catch block
    }

    /**
     * This gets the current location of the compiled.jar file
     * @return
     */
    public static String[] getJarLocation() {
        String[] paths= new String[2];

        // get path of location of compiled .jar file of this project in windows format 
        File f = new File(System.getProperty("java.class.path"));
        File dir = f.getAbsoluteFile().getParentFile();
        paths[0] = dir.toString()+"/";  
        return paths;
    }
}

RunCommandsWithArgs.java:

package mweCopy0;


import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;

public class RunCommandsWithArgs {
    /**
     * This method actually executes the command in WSL Ubuntu 16.04 if you run the 
     * compiled .JAR file.
     * You can automatically answers yes to any input the command requires with the
     * stdin.println("yes"); line
     * @param command
     * @return
     */
    public static void runCommands(String[] commandPart) {
        Process p;

        try {
            p = Runtime.getRuntime().exec(commandPart);
            new Thread(new SyncPipe(p.getErrorStream(), System.err)).start();
            new Thread(new SyncPipe(p.getInputStream(), System.out)).start();
            PrintWriter stdin = new PrintWriter(p.getOutputStream());

            //This is not necessary but can be used to answer yes to being prompted
            //stdin.println("yes");

            // write any other commands you want here
            stdin.close();
            int returnCode = p.waitFor();
            System.out.println("Return code = " + returnCode);

        } catch (IOException | InterruptedException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }       
    }
}

SyncPipe.java:

package mweCopy0;

import java.io.InputStream;
import java.io.OutputStream;

class SyncPipe implements Runnable
{
public SyncPipe(InputStream istrm, OutputStream ostrm) {
      istrm_ = istrm;
      ostrm_ = ostrm;
  }
  public void run() {
      try
      {
          final byte[] buffer = new byte[1024];
          for (int length = 0; (length = istrm_.read(buffer)) != -1; )
          {
              ostrm_.write(buffer, 0, length);
          }
      }
      catch (Exception e)
      {
          e.printStackTrace();
      }
  }
  private final OutputStream ostrm_;
  private final InputStream istrm_;
}
a.t.
  • 2,002
  • 3
  • 26
  • 66