2013-05-20 5 views
5

Короткий вопрос: у кого есть подробная информация о параметре -RemainingScripts параметра ForEach-Object?PowerShell: таинственный параметр -RemainingScripts параметра ForEach-Object

Длинный вопрос:

Я только начал изучать PowerShell с прошлой недели, и я иду через каждый командлетов, чтобы узнать более подробную информацию. На основании официальной документации, мы знаем, ForEach-Object может иметь Begin-Process-End блоков, как это:

Get-ChildItem | foreach -Begin { "block1"; 
    $fileCount = $directoryCount = 0} -Process { "block2"; 
    if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}} -End { 
    "block3"; "$directoryCount directories and $fileCount files"} 

Результат ожидается: 1 раз для "block1" и "block3", "block2" повторяется каждый элемент передан, и счетчик count/fil count - все правильно. Все идет нормально.

Теперь, что интересно, следующая команда также работает и дает точно такой же результат:

Get-ChildItem | foreach { "block1" 
    $fileCount = $directoryCount = 0}{ "block2"; 
    if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}}{ 
    "block3"; "$directoryCount directories and $fileCount files"} 

Just 3 ScriptBlocks переданного Foreach. Основываясь на руководстве, первый идет на -Process (Позиция 1). Но как насчет оставшихся 2? Согласно руководству, параметр «Позиция 2» отсутствует. Поэтому я обратился к Trace-Command и обнаружил, что более поздние 2 блока скриптов фактически были RemainingScripts как «IList с 2 элементами».

BIND arg [$fileCount = $directoryCount = 0] to parameter [Process] 
BIND arg [System.Management.Automation.ScriptBlock[]] to param [Process] SUCCESSFUL 
BIND arg [System.Collections.ArrayList] to parameter [RemainingScripts] 
BIND arg [System.Management.Automation.ScriptBlock[]] to param [RemainingScripts] SUCCESSFUL 

Так что если я изменить команду к следующему:

# No difference with/without the comma "," between the last 2 blocks 
Get-ChildItem | foreach -Process { "block1" 
    $fileCount = $directoryCount = 0} -RemainingScripts { "block2"; 
    if ($_.PsIsContainer) {$directoryCount++} else {$fileCount++}},{ 
    "block3"; "$directoryCount directories and $fileCount files"} 

Тем не менее, точно такой же результат.

Итак, как вы заметили, все три команды дают одинаковый результат. Это вызывает интересный вопрос: обе из последних двух команд (неявно) указаны -Process, однако ForEach-Object неожиданно заканчивается использованием аргумента -Process как «-Begin»! (блок сценария выполняется один раз в начале).

Этот эксперимент предполагает:

параметра
  1. -RemainingScripts будет принимать все несвязанный ScriptBlocks
  2. Когда 3 блока передается в, хотя первый один идет в -Process, позже она фактически используется как «Начало» в то время как оставшиеся 2 становятся «Процессом» и «Конец»

Все еще, все это просто мое дикое предположение. Я не нашел документацию для поддержки своей догадки

Итак, наконец, мы вернемся к моему короткому вопросу :) У кого-нибудь есть подробная информация о параметре -RemainingScripts параметра ForEach-Object?

Спасибо.

ответ

3

Я больше исследований, и теперь чувствую себя уверенно, чтобы ответить на поведение параметра -RemainingScripts, когда несколько ScriptBlocks передаются в.

Если вы запустите следующие команды и проверьте результат тщательно, вы найдете шаблон. Это не совсем понятно, но все же не сложно понять.

1..5 | foreach { "process block" } { "remain block" } 
1..5 | foreach { "remain block" } -Process { "process block" } 
1..5 | foreach { "remain block" } -End { "end block" } -Process { "process block" } -Begin { "begin block" } 
1..5 | foreach { "remain block 1" } -End { "end block" } -Process { "process block" } { "remain block 2" } 
1..5 | foreach { "remain block 1" } { "remain block 2" } -Process { "process block" } -Begin { "begin block" } 
1..5 | foreach { "remain block 1" } { "remain block 2" } -Process { "process block" } { "remain block 3" } 
1..5 | foreach { "process block" } { "remain block 1" } { "remain block 2" } -Begin { "begin block" } 
1..5 | foreach { "process block" } { "remain block 1" } { "remain block 2" } { "remain block 3" } 

