I have a directory a\ containing files and sub-directories that I want to copy into path b\ where instead of copying the files I want to perform a call to MKLINK <link> <target> on each file into the new path rather than performing an actual copy.

So if I have a directory:

Z:\a\file1.txt
Z:\a\file2.txt
Z:\a\some_path\file3.txt
Z:\a\some_path\file4.txt

And I copy links from path a\ to b\ the result will look like:

Z:\b\file1.txt           <<===>> z:\a\file1.txt
Z:\b\file2.txt           <<===>> z:\a\file2.txt
Z:\b\some_path\file3.txt <<===>> z:\a\some_path\file3.txt
Z:\b\some_path\file4.txt <<===>> z:\a\some_path\file4.txt

The directory hierarchy is to be preserved as non-link folders in the event that the target directory does not have a matching folder structure. Note that only the files are links.

A successful test will succeed where Z:\b is an empty directory, Z:\b contains a folder Z:\b\some_path, and either previous tests but Z:\b my already contain files of the same name; conflicts are ignored and no link is created for them.

How can I do this using a batch file with no additional dependencies beyond what is available in a standard Windows 10 installation?

Emulating a Recursive File and Folder Structure with MKLink

Note: This works with an already existing root level target folder.

You can use a for /d loop and iterate the first level root sub-folders within the source directory, and then use the mklink command with the /D parameter to create directory symbolic links to link those sub-folders in the target path's root directory creating an emulated directory structure beneath each as you desire with files beneath each being referenced recursively too—the root level target folder can already exist with this method.

You can then use a for loop iterating the first level files within the source directory and then use the mklink command accordingly to create those as direct symbolic links within the root of the target directory, and the root level target folder can already exist with this method too.


Batch Script

@ECHO ON

SET SrcRoot=Z:\a
SET TargetRoot=Z:\b

FOR /D %%A IN ("%SrcRoot%\*") DO (
    MKLINK /D "%TargetRoot%\%%~NA" "%%~A"
    )

FOR %%A IN ("%SrcRoot%\*") DO (
    MKLINK "%TargetRoot%\%%~NXA" "%%~A"
    )

PAUSE
EXIT

Results

enter image description here

enter image description here

enter image description here


Further Resources

  • FOR /D
  • FOR

  • Batch Substitutions (FOR /?)

    In addition, substitution of FOR variable references has been enhanced. You can now use the following optional syntax:

    %~nI        - expands %I to a file name only
    %~xI        - expands %I to a file extension only
    
  • MKLink

  • mklink /?

    Creates a symbolic link.
    
    MKLINK [[/D] | [/H] | [/J]] Link Target
    
            /D      Creates a directory symbolic link.  Default is a file
                    symbolic link.
            /H      Creates a hard link instead of a symbolic link.
            /J      Creates a Directory Junction.
            Link    Specifies the new symbolic link name.
            Target  Specifies the path (relative or absolute) that the new link
                    refers to.
    
  • This is certainly closer but only files are to be links. This requirement was in the original question but I've modified it to added additional clarification and emphasis. Using the code from your answer with my question's test case, if Z:\b\some_path as a folder already exists then MKLINK /D Z:\b\some_path Z:\a\some_path will fail and subsequently Z:\b\some_path\file3.txt and Z:\b\some_path\file4.txt won't exist. And in the extent that it does succeed, Z:\b\some_path is a link to Z:\a\some_path and therefore Z:\b\some_path\file3.txt and Z:\b\some_path\file4.txt are not links. – Zhro Jun 27 at 20:33

Emulating a Recursive File and Folder Structure with MKLink

Note: This will only work if the target path folder does not already exist.

You can simply use the mklink command with the /J parameter to create a directory junction pointing the linked path to the location of the source's root directory linking the two and creating an emulated directory structure as you desire with beneath files being referenced recursively too.


Batch Script

@ECHO ON

SET SrcRoot=Z:\a
SET LinkRoot=Z:\b
MKLINK /J "%LinkRoot%" "%SrcRoot%"

PAUSE
EXIT

Results

enter image description here

enter image description here

enter image description here


Further Resources

  • MKLink
  • mklink /?

    Creates a symbolic link.
    
    MKLINK [[/D] | [/H] | [/J]] Link Target
    
            /D      Creates a directory symbolic link.  Default is a file
                    symbolic link.
            /H      Creates a hard link instead of a symbolic link.
            /J      Creates a Directory Junction.
            Link    Specifies the new symbolic link name.
            Target  Specifies the path (relative or absolute) that the new link
                    refers to.
    
  • This does not answer the question. To be more specific, directory b already exists and contains other files already. The idea is to merge in a bunch of symbolic links instead of copying a folder over and duplicating disk space. – Zhro Jun 22 at 3:12
  • I'm not able to test this tonight but I will look into your answer more closely tomorrow. – Zhro Jun 22 at 5:38

Create symbolic links with MKLink from one folder structure to another recursively but for files only that do not already exist in the target directory

