Что такое PowerShell ScriptBlock

PowerShell ScriptBlock не является лексическим закрытием, поскольку он не закрывается над переменными, на которые ссылается в среде объявления. Вместо этого он, как представляется, использует динамическую область и свободные переменные, которые связаны во время выполнения в выражении лямбда.

function Get-Block {
  $b = "PowerShell"
  $value = {"Hello $b"}
  return $value
}
$block = Get-Block
& $block
# Hello
# PowerShell is not written as it is not defined in the scope
# in which the block was executed.


function foo {
  $value = 5
  function bar {
    return $value
  }
  return bar
}
foo
# 5
# 5 is written $value existed during the evaluation of the bar function
# it is my understanding that a function is a named scriptblock
#  which is also registered to function:

Вызов GetNewClosure() в ScriptBlock возвращает новый ScriptBlock, который закрывается над указанными переменными. Но это очень ограничено по объему и возможностям.

Что такое классификация ScriptBlock?

Ответ 1

Per docs, scriptblock - это "предварительно скомпилированный блок script text". Таким образом, по умолчанию вы просто предварительно разобранный блок script, не более, не менее. Выполнение этого создает дочернюю область, но помимо этого, как будто вы вставили код в строку. Таким образом, наиболее подходящий термин будет просто "исходным кодом только для чтения".

Вызов GetNewClosure болтов на динамически сгенерированном модуле, который в основном переносит моментальный снимок всех переменных в области вызывающего абонента во время вызова GetNewClosure. Это не реальное закрытие, просто копия переменных моментальных копий. Сам скриптовый блок по-прежнему остается только исходным кодом, а привязка переменных не происходит до тех пор, пока он не будет вызван. Вы можете добавлять/удалять/редактировать переменные в прикрепленном модуле по своему усмотрению.

function GetSB
{
   $funcVar = 'initial copy'

   {"FuncVar is $funcVar"}.GetNewClosure()

   $funcVar = 'updated value'  # no effect, snapshot is taken when GetNewClosure is called
}

$sb = GetSB

& $sb  # FuncVar is initial copy

$funcVar = 'outside'
& $sb  # FuncVar is initial copy

$sb.Module.SessionState.PSVariable.Remove('funcVar')
& $sb  # FuncVar is outside

Ответ 2

Блок сценариев PowerShell ScriptBlock эквивалентен first-class, анонимная функция. Большая часть замешательства, которую я видел, не с ScriptBlocks, а с функцией.

  • PowerShell поддерживает функцию закрывает, однако ключевое слово функции не поддерживает.

Примеры

Функции:

PS> function Hello {
>>      param ([string] $thing)
>>      
>>      return ("Hello " + $thing)
>>  }

PS> Hello "World"
"Hello World"

ScriptBlock:

PS> $HelloSB = {
>>      param ([string] $thing)
>>      
>>      return ("Hello " + $thing)
>>  }

PS> & $HelloSB "World"
"Hello World"

PS> $HelloRef = $HelloSB
PS> & $HelloRef "Universe"
"Hello Universe"

Закрытие:

PS> $Greeter = {
>>      param ([string] $Greeting)
>>      
>>      return ( {
>>          param ([string] $thing)
>>          
>>          return ($Greeting + " " + $thing)
>>      }.GetNewClosure() )
>>  }

PS> $Ahoy = (& $Greeter "Ahoy")
PS> & $Ahoy "World"
"Ahoy World"

PS> $Hola = (& $Greeter "Hola")
PS> & $Hola "Mundo"
"Hola Mundo"

Хотя вы можете обойти ограничение ключевого слова function с помощью командлета Set-Item:

PS> function Greeter = { ... }  # ✕ Error
PS> function Greeter { ... }.GetNewClosure()  # ✕ Error

PS> Set-Item -Path "Function:Greeter" -Value $Greeter  # (defined above)  ✓ OK
PS> $Hola = Greeter "Hola"
PS> & $Hola "Mundo"
"Hola Mundo"

Параметр Value командлета Set-Item может быть любым ScriptBlock, даже одним из возвращаемых другой функцией. (Например, функция "Greeter" возвращает замыкание, как показано выше).

PS> Set-Item -Path "Function:Aloha" -Value (Greeter "Aloha")
PS> Aloha "World"
"Aloha World"

Два других важных момента:

  • PowerShell использует динамическое масштабирование, а не лексическое охват.

    Лексическое замыкание закрывается в среде исходного кода, тогда как динамическое закрытие закрывается на основе активной/динамической среды, которая существует при вызове GetNewClosure(). (Что более подходит для языка сценариев.)

  • В PowerShell могут быть операторы "функции" и "возврат", но на самом деле его ввод/вывод основан на потоках и трубопроводах. Все, что выписано из ScriptBlock с командлетом "Write-Output" или "write", будет возвращено.