Building a ZIP file from a folder containing shortcuts on Windows
While shortcuts can be handy, you don't generally want to include them in a compressed ZIP archive and would rather have the actual files that they point to. I couldn't find an obvious way to do this built in to Windows, so I ended up creating a PowerShell script.
Given a source directory such as the following:
.
├── Child1
│ └── Document1.docx.lnk
├── Child2
│ └── README.txt
└── Child3
└── Document3.pdf.lnk
I needed to recurse all child folders and maintain the structure. I came up with the following PowerShell script to do just that:
Param (
[Parameter(Mandatory = $True)]
[string]
$Source,
[Parameter(Mandatory = $True)]
[string]
$Target,
[array]
$Exclude = @()
)
$ErrorActionPreference = 'Stop'
Function Create-Temporary-Directory {
$parent = [System.IO.Path]::GetTempPath()
[string] $name = [System.Guid]::NewGuid()
$name = "${name}"
$created = New-Item -Path $parent -Name $name -ItemType "directory" -Force
Return $created.FullName
}
$tempDirectory = Create-Temporary-Directory
$timeTaken = Measure-Command {
try {
Copy-Item -Path "${Source}/*" -Destination "${tempDirectory}" -Recurse -Exclude $Exclude
$sh = New-Object -ComObject WScript.Shell
Get-ChildItem -Path "${tempDirectory}" -Filter "*.lnk" -Recurse | ForEach-Object {
$shortcutPath = $_.FullName
$shortcutTargetPath = $sh.CreateShortcut("${shortcutPath}").TargetPath
$targetFolder = Split-Path -Path "${shortcutPath}" -Parent
$targetFilename = Split-Path -Path "${shortcutPath}" -LeafBase
$targetPath = Join-Path -Path "${targetFolder}" -ChildPath "${targetFilename}"
Remove-Item -Path "${shortcutPath}"
If (Test-Path -Path "${shortcutTargetPath}" -PathType Container) {
Copy-Item -Path "${shortcutTargetPath}" -Destination "${targetPath}" -Recurse
} else {
Copy-Item -Path "${shortcutTargetPath}" -Destination "${targetPath}"
}
}
Compress-Archive -Path "${tempDirectory}\*" -DestinationPath "${Target}" -Force
}
finally {
Remove-Item -Path "${tempDirectory}" -Recurse -Force
}
}
Write-Host "ZIP file created: ${Target}`nTime taken: ${timeTaken}"
The script has parameters for the source directory, the path to the target zip file, and an optional list of file names to exclude from the zip file.