13

あなたへの1つの質問はここにあります;)

私はこの機能を持っています:

function Set-DbFile {
    param(
        [Parameter(ValueFromPipeline=$true)]
        [System.IO.FileInfo[]]
        $InputObject,
        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [scriptblock]
        $Properties
    )
    process {
        $InputObject | % { 
            Write-Host `nInside. Storing $_.Name
            $props = & $Properties
            Write-Host '  properties for the file are: ' -nonew
            write-Host ($props.GetEnumerator()| %{"{0}-{1}" -f $_.key,$_.Value})
        }
    }
}

を見てください$Properties。ファイルごとに評価してから、ファイルとプロパティをさらに処理する必要があります。

使用方法の例は次のとおりです。

Get-ChildItem c:\windows |
    ? { !$_.PsIsContainer } |
    Set-DbFile -prop { 
        Write-Host Creating properties for $_.FullName
        @{Name=$_.Name } # any other properties based on the file
    }

関数をコマンドラインにコピーして貼り付けSet-dbFile、サンプルスニペットを実行すると、すべて問題ありません。

ただし、関数をモジュールに保存し、インポートして例を実行すると、$_変数が空になります。理由を知っている人はいますか?そして、それを解決する方法は?(他のソリューションも同様に歓迎されます)


スクリプトで定義された/コマンドラインで入力された関数の結果:

Inside. Storing adsvw.ini
Creating properties for C:\windows\adsvw.ini
  properties for the file are: Name-adsvw.ini

Inside. Storing ARJ.PIF
Creating properties for C:\windows\ARJ.PIF
  properties for the file are: Name-ARJ.PIF
....

モジュールで定義された関数の結果:

Inside. Storing adsvw.ini
Creating properties for
  properties for the file are: Name-

Inside. Storing ARJ.PIF
Creating properties for
  properties for the file are: Name- 
....
4

3 に答える 3

6

ここでの問題は、スコープ階層にまで及びます。次のような2つの関数を定義すると...

function F1{
    $test="Hello"
    F2
}
function F2{
    $test
}

次に、F2 は F1 のスコープから呼び出されるため、F1 の変数スコープを継承します。モジュールで関数 F2 を定義し、関数をエクスポートする場合、モジュールには独自のスコープ ツリーがあるため、$test 変数は使用できません。Powershell 言語仕様(セクション 3.5.6)を参照してください。

あなたの場合、現在のノード変数はローカルスコープで定義されているため、(グローバル変数を除いて)異なるスコープルートを持つ別のツリーにあるため、モジュールスコープには残りません。

Powershell言語仕様(セクション 4.3.7)の GetNewClosure() メソッドのテキストを引用するには:

モジュールにバインドされているスクリプト ブロックを取得します。呼び出し元のコンテキストにあるすべてのローカル変数がモジュールにコピーされます。

...したがって、 GetNewClosure() は、ローカルスコープ/モジュールの分割を橋渡しするため、扱いやすく機能します。これが役立つことを願っています。

于 2011-06-23T10:17:19.017 に答える
5

GetNewClosure()回避策として優れているように見えますが、スクリプト ブロックがこれらの変数を認識する方法が変わります。$_引数としてスクリプトブロックに渡すことも機能します。

これは、通常のスコープの問題 (たとえば、グローバルかローカルか) とは関係ありませんが、最初はそのように見えます。これが私の非常に単純化された複製であり、いくつかの説明は次のとおりです。

script.ps1通常のドットソーシングの場合:

function test-script([scriptblock]$myscript){
    $message = "inside"
    &{write-host "`$message from $message"}    
    &$myscript
}

Module\MyTest\MyTest.psm1インポート用:

function test-module([scriptblock]$myscript){
    $message = "inside"
    &{write-host "`$message from $message"}    
    &$myscript
}

function test-module-with-closure([scriptblock]$myscript){
    $message = "inside"
    &{write-host "`$message from $message"}    
    &$myscript.getnewclosure()
}

呼び出しと出力:

» . .\script.ps1

» import-module mytest

» $message = "outside"

» $block = {write-host "`$message from $message (inside?)"}

» test-script $block
$message from inside
$message from inside (inside?)

» test-module $block
$message from inside
$message from outside (inside?)

» test-module-with-closure $block
$message from inside
$message from inside (inside?)

好奇心をそそられたので探してみたら、面白いものをいくつか見つけました。

このバグ レポートへのリンクも掲載されているこの Q&Aは、私が遭遇した他のブログ記事とほぼ同じトピックです。しかし、それはバグとして報告されましたが、私は同意しません。

about_Scopesページには次のように書かれています (w:

...

Restricting Without Scope

  A few Windows PowerShell concepts are similar to scope or interact with 
  scope. These concepts may be confused with scope or the behavior of scope.

  Sessions, modules, and nested prompts are self-contained environments,
  but they are not child scopes of the global scope in the session.

  ...

  Modules:
    ...

    The privacy of a module behaves like a scope, but adding a module
    to a session does not change the scope. And, the module does not have
    its own scope, although the scripts in the module, like all Windows
    PowerShell scripts, do have their own scope. 

今、私はその動作を理解していますが、上記といくつかの実験が私をそれに導いたのです:

  • $messageスクリプト ブロックを に変更すると、スクリプト ブロックのローカル スコープで定義されていない$local:messageため、3 つのテストすべてに空白ができます。$message
  • を使用する$global:messageと、3 つのテストすべてが出力されますoutside
  • を使用する$script:messageと、最初の 2 つのテストが出力さoutsideれ、最後のテストが出力されinsideます。

それから私もこれを読んだabout_Scopes

Numbered Scopes:
    You can refer to scopes by name or by a number that
    describes the relative position of one scope to another.
    Scope 0 represents the current, or local, scope. Scope 1
    indicates the immediate parent scope. Scope 2 indicates the
    parent of the parent scope, and so on. Numbered scopes
    are useful if you have created many recursive
    scopes.
  • 直接の親スコープから値を取得しようとするためにを使用$((get-variable -name message -scope 1).value)すると、どうなりますか? outsideではなく、まだ得られinsideます。

この時点で、少なくともスクリプト ブロックについては、セッションとモジュールが独自の宣言スコープまたはある種のコンテキストを持っていることは十分に明らかでした。スクリプト ブロックは、それらが呼び出されるまで、宣言された環境で無名関数のように動作します。GetNewClosure()その時点で、GetNewClosure()呼び出されたスコープ内の同じ名前の変数のコピーを内部化します (最初にローカルを使用して、グローバル)。簡単なデモンストレーション:

$message = 'first message'
$sb = {write-host $message}
&$sb
#output: first message
$message = 'second message'
&$sb
#output: second message
$sb = $sb.getnewclosure()
$message = 'third message'
&$sb
#output: second message

これが役立つことを願っています。

追記:デザインについて。

JasonMArcher のコメントは、scriptblock がモジュールに渡される設計上の問題について考えさせられました。質問のコードでは、GetNewClosure()回避策を使用する場合でも、スクリプトブロックが機能するために実行される変数の名前を知っている必要があります。

一方、スクリプト ブロックにパラメーターを使用$_し、引数として渡した場合、スクリプト ブロックは変数名を知る必要はなく、特定の型の引数が渡されることだけを知る必要があります。したがって、モジュールは$props = & $Properties $_の代わりに使用$props = & $Properties.GetNewClosure()し、スクリプトブロックは次のようになります。

{ (param [System.IO.FileInfo]$fileinfo)
    Write-Host Creating properties for $fileinfo.FullName
    @{Name=$fileinfo.Name } # any other properties based on the file
}

詳細については、CosmosKey の回答を参照してください。

于 2011-06-18T03:09:17.450 に答える
1

実行する前に、そのスクリプト ブロックで getnewclosure() を呼び出す必要があると思います。スクリプト ファイルまたはモジュールから呼び出されるスクリプト ブロックは、コンパイル時に評価されます。コンソールから作業する場合、「コンパイル時間」はありません。実行時に評価されるため、モジュール内にある場合とは異なる動作をします。

于 2011-06-07T11:36:41.053 に答える