Search code examples
powershellsymlink

Creating a symbolic link using PowerShell


I am struggling with a bit of PowerShell, which I feel I should be able to do easily, and would appreciate some help.

I have a series of paths storing log and config files within C:\temp. Not only that, but I need to delete the temp file and then create a symbolic link to the logged on user's OneDrive every time they run the app (which is launched with a batch file).

So I need it to run through about 10 folders such as

rmdir /Q /S c:\temp\app\cgi-bin
rmdir /Q /S c:\temp\app\contrib
rmdir /Q /S c:\temp\app\logs 

etc.

...and then run the equivalent of:

mklink /D c:\temp\app\cgi-bin c:\users\<logged on user>\One Drive - Company Name\app\cgi-bin
mklink /D c:\temp\app\contrib c:\users\<logged on user>\One Drive - Company Name\app\contrib
mklink /D c:\temp\app\logs c:\users\<logged on user>\One Drive - Company Name\app\logs

What I have attempted in PS to do this is below:

$username = $env:username

(get-item C:\temp\app\cgi-bin).Delete()
New-item -ItemType SymbolicLink -path c:\temp\app\cgi-bin -target 'C:\users\$username\OneDrive - Company Name\app\cgi-bin\'

...and then was going to get it to run the same for each location:

$username = $env:username

(get-item C:\temp\app\contrib).Delete()
New-item -ItemType SymbolicLink -path c:\temp\app\contrib -target 'C:\users\$username\OneDrive - Company Name\app\contrib\'

etc.

...but have got completely lost. I feel there should be an easy way to loop through the original folder locations and then joining the parts together. I had another go with join-path, but it didn't go too well either:

New-item -ItemType SymbolicLink -path C:\temp\app\cgi-bin -target (join-path 'C:\users' '"OneDrive - Company Name\app\cgi-bin\"')

Solution

  • Just, first, as you're new here -- so Welcome ! --, it doesn't really matter, but keep in mind to post the errors you are getting, because I had to figure them out here 😅.

    Right, just a few things first :

    • to loop through an array, you can use the foreach(){} method.
    • as @Compo stated, you can use $env:onedrive to get the current user's OneDrive directory ($env is the environment variable, it's a preset system-wide variable basically containing user-useful settings)
    • you cannot create a symbolic link pointing to a non-existing directory, so here, if the folders in OneDrive don't exist, it won't work; so I'm adding a line here to check if the folder exists, and create it if not, just make sure app exists !
    $onedrive = "$env:onedrive - Company Name"
    foreach ($folder in (Get-ChildItem -Path "C:\temp\app").Name){
        Remove-Item -Path $folder
        $onedrivePath = "$onedrive\app\$folder"
        if (-not(Test-Path $onedrivePath)){
            New-Item -ItemType Directory -Path $onedrivePath
        }
        New-item -ItemType SymbolicLink -Path "c:\temp\app\$folder" -target $onedrivePath
    }
    

    Feel free to comment if you want more explanations, and have a wonderful PowerShell journey (PowerShell is one of those incredible but underestimated languages...)

    Silloky

    EDIT :

    After modifications to the script as you requested, here it is in all its beauty !

    $onedrive = "$env:onedrive"
    $pathArray = @(
        "contrib", 
        "htdocs", 
        "install", 
        "licenses", 
        "locale", 
        "apache\logs", 
        "mysql\data", 
        "mysql\backup", 
        "perl", 
        "php", 
        "phpMyAdmin", 
        "sendmail", 
        "tmp", 
        "tomcat", 
        "webdav"
    )
    
    foreach ($path in $pathArray) {
        $localpath = "c:\app\$path"
        if (Test-Path $localpath) {
            Remove-Item -path $localpath -Recurse -force -Confirm:$false
        }
        # Write-Output "Setting OneDrive Path"
        $onedrivePath = "$onedrive\app\$path"
        if (-not(Test-Path $onedrivePath)) {
            New-Item -ItemType Directory -Path $onedrivePath
        }
        # Write-Output "Creating Junction"
        New-item -ItemType Junction -Path "$localpath" -target $onedrivePath
    }
    

    I replaced Read-Host by Write-Output as it is more adequate : Read-Host prompts for user input (like input() in Python) when Write-Output prints to the console (like print() in Python), which is what you want assuming from the "prompt" texts.

    I've also organised the array in much more semantic way, making it easier to edit.

    Silloky