compress-archive and preserve relative paths

前端 未结 4 2053
情书的邮戳
情书的邮戳 2020-12-06 06:38

I\'m having a challenging time getting compress-archive to do what I want...

I have a root project folder and I want to zip up some of the files in sub

相关标签:
4条回答
  • 2020-12-06 07:06

    It is a bit old thread, but I think this will help folks to create zip files through PowerShell 5.1, which is standard with Windows 10 installations these days. Script allows you to keep original subdirectory structure as well as to exclude some unnecessary subtrees / files. This is what I use to archive source code of my Visual Studio solutions:

    Write-Output "Zipping Visual Studio solution..."
    
    # top level from where to start and location of the zip file
    $path = "C:\TheSolution"
    # top path that we want to keep in the source code zip file
    $subdir = "source\TheSolution"
    # location of the zip file
    $ZipFile = "${path}\TheSolution.zip"
    
    # change current directory
    Set-Location "$path"
    
    # collecting list of files that we want to archive excluding those that we don't want to preserve
    $Files  = @(Get-ChildItem "${subdir}" -Recurse -File | Where-Object {$_.PSParentPath -inotmatch "x64|packages|.vs|Win32"})
    $Files += @(Get-ChildItem "${subdir}\packages" -Recurse -File)
    $Files += @(Get-ChildItem "${subdir}\.git" -Recurse -File)
    $FullFilenames = $files | ForEach-Object -Process {Write-Output -InputObject $_.FullName}
    
    # remove old zip file
    if (Test-Path $ZipFile) { Remove-Item $ZipFile -ErrorAction Stop }
    
    #create zip file
    Add-Type -AssemblyName System.IO.Compression
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    $zip = [System.IO.Compression.ZipFile]::Open(($ZipFile), [System.IO.Compression.ZipArchiveMode]::Create)
    
    # write entries with relative paths as names
    foreach ($fname in $FullFilenames) {
        $rname = $(Resolve-Path -Path $fname -Relative) -replace '\.\\',''
        Write-Output $rname
        $zentry = $zip.CreateEntry($rname)
        $zentryWriter = New-Object -TypeName System.IO.BinaryWriter $zentry.Open()
        $zentryWriter.Write([System.IO.File]::ReadAllBytes($fname))
        $zentryWriter.Flush()
        $zentryWriter.Close()
    }
    
    # release zip file
    $zip.Dispose()
    
    0 讨论(0)
  • 2020-12-06 07:10

    It appears that Compress-Archive (as of Windows PowerShell v5.1) doesn't support what you want:

    Targeting a folder recursively adds that folder's subtree to the archive, but only by the target folder's name (which becomes a child folder inside the archive), not its path.

    Specifically,

    Compress-Archive -Path scripts\module2 -DestinationPath tmp.zip
    

    will (recursively) store the contents of scripts\module2 in tmp.zip, but not with archive-internal path .\scripts\module2, just with .\module2 - the target folder's name (the last input path component).

    The implication is that you'd have to pass folder scripts instead to get the desired archive-internal path, but that would invariably include the entire subtree of scripts, given that Compress-Archive offers no inclusion/exclusion mechanism.


    One - cumbersome - option is to recreate the desired hierarchy in, say, the $env:TEMP folder, copy the target folder there, run Compress-Archive against the root of the recreated hierarchy, and then clean up:

    New-Item -Force -ItemType Directory $env:TEMP/scripts
    Copy-Item -Recurse -Force scripts/module2 $env:TEMP/scripts
    Compress-Archive -LiteralPath $env:TEMP/scripts -DestinationPath tmp.zip
    Remove-Item $env:TEMP/Scripts -Recurse -Whatif
    

    Otherwise, you may be able to find a solution:

    • by using the .NET v4.5+ [System.IO.Compression.ZipFile] class directly; you can load it into your session with Add-Type -Assembly System.IO.Compression.FileSystem (not necessary in PowerShell Core).

    • by using external programs such as 7-Zip,

    0 讨论(0)
  • 2020-12-06 07:16

    The cumbersome technique mklement0 mentioned worked for me. Below is the script I created to support a list of various files mixed with folders.

    # Compress LFS based files into a zip
    # To use
    #  1. place this script in the root folder
    #  2. modify the contents of $lfsAssetFiles to point to files relative to this root folder
    #  3. modify $zipDestination to be where you want the resultant zip to be placed
    # based off of https://stackoverflow.com/a/51394271
    
    # this should match files being .gitignored
    $lfsAssetFiles = 
    "\Assets\Project\Plugins\x32",
    "\Assets\Project\Plugins\x64\HugePlugin.dll"
    
    # This is where the contents of the zip file will be structured, because placing them inside of a specific folder of the zip is difficult otherwise
    $zipStruct = $PSScriptRoot + "\zipStruct"
    
    # the actual zip file that will be created
    $zipDestination = "C:\Dropbox\GitLfsZip\ProjectNameLfs.zip"
    
    # remove files from previous runs of this script
    If(Test-path $zipStruct) {Remove-item $zipStruct -Recurse}
    If(Test-path $zipDestination) {Remove-item $zipDestination}
    
    Foreach ($entry in $lfsAssetFiles)
    {
      # form absolute path to source each file to be included in the zip
      $sourcePath = $PSScriptRoot + $entry;
    
      # get the parent directories of the path. If the entry itself is a directory, we still only need the parent as the directory will be created when it is copied over.
      $entryPath = Split-Path -Parent $entry
    
      # form what the path will look like in the destination
      $entryPath = $zipStruct + $entryPath
    
      # ensure the folders to the entry path exist
      $createdPath = New-Item -Force -ItemType Directory $entryPath
    
      # copy the file or directory
      Copy-Item -Recurse -Force $sourcePath $createdPath
    }
    
    # create a zip file https://blogs.technet.microsoft.com/heyscriptingguy/2015/page/59/
    Add-Type -AssemblyName "system.io.compression.filesystem"
    [io.compression.zipfile]::CreateFromDirectory($zipStruct, $zipDestination)
    # Compress-Archive doesn't work here because it includes the "zipStruct" folder: Compress-Archive -Path $zipStruct -DestinationPath $zipDestination
    
    0 讨论(0)
  • 2020-12-06 07:20

    I wanted to do this without having to copy the full structure to a temp directory.

    #build list of files to compress
    $files = @(Get-ChildItem -Path .\procedimentos -Recurse | Where-Object -Property Name -EQ procedimentos.xlsx);
    $files += @(Get-ChildItem -Path .\procedimentos -Recurse | Where-Object -Property Name -CLike procedimento_*_fs_*_d_*.xml);
    $files += @(Get-ChildItem -Path .\procedimentos -Recurse | Where-Object -Property FullName -CLike *\documentos_*_fs_*_d_*);
    
    # exclude directory entries and generate fullpath list
    $filesFullPath = $files | Where-Object -Property Attributes -CContains Archive | ForEach-Object -Process {Write-Output -InputObject $_.FullName}
    
    #create zip file
    $zipFileName = 'procedimentos.zip'
    $zip = [System.IO.Compression.ZipFile]::Open((Join-Path -Path $(Resolve-Path -Path ".") -ChildPath $zipFileName), [System.IO.Compression.ZipArchiveMode]::Create)
    
    #write entries with relative paths as names
    foreach ($fname in $filesFullPath) {
        $rname = $(Resolve-Path -Path $fname -Relative) -replace '\.\\',''
        echo $rname
        $zentry = $zip.CreateEntry($rname)
        $zentryWriter = New-Object -TypeName System.IO.BinaryWriter $zentry.Open()
        $zentryWriter.Write([System.IO.File]::ReadAllBytes($fname))
        $zentryWriter.Flush()
        $zentryWriter.Close()
    }
    
    # clean up
    Get-Variable -exclude Runspace | Where-Object {$_.Value -is [System.IDisposable]} | Foreach-Object {$_.Value.Dispose(); Remove-Variable $_.Name};
    
    0 讨论(0)
提交回复
热议问题