How to parse a directory recursively and MKLINK each file into a target path with the same tree structure

5,337

Solution 1

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.
    

Solution 2

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.
    
Share:
5,337

Related videos on Youtube

Zhro
Author by

Zhro

Updated on September 18, 2022

Comments

  • Zhro
    Zhro over 1 year

    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?

  • Zhro
    Zhro almost 6 years
    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
    Zhro almost 6 years
    I'm not able to test this tonight but I will look into your answer more closely tomorrow.
  • Zhro
    Zhro almost 6 years
    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
    Zhro almost 6 years
    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
    Zhro almost 6 years
    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.
  • Vomit IT - Chunky Mess Style
    Vomit IT - Chunky Mess Style almost 5 years
    What's up @Zhro .... Let me know if you've taken a stab at it yet and what results you got.