从 ZIP 存档中提取单个文件

问题描述 投票:0回答:1

我正在使用下一个代码来下载一些 zip 存档:

$client = new-object System.Net.WebClient
$client.DownloadFile("https://chromedriver.storage.googleapis.com/$LatestChromeRelease/chromedriver_win32.zip","D:\MyFolder.zip")

结果我得到了包含所需文件的 ZIP 存档“MyFolder.zip”(让我们想象一下“test.txt”)。

如何将这个特定文件从 ZIP 存档提取到给定文件夹中?

powershell unzip
1个回答
7
投票

PowerShell 4+ 有一个

Expand-Archive
命令,但从 PS 7.2.3 开始,它只能完全提取存档。因此,将其解压到临时目录并复制您感兴趣的文件。

如果您有 PS 5.1+ 可用,请向下滚动以获取使用 .NET 类的更高效的解决方案。

$archivePath = 'D:\MyFolder.zip'
$destinationDir = 'D:\MyFolder'

# Relative path of file in ZIP to extract. 
$fileToExtract = 'test.txt'

# Create destination dir if not exist.
$null = New-Item $destinationDir -ItemType Directory -Force

# Create a unique temporary directory
$tempDir = Join-Path ([IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString('n'))
$null = New-Item $tempDir -ItemType Directory

try {
    # Extract archive to temp dir
    Expand-Archive -LiteralPath $archivePath -DestinationPath $tempDir

    # Copy the file we are interested in
    $tempFilePath = Join-Path $tempDir $fileToExtract
    Copy-Item $tempFilePath $destinationDir 
}
finally {
    # Remove the temp dir
    if( Test-Path $tempDir ) { 
        Remove-Item $tempDir -Recurse -Force -EA Continue 
    }
}

使用 PS 5.1+,您可以使用 .NET 类 直接提取单个文件(无需提取整个存档):

# Load required .NET assemblies. Not necessary on PS Core 7+.
Add-Type -Assembly System.IO.Compression.FileSystem

$archivePath = 'D:\MyFolder.zip'
$destinationDir = 'D:\MyFolder'

# Relative path of file in ZIP to extract.
# Use FORWARD slashes as directory separator, e. g. 'subdir/test.txt'
$fileToExtract = 'test.txt'

# Create destination dir if not exist.
$null = New-Item $destinationDir -ItemType Directory -Force

# Convert (possibly relative) paths for safe use with .NET APIs
$resolvedArchivePath    = Convert-Path -LiteralPath $archivePath
$resolvedDestinationDir = Convert-Path -LiteralPath $destinationDir

$archive = [IO.Compression.ZipFile]::OpenRead( $resolvedArchivePath )
try {
    # Locate the desired file in the ZIP archive.
    # Replace $_.Fullname by $_.Name if file shall be found in any sub directory.
    if( $foundFile = $archive.Entries.Where({ $_.FullName -eq $fileToExtract }, 'First') ) {
    
        # Combine destination dir path and name of file in ZIP
        $destinationFile = Join-Path $resolvedDestinationDir $foundFile.Name
        
        # Extract the file.
        [IO.Compression.ZipFileExtensions]::ExtractToFile( $foundFile[ 0 ], $destinationFile )
    }
    else {
        Write-Error "File not found in ZIP: $fileToExtract"
    }
}
finally {
    # Dispose the archive so the file will be unlocked again.
    if( $archive ) {
        $archive.Dispose()
    }
}

备注:

    将可能是相对路径的 PowerShell 路径传递到 .NET API 时,应使用
  • Convert-Path
    。 .NET 框架有自己的当前目录,该目录不一定与 PowerShell 的目录匹配。使用
    Convert-Path
    我们转换为绝对路径,因此 .NET 的当前目录不再相关。
  • .Where
    .ForEach
    是 PowerShell 内在方法,可用于所有对象。它们类似于
    Where-Object
    ForEach-Object
    命令,但效率更高。一旦我们找到文件,将
    'First'
    作为第二个参数传递给
    .Where
    就会停止搜索。
  • 请注意,
    .Where
    始终输出一个集合,即使只有一个元素匹配。这与
    Where-Object
    相反,如果只有单个元素匹配,则返回 单个对象。因此,在将其传递给函数
    $foundFile[ 0 ]
    时,我们必须编写
    ExtractToFile
    ,而不是仅仅将
    $foundFile
    写为数组。
© www.soinside.com 2019 - 2024. All rights reserved.