嵌套的 zip 内容列表
Nested zip contents listing
我一直在做一个小项目,列出压缩在嵌套 zip 文件中的文件。
我编写了一个脚本来执行此操作,但前提是已知 zip 文件的深度。
在下面的示例中,zip 文件中有额外的 zip,然后在其中一个中有 anthoer。
Add-Type -AssemblyName System.IO.Compression.Filesystem
$path = "PATH"
$CSV_Path = "CSV_PATH"
$zipFile = Get-ChildItem $path -recurse -Filter "*.zip"
$rootArchive = [System.IO.Compression.zipfile]::OpenRead($zipFile.fullname)
$rootArchive.Entries | Select @{l = 'Source Zip'; e = {} }, @{l = "FullName"; e = { $_.FullName.Substring(0, $rootArchive.Fullname.Lastindexof('\')) } }, Name | Export-csv $CSV_Path -notypeinformation
$archivesLevel2 = $rootArchive.Entries | Where { $_.Name -like "*.zip" }
foreach ($archive in $archivesLevel2)
{
(New-object System.IO.Compression.ZipArchive ($archive.Open())).Entries | Select @{l = 'Source Zip'; e = { $archive.name } }, @{l = "FullName"; e = { $archive.FullName.Substring(0, $_.Fullname.Lastindexof('\')) } }, Name | Export-Csv $CSV_Path -NoTypeInformation -append;
New-object System.IO.Compression.ZipArchive($archive.Open()) -OutVariable +lastArchiveLevel2
}
$archivesLevel3 = $lastArchiveLevel2.entries | Where { $_.Name -like "*.zip" }
foreach ($archive in $archivesLevel3)
{
(New-Object System.IO.Compression.ZipArchive ($archive.Open())).Entries | Select @{l = 'Source Zip'; e = { $archive.name } }, @{l = "FullName"; e = { $archive.FullName.Substring(0, $_.Fullname.Lastindexof('\')) } }, Name | Export-Csv $CSV_Path -NoTypeInformation -append
}
我要求你帮我修改它以容纳未知深度的内部 zip 文件。这可能吗?
这里有一个递归的小例子,基本上,你遍历 ZipFile
的 .Entries
属性 并检查每个项目的扩展名是否是 .zip
,如果是,则将该条目传递给您的函数。
编辑: Un-deleting 这个答案主要是为了展示如何使用 递归函数 ,我之前的答案是不准确的。我正在使用 [ZipFile]::OpenRead(..)
读取嵌套的 .zip
文件,这些文件似乎在 Linux (.NET Core) 上可以正常工作,但在使用 Windows PowerShell 时显然不起作用。正确的方法是使用 [ZipArchive]::new($nestedZip.Open())
,如 所示。
using namespace System.IO
using namespace System.IO.Compression
function Get-ZipFile {
[cmdletbinding()]
param(
[parameter(ValueFromPipeline)]
[object]$Path,
[parameter(DontShow)]
[int]$Nesting = -1
)
begin { $Nesting++ }
process {
try
{
$zip = if(-not $Nesting) {
[ZipFile]::OpenRead($Path)
}
else {
[ZipArchive]::new($Path.Open())
}
foreach($entry in $zip.Entries) {
[pscustomobject]@{
Nesting = $Nesting
Parent = $Path.Name
Contents = $entry.FullName
}
if([Path]::GetExtension($entry) -eq '.zip') {
Get-ZipFile -Path $entry -Nesting $Nesting
}
}
}
catch
{
$PSCmdlet.WriteError($_)
}
finally
{
if($null -ne $zip) {
$zip.Dispose()
}
}
}
}
Get-ChildItem *.zip | Get-ZipFile
下面是一个关于如何使用 Queue 对象执行此操作的示例,它允许您一次性递归遍历 zip 文件的所有深度。
根据要求,这里有一些评论来解释发生了什么。
Add-Type -AssemblyName System.IO.Compression.Filesystem
$path = "PATH"
$CSV_Path = "CSV_PATH"
$Queue = [System.Collections.Queue]::New()
$zipFiles = Get-ChildItem $path -recurse -Filter "*.zip"
# All records will be stored here
$Output = [System.Collections.Generic.List[PSObject]]::new()
# Main logic. Used when looking at the root zip and any zip entries.
# ScriptBlock is used to prevent code duplication.
$ProcessEntries = {
Param($Entries)
$Entries | % {
# Put all zip in the queue for future processing
if ([System.IO.Path]::GetExtension($entry) -eq '.zip') { $Queue.Enqueue($_) }
# Add a Source Zip property with the parent zip since we want this informations in the csv export and it is not available otherwise.
$_ | Add-Member -MemberType NoteProperty -Name 'Source Zip' -Value $zip.name
# Every entries, zip or not, need to be part of the output
$output.Add($_)
}
}
# Your initial Get-ChildItem to find zip file implicate there could be multiple root zip files, so a loop is required.
Foreach ($zip in $zipFiles) {
$archive = [System.IO.Compression.zipfile]::OpenRead($zip.fullname)
# The $ProcessEntries scriptblock is invoked to fill the Queue and the output.
. $ProcessEntries $archive.Entries
# Should the Zip file have no zip entries, this loop will never be entered.
# Otherwise, the loop will continue as long as zip entries are detected while processing any child zip.
while ($Queue.Count -gt 0) {
# Removing item from the queue to avoid reprocessing it again.
$Item = $Queue.Dequeue()
$archive = New-object System.IO.Compression.ZipArchive ($Item.open())
# We call the main scriptblock again to fill the queue and the output.
. $ProcessEntries $archive.Entries
}
}
$Output | Select 'Source Zip', FullName, Name | Export-Csv $CSV_Path -NoTypeInformation
参考资料
我一直在做一个小项目,列出压缩在嵌套 zip 文件中的文件。 我编写了一个脚本来执行此操作,但前提是已知 zip 文件的深度。 在下面的示例中,zip 文件中有额外的 zip,然后在其中一个中有 anthoer。
Add-Type -AssemblyName System.IO.Compression.Filesystem
$path = "PATH"
$CSV_Path = "CSV_PATH"
$zipFile = Get-ChildItem $path -recurse -Filter "*.zip"
$rootArchive = [System.IO.Compression.zipfile]::OpenRead($zipFile.fullname)
$rootArchive.Entries | Select @{l = 'Source Zip'; e = {} }, @{l = "FullName"; e = { $_.FullName.Substring(0, $rootArchive.Fullname.Lastindexof('\')) } }, Name | Export-csv $CSV_Path -notypeinformation
$archivesLevel2 = $rootArchive.Entries | Where { $_.Name -like "*.zip" }
foreach ($archive in $archivesLevel2)
{
(New-object System.IO.Compression.ZipArchive ($archive.Open())).Entries | Select @{l = 'Source Zip'; e = { $archive.name } }, @{l = "FullName"; e = { $archive.FullName.Substring(0, $_.Fullname.Lastindexof('\')) } }, Name | Export-Csv $CSV_Path -NoTypeInformation -append;
New-object System.IO.Compression.ZipArchive($archive.Open()) -OutVariable +lastArchiveLevel2
}
$archivesLevel3 = $lastArchiveLevel2.entries | Where { $_.Name -like "*.zip" }
foreach ($archive in $archivesLevel3)
{
(New-Object System.IO.Compression.ZipArchive ($archive.Open())).Entries | Select @{l = 'Source Zip'; e = { $archive.name } }, @{l = "FullName"; e = { $archive.FullName.Substring(0, $_.Fullname.Lastindexof('\')) } }, Name | Export-Csv $CSV_Path -NoTypeInformation -append
}
我要求你帮我修改它以容纳未知深度的内部 zip 文件。这可能吗?
这里有一个递归的小例子,基本上,你遍历 ZipFile
的 .Entries
属性 并检查每个项目的扩展名是否是 .zip
,如果是,则将该条目传递给您的函数。
编辑: Un-deleting 这个答案主要是为了展示如何使用 递归函数 ,我之前的答案是不准确的。我正在使用 [ZipFile]::OpenRead(..)
读取嵌套的 .zip
文件,这些文件似乎在 Linux (.NET Core) 上可以正常工作,但在使用 Windows PowerShell 时显然不起作用。正确的方法是使用 [ZipArchive]::new($nestedZip.Open())
,如
using namespace System.IO
using namespace System.IO.Compression
function Get-ZipFile {
[cmdletbinding()]
param(
[parameter(ValueFromPipeline)]
[object]$Path,
[parameter(DontShow)]
[int]$Nesting = -1
)
begin { $Nesting++ }
process {
try
{
$zip = if(-not $Nesting) {
[ZipFile]::OpenRead($Path)
}
else {
[ZipArchive]::new($Path.Open())
}
foreach($entry in $zip.Entries) {
[pscustomobject]@{
Nesting = $Nesting
Parent = $Path.Name
Contents = $entry.FullName
}
if([Path]::GetExtension($entry) -eq '.zip') {
Get-ZipFile -Path $entry -Nesting $Nesting
}
}
}
catch
{
$PSCmdlet.WriteError($_)
}
finally
{
if($null -ne $zip) {
$zip.Dispose()
}
}
}
}
Get-ChildItem *.zip | Get-ZipFile
下面是一个关于如何使用 Queue 对象执行此操作的示例,它允许您一次性递归遍历 zip 文件的所有深度。
根据要求,这里有一些评论来解释发生了什么。
Add-Type -AssemblyName System.IO.Compression.Filesystem
$path = "PATH"
$CSV_Path = "CSV_PATH"
$Queue = [System.Collections.Queue]::New()
$zipFiles = Get-ChildItem $path -recurse -Filter "*.zip"
# All records will be stored here
$Output = [System.Collections.Generic.List[PSObject]]::new()
# Main logic. Used when looking at the root zip and any zip entries.
# ScriptBlock is used to prevent code duplication.
$ProcessEntries = {
Param($Entries)
$Entries | % {
# Put all zip in the queue for future processing
if ([System.IO.Path]::GetExtension($entry) -eq '.zip') { $Queue.Enqueue($_) }
# Add a Source Zip property with the parent zip since we want this informations in the csv export and it is not available otherwise.
$_ | Add-Member -MemberType NoteProperty -Name 'Source Zip' -Value $zip.name
# Every entries, zip or not, need to be part of the output
$output.Add($_)
}
}
# Your initial Get-ChildItem to find zip file implicate there could be multiple root zip files, so a loop is required.
Foreach ($zip in $zipFiles) {
$archive = [System.IO.Compression.zipfile]::OpenRead($zip.fullname)
# The $ProcessEntries scriptblock is invoked to fill the Queue and the output.
. $ProcessEntries $archive.Entries
# Should the Zip file have no zip entries, this loop will never be entered.
# Otherwise, the loop will continue as long as zip entries are detected while processing any child zip.
while ($Queue.Count -gt 0) {
# Removing item from the queue to avoid reprocessing it again.
$Item = $Queue.Dequeue()
$archive = New-object System.IO.Compression.ZipArchive ($Item.open())
# We call the main scriptblock again to fill the queue and the output.
. $ProcessEntries $archive.Entries
}
}
$Output | Select 'Source Zip', FullName, Name | Export-Csv $CSV_Path -NoTypeInformation
参考资料