0

ファイルアイテムを関数に渡し、foreachループで$ inputを使用すると、(非決定論的のように)奇妙な動作が発生します。

このように関数を呼び出しています...

get-childitem Stuff | Create-Zip C:\Stuff.zip

ここで、「Stuff」には、ディレクトリとサブディレクトリを含む一連のフォルダが含まれています。問題は、繰り返し実行すると、空であるかどうかに関係なく、一部の最上位ディレクトリがコピーされないことです。

この関数は、 http://blogs.msdn.com/b/daiken/archive/2007/02/12/compress-files-with-windows-powershell-then-package-a-windowsにあるもののほぼ直接のコピーです。-vista-sidebar-gadget.aspx

function Create-Zip
{
    param([string]$zipfile)
    set-content $zipfile ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
    (dir $zipfile).IsReadOnly = $false  

    $shellApplication = new-object -comObject Shell.Application
    $zipPackage = $shellApplication.NameSpace($zipfile)

    foreach($item in $input)
    { 
        $zipPackage.CopyHere($item.FullName)
        Start-sleep -milliseconds 500
    }
}

問題はStart-Sleep行にあるようです。これを完全に省略すると、zipファイルは空になります... 10秒に増やすと、通常、zipファイルはいっぱいになります。これはなぜですか、そして睡眠値に依存せずにこれを書くためのより良い方法はありますか?

4

2 に答える 2

0

$inputスクリプトブロックのコンテキストで評価する必要がありますprocess {}。例:

function Create-Zip
{
    param([string]$zipfile)
     set-content $zipfile ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
     (dir $zipfile).IsReadOnly = $false 

    $shellApplication = new-object -comObject Shell.Application
    $zipPackage = $shellApplication.NameSpace($zipfile)

    process {
        foreach($item in $input)
        { 
            $zipPackage.CopyHere($item.FullName)
            Start-sleep -milliseconds 500
        }
    }
}

ただし、PowerShell V2 を使用している場合、これはファイルに関連するパイプライン入力 (文字列パス、FileInfo など) を渡すためのより良い方法です。

function Verb-Noun
{
    [CmdletBinding(DefaultParameterSetName="Path")]
    param(
        [Parameter(Mandatory=$true, Position=0, ParameterSetName="Path", 
                   ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true,
                   HelpMessage="Path to ...")]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Path,

        [Alias("PSPath")]
        [Parameter(Mandatory=$true, Position=0, ParameterSetName="LiteralPath", 
                   ValueFromPipelineByPropertyName=$true,
                   HelpMessage="Path to ...")]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $LiteralPath
    )

    Begin { Set-StrictMode -Version latest }

    Process {
        if ($psCmdlet.ParameterSetName -eq "Path")
        {
            # In the -Path (non-literal) case we may need to resolve a wildcarded path
            $resolvedPaths = @($Path | Resolve-Path | Convert-Path)
        }
        else 
        {
            # Must be -LiteralPath
            $resolvedPaths = @($LiteralPath | Convert-Path)
        }

        foreach ($rpath in $resolvedPaths) 
        {
            Write-Verbose "Processing $rpath"
            # ... Do something with the raw, resolved $rpath here ...
        }  
    }
}
于 2012-10-02T14:43:36.867 に答える
0

アイテムの処理中に ParseName が null を返すことに気付いたので、次のコードを使用できます。

function Create-Zip
{
    param([string]$zipfile)
         set-content $zipfile ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
         (dir $zipfile).IsReadOnly = $false 

    $shellApplication = new-object -comObject Shell.Application
    $zipPackage = $shellApplication.NameSpace($zipfile)

    foreach($item in $input)
    { 
             $zipPackage.CopyHere($item.FullName)
             do {
                $i = $zipPackage.ParseName($item.Name)
                Start-Sleep -milliseconds 10
            } while ($i -eq $null)
    }
}

ここでは、CPU を焼き尽くさないためだけにある Start-Sleep がなくても、問題なく動作します。

于 2012-10-02T17:58:13.487 に答える