Powershell script to copy old files and create shortcuts

444 Views Asked by At

I am working on creating a stubbing script using powershell. My intentions for this script is to copy any data that hasn't been written to in the past x time period to a new location, check that the file copied, create a shortcut to the "archived" file in it's original location, and eventually delete the old file (I haven't written this section yet). Below is what I have so far. The issue I am having now is that a shortcut is created however all of the shortcuts are saved to the C:\Temp directory and not in any subfolders; if that is where the original file was stored. I think my issue is with $link and I need to split the path's and join them but I am not sure. Any assistance is greatly appreciated!

# Variables
$Original = "C:\Temp"
$Archive = "\\data\users\Temp"

Get-ChildItem -Path $Original -Recurse |
Where-Object {
    $_.LastWriteTime -lt [datetime]::Now.AddMinutes(-1)
} | Copy-Item -Destination $Archive -Recurse -force

$sourceFiles = Get-ChildItem $Original -Recurse | Where-Object { $_.LastWriteTime -lt [datetime]::Now.AddMinutes(-1) } | Select Name, Fullname
$destFiles = Get-ChildItem $Archive -Recurse | Select Name, Fullname

$Comparison = Compare-Object $sourceFiles.name $destFiles.name 

If ($comparison.sideindicator -ne "<=") {
    get-childitem $Archive -Recurse | where { $_.PsIsContainer -eq $false } | ForEach-Object {
        $path = '"' + $_.FullName + '"'
        $link = $Original + '\' + $_.Basename + '.lnk'
        $wshell = New-Object -ComObject WScript.Shell
        $shortcut = $wshell.CreateShortcut($link)
        $shortcut.TargetPath = $path
        $shortcut.Save()
    }
}

If ($comparison.sideindicator -eq "<=") {
    $comparison.inputobject, $sourceFiles.fullname | Out-File  'C:\ScriptLogs\stubbing.csv' -Force
}
1

There are 1 best solutions below

2
On

This is the problematic code:

$link = $Original + '\' + $_.Basename + '.lnk'

Basename is only the filename portion without the path, so the .lnk files end up directly in the top-level directory.

You have to use the relative path like this:

$RelativePath = [IO.Path]::GetRelativePath( $Archive, $_.FullName ) 
$link = [IO.Path]::ChangeExtension( "$Original\$RelativePath", 'lnk' ) 

This requires .NET 5 for GetRelativePath. To support older .NET versions you can use:

Push-Location $Archive
try     { $RelativePath = Resolve-Path -Relative $_.FullName }
finally { Pop-Location }

Resolve-Path -Relative uses the current location as the base base. So we use Push-Location to temporarily change the current location. The try / finally is used to ensure restoring of the original location even in case Resolve-Path throws an exception (e. g. when you have set $ErrorActionPreference = 'Stop').


Although the code might work like this, I think it could be refactored like this:

  • A single Get-ChildItem call to store the files to be moved into an array (your current $sourceFiles = ... line)
  • A foreach loop over this array to move each file and create the shortcut.
  • I think the Compare-Object isn't even necessary, but maybe I'm missing something.

Currently you are iterating over the same directories multiple times, which isn't very efficient and a lot of duplicate code.