【问题标题】:Creating a symlink on Windows during JVM runtime在 JVM 运行时在 Windows 上创建符号链接
【发布时间】:2015-10-11 15:24:08
【问题描述】:

有些奇怪的问题,但我在 Windows 7 上使用 mklink 创建符号链接时遇到了问题。由于通过创建符号链接使用 cmd.exe 时存在 260 个字符限制,我正在做一些奇怪的事情在我的 Java 源代码中使用Process。由于我不能完全解释它,这里是代码:

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Arrays;

public class WindowsSymlinkUtility {

    private List<String> command, currentSymlinks;

    public WindowsSymlinkUtility() {
        this.command = this.currentSymlinks = new ArrayList<String>();
        this.command.add("cmd.exe");
        this.command.add("/C");
    }

    /**
     * Automatically creates a directory junction
     * @param String link - the path and name of the symlink
     * @param String target - the directory to point the symlink to
     * @return boolean
     * @see http://ss64.com/nt/mklink.html
     */
    public boolean createSymlink(String link, String target) {
        return createSymlink("\\J", link, target);
    }

    /**
     *
     * @param String flag - the flag for mklink
     * @param String link - the path and name of the symlink
     * @param String target - the directory to point the symlink to
     * @return boolean
     * @see http://ss64.com/nt/mklink.html
     */
    public boolean createSymlink(String flag, String link, String target) {
        this.command.clear();
        this.command.add("mklink");
        this.command.add(flag);
        this.command.add(link);
        this.command.add(target);
        this.currentSymlinks.add(link);

        return this.runner() == 0;
    }

    public boolean removeSymlink(String link) {
        this.command.clear();
        this.command.add("RD");
        this.command.add(link);

        if(this.runner() != 0) {
            this.command.clear();
            this.command.add("DEL");
            this.command.add(link);
        } else {
            return true;
        }

        return this.runner() == 0;
    }

