El script de PowerShell no comprime los archivos correctos

Resuelto techguy1029 asked hace 6 años • 2 respuestas
 Function Zip
{
    Param
    (
        [string]$zipFile
        ,
        [string[]]$toBeZipped
    )
    $CurDir = Get-Location
    Set-Location "C:\Program Files\7-Zip"
    .\7z.exe A -tzip $zipFile $toBeZipped | Out-Null
    Set-Location $CurDir
}
$Now = Get-Date
$Days = "60"
$TargetFolder = "C:\users\Admin\Downloads\*.*"
$LastWrite = $Now.AddDays(-$Days)
$Files = Get-Childitem $TargetFolder -Recurse | Where {$_.LastWriteTime -le "$LastWrite"}
$Files
Zip C:\Users\Admin\Desktop\TEST.zip $Files

Estoy probando este script que encontré en línea. Mi problema es que en lugar de comprimir los archivos en la carpeta de destino, copia y comprime el contenido de la carpeta de archivos del programa 7-zip. Que podria causar esto? gracias de antemano

techguy1029 avatar Nov 21 '18 01:11 techguy1029
Aceptado

Pase los archivos como rutas completas a la Zipfunción, usando su .FullNamepropiedad (sintaxis de PSv3+):

Zip C:\Users\Admin\Desktop\TEST.zip $Files.FullName

El problema es que, en Windows PowerShell , las [System.IO.FileInfo]instancias devueltas situacionalmente [1] se encadenan solo a sus nombres de archivoGet-ChildItem , que es lo que sucedió en su caso, por lo que su Zipfunción interpretó los $toBeZippedvalores como relativos a la ubicación actual , que es C:\Program Files\7-Zipen ese punto.

Dicho esto, es mejor no usarlo Set-Locationen tu función por completo , de modo que en caso de que quieras pasar rutas relativas reales , se interpreten correctamente como relativas a la ubicación actual :

Function Zip {
    Param
    (
        [Parameter(Mandatory)] # make sure a value is passed          
        [string]$zipFile
        ,
        [Parameter(Mandatory)] # make sure a value is passed
        [string[]]$toBeZipped
    )
    # Don't change the location, use & to invoke 7z by its full path.
    $null = & "C:\Program Files\7-Zip\7z.exe" A -tzip $zipFile $toBeZipped
    # You may want to add error handling here.
}

[1] Cuando la salida se refiere únicamente Get-ChildItema nombres de archivos:

Nota:

  • Si Get-ChildItemla salida se va a pasar a otros cmdlets de procesamiento de archivos , por ejemplo Rename-Item, el problema se puede evitar proporcionándoles entrada a través de la canalización , que implícitamente se vincula al -LiteralPathparámetro del cmdlet de destino mediante la ruta completa; consulte esta respuesta para obtener más información.

  • Get-ItemAfortunadamente, la salida del cmdlet relacionado siempre incluye la ruta completa .

  • Afortunadamente, en PowerShell (Core) v6.1+, Get-ChildItemtambién siempre se especifica la ruta completa .

Por lo tanto, lo siguiente sólo se aplica Get-ChildItemen Windows PowerShell :

El problema es doble:

  • Incluso los cmdlets integrados de PowerShell vinculan los argumentos de archivos/directorios (valores de parámetros, a diferencia de la entrada a través de la canalización ) no como objetos , sino como cadenas (el cambio de este comportamiento se analiza en el número 6057 de GitHub ).

  • Por lo tanto, para un paso de argumentos sólido, debe asegurarse de que su Get-ChildItemsalida se encadene consistentemente a rutas completas , lo cual Get-ChildItemno garantiza , y es fácil olvidar cuando se produce una encadenamiento de solo nombre o incluso que debe prestarle atención.

Pasar siempre los .FullNamevalores de las propiedades es la solución más sencilla o, para un funcionamiento fiable con cualquier proveedor de PowerShell, no solo con el sistema de archivos, .PSPath.

[System.IO.FileInfo]y [System.IO.DirectoryInfo]las instancias generadas por un Get-ChildItemcomando se encadenan a sus nombres de archivo solo, si y solo si :

  • Si se pasan una o más rutas de directorio literales-LiteralPath a o -Path(posiblemente como el primer argumento posicional) o no se pasa ninguna ruta (apunta a la ubicación actual); es decir, si se enumeran los contenidos de los directorios.

  • y tampoco usa los parámetros /-Include-Exclude (si se usan no-Filter hacediferencia).

  • Por el contrario, no importa si están presentes o no los siguientes :

    • -Filter(opcionalmente como segundo argumento posicional, pero tenga en cuenta que especificar una expresión comodín como *.txtel primer (y posiblemente único) argumento posicional se vincula al -Pathparámetro)
    • -Recurse(por sí solo , pero tenga en cuenta que a menudo se combina con -Include/ -Exclude)

Comandos de ejemplo:

# NAME-ONLY stringification:

Get-ChildItem | % ToString # no target path

Get-ChildItem . | % ToString # path is literal dir.

Get-ChildItem . *.txt | % ToString  # path is literal dir., combined with -Filter

# FULL PATH stringification:

Get-ChildItem foo* | % ToString # non-literal path (wildcard)

Get-ChildItem -Recurse -Include *.txt | % ToString # use of -Include

Get-ChildItem file.txt | % ToString # *file* path
mklement0 avatar Nov 20 '2018 19:11 mklement0

Si desactiva (temporalmente) |Out-Nullverá qué mensaje de error pasa.
$Files contiene objetos, no solo una matriz de nombres de archivos.

De forma predeterminada, PowerShell intenta encadenar esto usando la Namepropiedad que no contiene la ruta, por lo que 7zip no puede encontrar los archivos ya que también cambia la ruta a la carpeta 7zip (y -recurse recopila $files)

Así que cambia la línea

$Files = Get-Childitem $TargetFolder -Recurse | Where {$_.LastWriteTime -le "$LastWrite"}

y agregar

| Select-Object -ExpandProperty FullName

Una versión ligeramente reformateada de su fuente:

Function Zip{
    Param (
        [string]$zipFile,
        [string[]]$toBeZipped
    )
    & "C:\Program Files\7-Zip\7z.exe" A -tzip $zipFile $toBeZipped | Out-Null
}
$Days = "60"
$LastWrite = (Get-Date).Date.AddDays(-$Days)

$TargetFolder = "$($ENV:USERPROFILE)\Downloads\*"

$Files = Get-Childitem $TargetFolder -Recurse | 
   Where {$_.LastWriteTime -le $LastWrite} | 
     Select-Object -ExpandProperty FullName
$Files
Zip "$($ENV:USERPROFILE)\Desktop\TEST.zip" $Files
 avatar Nov 20 '2018 19:11