読者です 読者をやめる 読者になる 読者になる

tech.guitarrapc.cóm

C#, PowerShell, Unity, Cloud, Serverless Technical Update and Features

PowerShell で Zip圧縮、解凍処理に ZipFileクラスを利用してみる (続き)

前回、Powershell のZip処理をDSCと比較してみるでPowerShellによる zip処理に、ZipFileクラスを使う例を紹介しました。

牟田口先生から以下のご指摘を受けたので修正版を公開します。

コード

GitHubと、修正後のコード全体をさらしておきます。

GitHub

PowerShellUtil / PS-Zip / PS-Zip.psm1

Zip圧縮処理

function New-ZipCompression{

    [CmdletBinding()]
    param(
        [parameter(
            mandatory,
            position = 0,
            valuefrompipeline,
            valuefrompipelinebypropertyname)]
        [string]
        $source,

        [parameter(
            mandatory = 0,
            position = 1,
            valuefrompipeline,
            valuefrompipelinebypropertyname)]
        [string]
        $destination,

        [parameter(
            mandatory = 0,
            position = 2)]
        [switch]
        $quiet,

        [parameter(
            mandatory = 0,
            position = 3)]
        [switch]
        $force
    )

    try
    {
        Add-Type -AssemblyName "System.IO.Compression.FileSystem"
    }
    catch
    {
    }

    # check file is directory
    $file = Get-Item -Path $source

    # set zip extension
    $zipExtension = ".zip"

    # set desktop as destination path if it null
    if ([string]::IsNullOrWhiteSpace($destination))
    {
        if ($file.Mode -like "d*")
        {
            $destination = (Join-Path ([System.Environment]::GetFolderPath([Environment+SpecialFolder]::Desktop)) *1 + $zipExtension))
        }
        else
        {
            $destination = (Join-Path ([System.Environment]::GetFolderPath([Environment+SpecialFolder]::Desktop)) *2
        }
        
    }

    # check destination is input as .zip
    if (-not($destination.EndsWith($zipExtension)))
    {
        throw ("destination parameter value [{0}] not end with extension {1}" -f $destination, $zipExtension)
    }

    # check destination is already exist or not
    if (Test-Path $destination)
    {
        Remove-Item -Path $destination -Confirm
    }


    # compressionLevel
    $compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal

    # check file mode for source
    Write-Verbose ("file.mode = {0}" -f $file.Mode)

    if ($file.Mode -like "d*") # Directory should be d---
    {
        try # create zip from directory
        {
            # force output zip file to new destination path, avoiding destination zip name conflict.
            if ($force)
            {
                # check destination is already exist, CreateFromDirectory will fail with same name of destination file.
                if (Test-Path $destination)
                {
                    # show warning for same destination exist.
                    Write-Verbose ("Detected destination name {0} is already exist. Force trying output to new destination zip name." -f $destination)

                    # get current destination information
                    $destinationRoot = [System.IO.Path]::GetDirectoryName($destination)
                    $destinationfile = [System.IO.Path]::GetFileNameWithoutExtension($destination)
                    $destinationExtension = [System.IO.Path]::GetExtension($destination)

                    # renew destination name with (2)...(x) until no more same name catch.
                    $count = 2
                    $destination = Join-Path $destinationRoot ($destinationfile + "(" + $count + ")" + $destinationExtension)
                    while (Test-Path $destination)
                    {
                        ++$count
                        $destination = Join-Path $destinationRoot ($destinationfile + "(" + $count + ")" + $destinationExtension)
                    }

                    # show warning as destination name had been changed due to escape error.
                    Write-Verbose ("Deistination name change to new name {0}" -f $destination)
                }
            }

            # include BaseDirectory
            $includeBaseDirectory = $true

            Write-Verbose ("destination = {0}" -f $destination)

            Write-Verbose ("file.fullname = {0}" -f $file.FullName)
            Write-Verbose ("compressionLevel = {0}" -f $compressionLevel)
            Write-Verbose ("includeBaseDirectory = {0}" -f $includeBaseDirectory)

            if ($quiet)
            {
                Write-Verbose ("zipping up folder {0} to {1}" -f $file.FullName,$destination)
                [System.IO.Compression.ZipFile]::CreateFromDirectory($file.fullname,$destination,$compressionLevel,$includeBaseDirectory) > $null
                $?
            }
            else
            {
                Write-Verbose ("zipping up folder {0} to {1}" -f $file.FullName,$destination)
                [System.IO.Compression.ZipFile]::CreateFromDirectory($file.fullname,$destination,$compressionLevel,$includeBaseDirectory)
                Get-Item $destination
            }
        }
        catch
        {
            Write-Error $_
            $?
        }
    }
    else
    {
        try # create zip from files
        {
            # create zip to add
            $destzip = [System.IO.Compression.Zipfile]::Open($destination,"Update")

            # get items
            $files = Get-ChildItem -Path $source

            foreach ($file in $files)
            {
                $file2 = $file.name

                Write-Verbose ("destzip = {0}" -f $destzip)
                Write-Verbose ("file.fullname = {0}" -f $file.FullName)
                Write-Verbose ("file2 = {0}" -f $file2)
                Write-Verbose ("compressionLevel = {0}" -f $compressionLevel)

                if ($quiet)
                {
                    Write-Verbose ("zipping up files {0} to {1}" -f $file.FullName,$destzip)
                    [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($destzip,$file.fullname,$file2,$compressionLevel) > $null
                    $?
                }
                else
                {
                    Write-Verbose ("zipping up files {0} to {1}" -f $file.FullName,$destzip)
                    [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($destzip,$file.fullname,$file2,$compressionLevel)
                }
            }
        }
        catch
        {
            Write-Error $_
            $?
        }
        finally
        {
            # dispose opened zip
            $destzip.Dispose()
        }
    }
}

Zip解凍処理

function New-ZipExtract{

    [CmdletBinding()]
    param(
        [parameter(
            mandatory,
            position = 0,
            valuefrompipeline,
            valuefrompipelinebypropertyname)]
        [string]
        $source,

        [parameter(
            mandatory = 0,
            position = 1,
            valuefrompipeline,
            valuefrompipelinebypropertyname)]
        [string]
        $destination,

        [parameter(
            mandatory = 0,
            position = 2)]
        [switch]
        $quiet,

        [parameter(
            mandatory = 0,
            position = 3)]
        [switch]
        $force
    )

    try
    {
        Add-Type -AssemblyName "System.IO.Compression.FileSystem"
    }
    catch
    {
    }

    $zipExtension = ".zip"

    # check source is input as .zip
    if (-not($source.EndsWith($zipExtension)))
    {
        throw ("source parameter value [{0}] not end with extension {1}" -f $source, $zipExtension)
    }

    # set desktop as destination path if it null
    if ([string]::IsNullOrWhiteSpace($destination))
    {
        $destination = (Join-Path ([System.Environment]::GetFolderPath([Environment+SpecialFolder]::Desktop)) (([System.IO.Path]::GetFileNameWithoutExtension($source))))
    }

    # check destination is already exist or not
    if (Test-Path $destination)
    {
        Remove-Item -Path $destination -Recurse -Confirm
    }
        
    try
    {
        # force output zip file to new destination path, avoiding destination zip name conflict.
        if ($force)
        {
            # check destination is already exist, CreateFromDirectory will fail with same name of destination file.
            if (Test-Path $destination)
            {
                # show warning for same destination exist.
                Write-Verbose ("Detected destination name {0} is already exist. Force trying output to new destination zip name." -f $destination)

                # get current destination information
                $destinationRoot = [System.IO.Path]::GetDirectoryName($destination)
                $destinationfile = [System.IO.Path]::GetFileName($destination)

                # renew destination name with (2)...(x) until no more same name catch.
                $count = 2
                $destination = Join-Path $destinationRoot ($destinationfile + "(" + $count + ")")
                while (Test-Path $destination)
                {
                    ++$count
                    $destination = Join-Path $destinationRoot ($destinationfile + "(" + $count + ")")
                }

                # show warning as destination name had been changed due to escape error.
                Write-Verbose ("Deistination name change to new name {0}" -f $destination)
            }
        }

        # create source zip and complression
        $sourcezip = [System.IO.Compression.Zipfile]::Open($source,"Update")
        $compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal

        Write-Verbose ("sourcezip = {0}" -f $sourcezip)
        Write-Verbose ("destination = {0}" -f $destination)

        if ($quiet)
        {
            [System.IO.Compression.ZipFileExtensions]::ExtractToDirectory($sourcezip,$destination) > $null
            $?
        }
        else
        {
            [System.IO.Compression.ZipFileExtensions]::ExtractToDirectory($sourcezip,$destination)
        }

        $sourcezip.Dispose()
    }
    catch
    {
        Write-Error $_
    }
    finally
    {
        $sourcezip.Dispose()
    }
}

利用方法

以前と変わっていませんが、いくつか追加しています。

  1. パラメータとして、-forceスイッチを付け加えています。
  2. 単独ファイルだけでなく、ワイルドカード(*)での複数ファイル指定、サブディレクトリを含むフォルダにも対応しています。
  3. 圧縮、解凍ともに-destination パラメータは必須ではないようにし、指定されなかった場合は デスクトップに生成するようにしています。

サンプル

圧縮例です。

# Compres Sample
New-ZipCompression -source D:\test -destination d:\hoge.zip

# 出力結果が true/falseのみとなる
New-ZipCompression -source D:\test -destination d:\hoge.zip -quiet

# デスクトップに、sourceに指定したフォルダ名でzipを生成
New-ZipCompression -source D:\test

# デスクトップに、sourceに指定したファイルを名称としてzipを生成
New-ZipCompression -source D:\test\hoge.ps1

# デスクトップに、sourceに指定したファイルの先頭を名称としてzipを生成
New-ZipCompression -source D:\test\*.ps1

# destinationが重複していた場合、削除するか聞いてきます(Nで消さないと例外)
New-ZipCompression -source D:\test\*.ps1

# -forceを付けることで、たとえdestinationが重複していた場合に削除しなくても、自動的に連番を付けてzip圧縮します。
New-ZipCompression -source D:\test\*.ps1 -force

解凍も同様に逆操作が可能です。

# Extract Sample
New-ZipExtract -source d:\hoge.zip -destination d:\hogehoge

# 出力結果が true/falseのみとなる
New-ZipExtract -source d:\hoge.zip -destination d:\hogehoge -quiet

# デスクトップに、sourceに指定したzip名でフォルダを生成
New-ZipExtract -source D:\hoge.zip

# destinationが重複していた場合、削除するか聞いてきます(Nで消さないと例外)
New-ZipExtract -source D:\test\hoge.zip

# -forceを付けることで、たとえdestinationが重複していた場合に削除しなくても、自動的に連番を付けてzip圧縮します。
New-ZipExtract -source D:\test\hoge.ps1 -force



まとめ

ずいぶん量が増えました (汗

とりあえず普通に使えそうなのですが、気になる点、おかしな点があればご指摘いただけると....!

*1:[System.IO.Path]::GetFileNameWithoutExtension($source

*2:[System.IO.Path]::GetFileNameWithoutExtension(($file | select -First 1 -ExpandProperty fullname))) + $zipExtension