在 WSL Ubuntu 16.04 中使用 `.jar` 复制文件

Copy file using `.jar` in WSL Ubuntu 16.04

在尝试复制我在 .jar 脚本中创建的文件时,我 运行 在 Windows 子系统中 Linux (WSL) Ubuntu 16.04 ,我收到以下错误:

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

或者,如果在源路径中不带引号执行命令:

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

但是,为了验证它是否有效,我还将命令打印到终端,当我复制粘贴 it/manually 执行它时,它确实有效,因为它更改了 /usr/share/taskd/pki/vars 文件内容。

所以我构建了一个最小工作示例 (MWE) 并且我 运行 script/MWE 使用命令: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;
    }
}

问题:

如何从 .jar 文件中复制 WSL Ubuntu 16.04 中的文件?

额外尝试:

我知道终端在用户输入时处理命令的方式与您从 .jar 文件中执行命令时的方式有所不同。具体来说,某些语法将不起作用,因为您需要将命令的输出通过管道传输到输入,反之亦然,cd 命令的功能也不同。但是,我还不能确定为什么 cp 命令会受到这两种现象的影响,因为没有与此命令运行相关的输出,唯一相关的环境是 Unix 环境。

根据评论更新:* 实际上,显式引号使路径无效。此外,在结束路径中,不应包含输出文件,因此删除了 command[8]=... 末尾的 vars。但是,由于源路径的前 8 个字符中可能包含空格,我目前正在研究如何在不添加额外的显式引号的情况下在命令中包含空格。

解法:

通过将命令和参数分别放在单独的字符串中并将分隔字符串的数组传递到 Runtime.getRuntime().exec(command) 中找到了解决方案,如评论中所建议的那样。

备注:

  1. 参数是否包含空格,ir相关。
  2. 无论参数是否包含空格,参数都应该用引号括起来。

执行:

使用两个额外的 classes 找到一个实现。对于这个应用程序,我只是在项目 src 文件夹中添加了两个 classes(与主 class 所在的文件夹相同)。额外的 classes runCommandsWithArgs.javaSyncPipe.java 列在主要 class(名为 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_;
}