Итак, что такое шаблон здесь?

  • Когда одинока ScriptBlock прошел в: легкий, он просто идет в -Process (наиболее общеупотребительным)

  • Когда ровно 2 ScriptBlocks передаются в, есть 3 возможных комбинаций

    1. -Process & -Begin -> выполнить, как указано
    2. -Process & -End -> выполнить, как указано
    3. -Process & -RemainingScripts -> Процесс становится началом, в то время как RemainingScripts становится процесс

Если мы запустим эти 2 заявления:

1..5 | foreach { "process block" } { "remain block" } 
1..5 | foreach { "remain block" } -Process { "process block" } 

# Both of them will return: 
process block 
remain block 
remain block 
remain block 
remain block 
remain block 

Как вы узнаете, что это просто особый в случае следующего испытательного случая:

  • Когда передано более 2 ScriptBlocks, следуйте инструкциям этот рабочий процесс:

    1. Привязать все скриптовые блоки как указано (начало, процесс, конец); оставшиеся ScriptBlocks переходят к RemainingScripts
    2. Закажите все сценарии как: Начало> Процесс> Оставшиеся> Конец
    3. Результат заказа - это коллекция ScriptBlocks. Давайте называть эту совокупность OrderedScriptBlocks

      • Если Begin/End не связаны, просто игнорировать
    4. (внутренне) параметры Re-связывания на основе OrderedScriptBlocks

      • OrderedScriptBlocks [ 0] Старт
      • OrderedScriptBlocks [1 ..-2] стал процесс
      • OrderedScriptBlocks [-1] (последняя) становится End

Давайте возьмем этот пример

1..5 | foreach { "remain block 1" } { "remain block 2" } -Process { "process block" } { "remain block 3" } 

результат Заказ:

{ "process block" } # new Begin 
{ "remain block 1" } # new Process 
{ "remain block 2" } # new Process 
{ "remain block 3" } # new End 

Теперь результат выполнения вполне предсказуем:

process block 
remain block 1 
remain block 2 
remain block 1 
remain block 2 
remain block 1 
remain block 2 
remain block 1 
remain block 2 
remain block 1 
remain block 2 
remain block 3 

Это секрет -RemainingScripts и теперь мы понимаем, более внутреннее поведение ForEach-Object!

По-прежнему я должен признать, что нет документации, подтверждающей мое предположение (не моя вина!), Но этих тестовых случаев должно быть достаточно, чтобы объяснить поведение, которое я описал.

1

вот подробнее. ValueFromRemainingArguments установлено в true, поэтому ваша догадка верна.

help ForEach-Object 

-RemainingScripts <ScriptBlock[]> 
    Takes all script blocks that are not taken by the Process parameter. 

    This parameter is introduced in Windows PowerShell 3.0. 

gcm ForEach-Object | select -exp parametersets 

Parameter Name: RemainingScripts 
    ParameterType = System.Management.Automation.ScriptBlock[] 
    Position = -2147483648 
    IsMandatory = False 
    IsDynamic = False 
    HelpMessage = 
    ValueFromPipeline = False 
    ValueFromPipelineByPropertyName = False 
    ValueFromRemainingArguments = True 
    Aliases = {} 
    Attributes = 
    System.Management.Automation.ParameterAttribute 
    System.Management.Automation.AllowEmptyCollectionAttribute 
    System.Management.Automation.AllowNullAttribute 
+0

Спасибо, Энди, вы ответили на пункт №1: «Параметр -RemainingScripts будет принимать все несвязанные ScriptBlocks», но не объяснил точку №2 «Когда передано 3 блока, хотя первый идет на -Process ... «на самом деле это главная причина, по которой задан этот вопрос. Я сделал больше исследований, и теперь я думаю, что могу ответить #point 2. Тем не менее, я ценю ваш ответ и технику для проверки параметра. И жаль, что у меня нет разрешения голосовать за ваш ответ, хотя это очень полезно. –

Смежные вопросы