    public boolean removeAllSymlinks() {
        for(String link : this.currentSymlinks) {
            if(!this.removeSymlink(link)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Leave for debugging purposes
     * @return String
     */
    public String getCurrentCommand() {
        String cmd = "";
        for(String part : this.command) {
            cmd += part + " ";
        }

        return cmd;
    }

    private int runner() {
        Process process = null;
        String message = null;
        BufferedInputStream bis = null;
        int exitVal = -1;
        StringBuilder strBuff = new StringBuilder();

        try {
            if(this.command.size() < 1) throw new Exception("Length of Windows command cannot be zero");

            ProcessBuilder pb = new ProcessBuilder(this.command);
            Map<String, String> envVars = pb.environment();

            pb.directory();
            pb.redirectErrorStream(true);
            process = pb.start();
            bis = new BufferedInputStream(process.getInputStream());
            byte[] bArr = new byte[2048];
            while (bis.read(bArr) != -1) {
                strBuff.append(new String(bArr).trim());
                bArr = new byte[2048];
            }

            exitVal = process.waitFor();
            message = strBuff.toString();
        } catch(Exception e) {
            e.printStackTrace();
            System.err.println(e.getMessage());
            System.err.println(message);
        }

        return exitVal;
    }

    public static void main(String[] args) {
        WindowsSymlinkUtility foo = new WindowsSymlinkUtility();
        foo.createSymlink("%TEMP%\\foo", "C:\\Users\\djthomps\\Downloads");
    }

}

我得到的错误:

java.io.IOException: Cannot run program "mklink": CreateProcess error=2, The system cannot find the file specified
        at java.lang.ProcessBuilder.start(Unknown Source)
        at WindowsSymlinkUtility.runner(WindowsSymlinkUtility.java:113)
        at WindowsSymlinkUtility.createSymlink(WindowsSymlinkUtility.java:56)
        at WindowsSymlinkUtility.createSymlink(WindowsSymlinkUtility.java:37)
        at WindowsSymlinkUtility.main(WindowsSymlinkUtility.java:134)
Caused by: java.io.IOException: CreateProcess error=2, The system cannot find the file specified
        at java.lang.ProcessImpl.create(Native Method)
        at java.lang.ProcessImpl.<init>(Unknown Source)
        at java.lang.ProcessImpl.start(Unknown Source)
        ... 5 more
Cannot run program "mklink": CreateProcess error=2, The system cannot find the file specified
null

您可能有一些问题:

  1. 为什么要这样做?
    • 由于相关文件和文件夹深深嵌套在文件系统中,完整命令的长度超过 260 个字符。
  2. 符号链接有什么帮助?
    • 我已进行测试以确保符号链接允许我“绕过”260 个字符的限制。

这是我的问题:

  1. 是否有另一种方法可以在 Java 中创建符号链接,以便在命令超过 260 个字符限制时 Windows 会运行?
  2. 可以用SET代替mklink吗?
  3. 即使命令运行超过 260 个字符,是否可以为此使用 java.nio.file?

再一次,我知道这是一个奇怪的问题。如果有问题,请要求澄清。

【问题讨论】:

  • 你需要在mklink程序中使用full path或者设置一个环境变量...即。 c:\windows\system32\mklinkjava默认不添加windows路径。
  • @EddieB:什么环境变量?我认为 Java 只是使用 PATH,就像其他所有东西一样?
  • 问题是 createSymlink(link, target) 函数中的拼写错误 - 您指定 /J 作为标志的地方是反斜杠而不是正斜杠。
  • @HarryJohnston Java application 使用路径。如果你想运行一个系统进程this application 需要它的完整路径。此外,您对 OP 的错误标志是正确的。但这是另一个问题 :)
  • @EddieB:实际上再看一遍我认为根本问题是 mklink 是一个内部命令,即没有可执行文件。 OP 需要将cmd /c 放在命令行的开头。

标签: java windows windows-7 cmd


【解决方案1】:

我对您的程序进行了一些修改,只是为了提供一个工作示例...本质上问题是您没有连接变量并将它们作为一个参数传递给cmd。

一个实现说明 :: 不要使用del 删除符号链接,否则目标目录中的所有文件都将被删除。使用我为后代添加的rmdir。

/**
 * @author Edward Beckett :: <Edward@EdwardBeckett.com>
 * @since :: 7/21/2015
 */
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;

public class WindowsSymlinkUtility {

    public static final String D_LINK = "/D";
    public static final String H_LINK = "/H";
    public static final String J_LINK = "/J";
    public static final String REM_LINK = "rmdir";
    private String command = "";
    private String link = "";
    private String target = "";

    private List<String> commands = Arrays.asList( D_LINK, H_LINK, J_LINK, REM_LINK );

    public void createSymlink( String command, String link, String target ) {
        this.command = command;
        this.link = link;
        this.target = target;

        if( !commands.contains( command ) ) {
            System.out.println( command + " Is not a valid command \n " );
            return;
        }
        runner();
    }


    private void runner() {

        try {

            String[] values = { "CMD", "/C", "mklink", this.command, this.link, this.target };
            ProcessBuilder builder = new ProcessBuilder( values );
            builder.directory( new File( this.link ) );
            Process process = builder.start();
            InputStream is = process.getInputStream();
            InputStreamReader isr = new InputStreamReader( is );
            BufferedReader br = new BufferedReader( isr );
            String line;
            System.out.printf( "Output of running %s is:\n",
                Arrays.toString( values ) );
            while( ( line = br.readLine() ) != null ) {
                System.out.println( line );
                int exitValue = process.waitFor();
                System.out.println( "\n\nExit Value is " + exitValue );
            }
        } catch( InterruptedException | IOException e ) {
            e.printStackTrace();
        }
    }
        public static void main( String[] args ) {
        ( new WindowsSymlinkUtility() ).createSymlink( J_LINK, "C:\\Foo", "C:\\Temp" );
    }

}

输出

Output of running [CMD, /C, mklink, /J, C:\Foo, C:\Temp] is:
Junction created for C:\Foo <<===>> C:\Temp
Exit Value is 0

【讨论】:

  • 嗯,所以我尝试了几种不同的方法:1.(new WindowsSymlinkUtility()).createSymlink(J_LINK, "C:\\temp", "C:\\Users\\djthomps\\Downloads\\"); 2.(new WindowsSymlinkUtility()).createSymlink(J_LINK, "C:\\temp\\node", "C:\\Users\\djthomps\\Downloads\\"); 我没有得到任何输出说明符号链接实际上是创建的。在这个确切的用例中,我只想在node.exe 所在的目录之间创建一个符号链接。这是第一次使用的错误:java.io.IOException: Cannot run program "CMD" (in directory "C:\temp\node"): CreateProcess error=267, The directory name is invalid
  • 房间用完了。第二个在Output of running [CMD, /C, mklink, /J, C:\temp, C:\Users\djthomps\Downloads\] is 之后没有任何结果编辑:mklink /J C:\temp\node C:\Users\djthomps\Downloads 运行此命令有效
  • 您的问题是添加了尾随目录斜杠。
【解决方案2】:

Eddie B 的解决方案是正确的,但是当 Java 尝试运行该命令时,我不断收到错误消息。这是我的有效演绎:

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Arrays;

public class WindowsSymlinkUtility {

    public static final String D_LINK = "/D";
    public static final String H_LINK = "/H";
    public static final String J_LINK = "/J";
    public static final String REM_LINK = "rmdir";

    private String command, flag, link, target;
    private List<String> commands = Arrays.asList(D_LINK, H_LINK, J_LINK, REM_LINK), symlinks;

    public WindowsSymlinkUtility() {
        this.command = this.flag = this.link = this.target = "";
        this.symlinks = new ArrayList<>();
    }

    /**
     * Automatically creates a directory junction
     * @param String link - the path and name of the symlink
     * @param String target - the directory to point the symlink to
     * @return boolean
     * @see http://ss64.com/nt/mklink.html
     */
    public boolean createSymlink(String link, String target) {
        return createSymlink(J_LINK, link, target);
    }

    /**
     *
     * @param String flag - the flag for mklink
     * @param String link - the path and name of the symlink
     * @param String target - the directory to point the symlink to
     * @return boolean
     * @see http://ss64.com/nt/mklink.html
     */
    public boolean createSymlink(String flag, String link, String target) {
        if(!this.commands.contains(flag)) {
            System.err.printf("%s is not a valid command\n", flag);
            return false;
        }

        this.command = "mklink";
        this.flag = flag;
        this.link = link;
        this.target = target;

        if(this.runner() == 0) {
            this.symlinks.add(this.link);
            return true;
        }

        return false;
    }

    private int runner() {
        Process process = null;
        String message = null;
        BufferedInputStream bis = null;
        StringBuilder strBuff = new StringBuilder();
        int exitVal = -1;

        try {
            ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/C", this.command, this.flag, this.link, this.target);
            Map<String, String> envVars = pb.environment();

            pb.directory();
            pb.redirectErrorStream(true);
            process = pb.start();
            bis = new BufferedInputStream(process.getInputStream());
            byte[] bArr = new byte[2048];
            while (bis.read(bArr) != -1) {
                strBuff.append(new String(bArr).trim());
                bArr = new byte[2048];
            }

            exitVal = process.waitFor();
            message = strBuff.toString();
            System.out.println(message);
        } catch(Exception e) {
            e.printStackTrace();
            System.err.println(e.getMessage());
            System.err.println(message);
        }

        return exitVal;
    }

    public static void main(String[] args) {
        (new WindowsSymlinkUtility()).createSymlink(J_LINK, "%TEMP%\\node", "C:\\users\\djthomps\\Downloads");
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 2010-09-16
    • 1970-01-01
    相关资源
    最近更新 更多