windowspowershellfor-loopcmdmklink

How to create symbolic links for multiple files in multiple folders using command prompt or power shell?


I have two main folders which have a lot of sub-folders in different drives. Have to create symbolic link for all files in the second folder into the first one.

C:\folderC>tree /f
C:.
├───folder1
│       file1.txt
│       file3.txt
│
└───folder2
        file1.txt
        file3.txt

D:\folderD>tree /f
D:.
├───folder1
│       file2.txt
│
└───folder2
        file2.txt

Result using 2 commands:

C:\>mklink C:\folderC\folder1\file2.txt D:\folderD\folder1\file2.txt
symbolic link created for C:\folderC\folder1\file2.txt <<===>> D:\folderD\folder1\file2.txt

C:\>mklink C:\folderC\folder2\file2.txt D:\folderD\folder2\file2.txt
symbolic link created for C:\folderC\folder2\file2.txt <<===>> D:\folderD\folder2\file2.txt

C:.
├───folder1
│       file1.txt
│       file2.txt
│       file3.txt
│
└───folder2
        file1.txt
        file2.txt
        file3.txt

How to make it for all files with a few commands instead of writing the code manually for each file?

PS: Firstly I wanted to use hard links but it seems it is not possible.

C:\>mklink /h C:\folderC\folder2\file2.txt D:\folderD\folder2\file2.txt
The system cannot move the file to a different disk drive.

Solution

  • You can try something link this:

    function createSymbolicLinks ($source, $destination, [switch]$recurse) {
        Get-ChildItem $source -Recurse:$recurse | ? { !$_.PSISContainer } | % {
            $destpath = $_.Fullname -replace [regex]::Escape($source), $destination
            if(!(Test-Path (Split-Path $destpath))) {
                #Create missing subfolders
                New-Item (Split-Path $destpath) -ItemType Directory -Force | Out-Null
            }
            cmd /c mklink $destpath $($_.FullName) | Out-Null
        }
    }
    
    #Create symbolic links in c:\folderC for all files in d:\folderD(with recursive search)
    createSymbolicLinks -source d:\folderD -destination c:\folderC -recurse
    

    I believe this will fail if the same filename already exists in c:\folderc. So if you need to replace a file in c:\folderc with a symbolic link from d:\folderd, you need to extend it to remove the existing file.

    UPDATE: This will only go down one level with the recurseoption. It's not the prettiest solution, but it should work.

    function createSymbolicLinks ($source, $destination, [switch]$recurse) {
        Get-ChildItem $source | % { 
            if($_.PSIsContainer -and $recurse) { 
                Get-ChildItem $_.FullName
            } else { 
                $_
            }
        } | ? { !$_.PSIsContainer } | % { 
            $destpath = $_.Fullname -replace [regex]::Escape($source), $destination
            if(!(Test-Path (Split-Path $destpath))) {
                #Create missing subfolders
                New-Item (Split-Path $destpath) -ItemType Directory -Force | Out-Null
            }
            cmd /c mklink $destpath $($_.FullName) | Out-Null
        }
    }
    
    #Create symbolic links in c:\folderC for all files in d:\folderD(with recursive search)
    createSymbolicLinks -source d:\folderD -destination c:\folderC -recurse