powershellzipsystem.io.compressionc#-ziparchive

Modify txt file inside a ZIP file in PowerShell


I have a script that can open a ZIP file and modify the content of a xml file and working good I want to run for a loop at all the ZIP files and change them but I get an error enter image description here

$files = Get-ChildItem -path "C:\Temp\SharedFolder\SideVIP" -filter *.VIP 
write-host $files 
$fileToEdit = "vip.manifest"
$replaceWithVersion = '<Prop Name="WarningDuringUpgrade" Value="False"'
Add-Type -assembly  System.IO.Compression.FileSystem

foreach ($file in $files)
{
 # Open zip and find the particular file (assumes only one inside the Zip file)
    
    $zip =  [System.IO.Compression.ZipFile]::Open($file,"Update")

    $nuspecFile = $zip.Entries.Where({$_.name -like $fileToEdit})
 # Read the contents of the file
    $desiredFile = [System.IO.StreamReader]($nuspecFile).Open()
    $text = $desiredFile.ReadToEnd()
    $desiredFile.Close()
    $desiredFile.Dispose()
    $text = $text -replace  '<Prop Name="WarningDuringUpgrade" Value="True"',$replaceWithVersion
    #update file with new content
    $desiredFile = [System.IO.StreamWriter]($nuspecFile).Open()
    $desiredFile.BaseStream.SetLength(0)
 # Insert the $text to the file and close
    $desiredFile.Write($text)
    $desiredFile.Flush()
    $desiredFile.Close()

 # Write the changes and close the zip file
   $zip.Dispose()
    Write-Host "zip file updated"
}

Solution

  • My guess would be that your code could be failing due to 2 possible reasons, either $nuspecFile is $null meaning, inside the .zip there were no entries with name vip.manifest or, there were more than 1 entry found. An inner loop should take care of both possibilities. Another thing to note, you're using -like but no wildcards:

    $fileToEdit = "vip.manifest"
    ....
    ....
    $entries = $zip.Entries.Where({ $_.Name -like $fileToEdit })
    

    Are you sure you didn't mean to do (note the wildcards on $fileToEdit):

    $fileToEdit = "*vip.manifest*"
    ....
    ....
    $entries = $zip.Entries.Where({ $_.Name -like $fileToEdit })
    

    If not, then you probably want to use -eq instead of -like for an exact match of the entry name. I have also changed -replace for .Replace(..) string method since, by the looks of it, you want to replace literal strings and there is no regex involved.

    Add-Type -Assembly  System.IO.Compression.FileSystem
    $ErrorActionPreference = 'Stop'
    
    $files       = Get-ChildItem -path "C:\Temp\SharedFolder\SideVIP" -Filter *.VIP 
    $fileToEdit  = "vip.manifest"
    $toReplace   = '<Prop Name="WarningDuringUpgrade" Value="True"'
    $replaceWith = '<Prop Name="WarningDuringUpgrade" Value="False"'
    
    foreach ($file in $files) {
        try {
            $zip = [System.IO.Compression.ZipFile]::Open($file, "Update")
            $entries = $zip.Entries.Where({ $_.Name -like $fileToEdit })
            foreach($entry in $entries) {
                $reader  = [System.IO.StreamReader]::new($entry.Open())
                $content = $reader.ReadToEnd().Replace($toReplace, $replaceWith)
                $writer  = [System.IO.StreamWriter]::new($entry.Open())
                $writer.BaseStream.SetLength(0)
                $writer.Write($content)
                $writer, $reader | ForEach-Object Dispose
            }
        }
        catch {
            Write-Warning $_.Exception.Message
            continue
        }
        finally {
            if($zip) {
                $zip.Dispose()
            }
        }
    }
    

    If you're looking to simplify the process demonstrated above, reading a zip archive and replacing the content of zip archive entries, you might find it easier with the PSCompression Module (Disclaimer: I'm the author of this module).

    This is how the code would look using the module:

    $toReplace = '<Prop Name="WarningDuringUpgrade" Value="True"'
    $replaceWith = '<Prop Name="WarningDuringUpgrade" Value="False"'
    
    Get-ChildItem -Path 'C:\Temp\SharedFolder\SideVIP' -Filter *.VIP |
        Get-ZipEntry -Include *vip.manifest* -EntryType Archive |
        ForEach-Object {
            $content = $_ | Get-ZipEntryContent -Raw
            $content.Replace($toReplace, $replaceWith) |
                Set-ZipEntryContent $_
        }