Modify txt file inside a ZIP file in PowerShell

1k Views Asked by At

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"
}
1

There are 1 best solutions below

9
Santiago Squarzon On BEST ANSWER

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 $_
    }