Is there a way to Expand-Archive without overwriting files?

Viewed 56

Is there a way in PowerShell to use Expand-Archive so that files are written where they don't exist, but are not overwritten when they do exist? I can achieve this with -ErrorAction SilentlyContinue, but that ignores things that might be actual errors.

1 Answers

To silence only "file already exists" error messages of Expand-Archive, you can redirect the error stream to the success stream and process error records using ForEach-Object:

Expand-Archive -Path Test.zip -DestinationPath . -EA Continue 2>&1 | ForEach-Object {
   if( $_ -is [System.Management.Automation.ErrorRecord] ) {
        if( $_.FullyQualifiedErrorId -split ',' -notcontains 'ExpandArchiveFileExists' ) {
            Write-Error $_   # output error that is not "file exists"
        }
   } 
   else {
       $_  # pass success stream through
   }
}
  • -EA Continue (-ErrorAction) overrides the preference variable $ErrorActionPreference to make sure errors are not turned into exceptions (in which case the first error would interrupt the extraction).
  • 2>&1 redirects (merges) the error stream (#2) to the success stream (#1), so both can be processed using ForEach-Object.
  • $_ -is [System.Management.Automation.ErrorRecord] tests if the current pipeline element is an error record.
    • When this is the case, we test what kind of error we have, by checking the FullyQualifiedErrorId property of the ErrorRecord (the exception type System.IO.IOException would be too general to test for)
    • Otherwise it is a message from the success stream, which will be simply passed through.
  • In case you are wondering how I came up with that FullyQualifiedErrorId thing, I just run Expand-Archive without redirection and called Get-Error afterwards. This outputs all information of the last error record, so I could look up the information to detect the error condition.

An alternative solution, similar to the one suggested by Abraham Zinala, is to unconditionally silence all errors and use -ErrorVariable to collect the errors and shown the relevant ones after the call to Expand-Archive has returned:

$oldErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'SilentlyContinue'
$archiveErrors = $null

Expand-Archive -Path Test.zip -DestinationPath . -ErrorVariable archiveErrors

$ErrorActionPreference = $oldErrorActionPreference

$archiveErrors | Sort-Object { $_ | Out-String } -Unique | ForEach-Object {
    if( $_ -is [System.Management.Automation.ErrorRecord] ) {
        if( $_.FullyQualifiedErrorId -split ',' -notcontains 'ExpandArchiveFileExists' ) {
            $_   # output error that is not "file exists"
        }
    }
}
  • The errors of Expand-Archive cannot be completely silenced through the -ErrorAction parameter, because some errors (like input file doesn't exist) are detected as part of parameter validation. To really silence all errors, the $ErrorActionPreference variable must be used.
  • It is important to set the error variable to $null before calling Expand-Archive because the command doesn't reset the variable, when there is no error.
  • The name of the variable passed to -ErrorVariable must be specified without $.
  • The Sort-Object -Unique command makes sure we don't show duplicate errors.
Related