Note: The below solutions will skip making symbolic links to files at target from the source if the same file name already exists at the target. It will also create the target path sub-folders that don't exist already if a symbolic link needs to be created.


1. Batch Script (pure batch)

Below is a pure batch script solution using a for /r loop with setlocal enabledelayedexpansion and variable substrings to get the iterated full file path and directory names from the source location. This sets additional variables from those values parsing the source root path from the string and then concatenates the target root path back to the string to be used to create the symbolic links accordingly as per your requirements and the above topmost note.

Important: It is important to understand the number of characters in the SrcRoot= path variable value here so you can set the variable substring number to skip that same number of characters in that string to parse it out so it can be replaced by the TargetRoot= variable value instead.

For Example: So Z:\a is exactly 4 characters where Z, :, \, and a each count as 1 character. Summed up this is 4 total; thus, this is where the ~4 comes into play in the !oDir:~4! and !oFile:~4! portions of the logic within the loop. So each literal character in the SrcRoot value will each count as 1 so sum those up and replace the number after the tilde (~<#>) with it.

@ECHO ON

SET SrcRoot=Z:\a
SET TargetRoot=Z:\b

SETLOCAL ENABLEDELAYEDEXPANSION
FOR /R "%SrcRoot%" %%A IN ("*") DO (
  SET oDir=%%~DPA
  SET oFile=%%~A
  IF NOT EXIST "%TargetRoot%!oDir:~4!" MD "%TargetRoot%!oDir:~4!"
    IF NOT EXIST "%TargetRoot%!oFile:~4!" MKLINK "%TargetRoot%!oFile:~4!" "%%~A"
  )

PAUSE
EXIT

2. Batch Script (with PowerShell)

Below is a batch script solution using a for /r loop nested into a for /f loop to get a variable name from a dynamically created and executed PowerShell script with variables passed in accordingly to then process with the replace method to replace the source path with the target path to then created the symbolic links as per your requirements and the above topmost note.

@ECHO ON

SET SrcRoot=Z:\a
SET TargetRoot=Z:\b

CALL :PowerShell

FOR /R "%SrcRoot%" %%A IN ("*") DO (
  CD /D "%PowerShellDir%"
  FOR /F "TOKENS=*" %%B IN ('Powershell -ExecutionPolicy Bypass -Command "& '%PSScript%' '"%SrcRoot%"' '"%TargetRoot%"' '"%%~A"'"') DO (
      IF NOT EXIST "%%~DPB" MD "%%~DPB"
      IF NOT EXIST "%%~B" MKLINK "%%~B" "%%~A"
      )
  )

PAUSE
EXIT

:PowerShell
SET PowerShellDir=C:\Windows\System32\WindowsPowerShell\v1.0
SET PSScript=%temp%\~tmpStrFldrRplc.ps1
IF EXIST "%PSScript%" DEL /Q /F "%PSScript%"
ECHO $Source = $args[0]>"%PSScript%"
ECHO $Dest   = $args[1]>>"%PSScript%"
ECHO $FPath  = $args[2]>>"%PSScript%"
ECHO $var    = $FPath.Replace($Source,$Dest)>>"%PSScript%"
ECHO Write-Output $Var>>"%PSScript%"
GOTO :EOF

Further Resources

  • FOR /R
  • Setlocal EnableDelayedExpansion
  • Variables: extract part of a variable (substring)
  • FOR /F
  • Replace
  • Script Parameters / Arguments

  • Batch Substitutions (FOR /?)

    In addition, substitution of FOR variable references has been enhanced. You can now use the following optional syntax:

    %~dI        - expands %I to a drive letter only
    %~pI        - expands %I to a path only
    
  • MKLink

  • mklink /?

    Creates a symbolic link.
    
    MKLINK [[/D] | [/H] | [/J]] Link Target
    
            /D      Creates a directory symbolic link.  Default is a file
                    symbolic link.
            /H      Creates a hard link instead of a symbolic link.
            /J      Creates a Directory Junction.
            Link    Specifies the new symbolic link name.
            Target  Specifies the path (relative or absolute) that the new link
                    refers to.
    
  • The the question is asking how to solve this using batch scripting. Introducing PowerShell, while technically available, is outside the scope of what I had intended by mentioning Windows 10. I meant that you could call other programs which might be available, such as MKLINK. – Zhro Jun 28 at 3:44
  • @Zhro Your comment is no longer relevant since the PowerShell solution is secondary & #2 optional method for those that may need/want it. I've tested & confirmed with the final solution above in the 1. Batch Script (pure batch) section of my answer—the 4th solution I've provided you for this task to help you out. I'd appreciate some feedback regarding this final solution & your thoughts concerning it one way or the other. This does exactly what you ask & within the scope after your editing & clarifying otherwise each time I gave you an answer that would suffice per each revision. – Pimp Juice IT Aug 10 at 21:27
  • 1
    I haven't had a chance to look at this yet as I've been very busy. Sorry for the delay. Please continue to be patient. – Zhro Aug 11 at 1:53

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.