3

I have written the following code:

cls
function GetFoo() { 
    function GetBar() {
        $bar = "bar"
        $bar
    }

    $foo = "foo"
    $bar = GetBar
    $foo
    $bar
}


$cred = Get-Credential "firmwide\srabhi_adm"
$result = Invoke-Command -Credential $cred -ComputerName localhost 
-ScriptBlock ${function:GetFoo}
Write-Host $result[0]
Write-Host $result[1]

It works but I don't want to define GetBar inside of GetFoo.

Can I do something like this?

cls
function GetBar() {
    $bar = "bar"
    $bar
}

function GetFoo() {     
    $foo = "foo"
    $bar = GetBar
    $foo
    $bar
}


$cred = Get-Credential "firmwide\srabhi_adm"
$result = Invoke-Command -Credential $cred -ComputerName localhost 
-ScriptBlock ${function:GetFoo; function:GetBar; call GetFoo}
Write-Host $result[0]
Write-Host $result[1]

Basically I am selectively putting the functions which I want in the ScriptBlock and then calling one of them. This way I don't have to define function inside of function and I can construct the ScriptBlock by injecting the functions which I want to be a part of that ScriptBlock.

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
Knows Not Much
  • 30,395
  • 60
  • 197
  • 373

5 Answers5

2

The problem is that Invoke-Command can only see what's inside ScriptBlock, it can't see functions definded outside. If you really want to - you can run everything in one line, like this:

$result = Invoke-Command  -ComputerName localhost  -ScriptBlock { function GetBar() { $bar = "bar"; $bar }; function GetFoo() { $foo = "foo"; $bar = GetBar; $foo;  $bar }; GetFoo }

But I personally would advise you to save functions in script and call Invoke-Command with -FilePath parameter, like this:

$result = Invoke-Command  -ComputerName localhost  -FilePath "\1.ps1"
Andrey Marchuk
  • 13,301
  • 2
  • 36
  • 52
2

I recently got this working:

function GetBar() {
    $bar = "bar"
    $bar
}

function GetFoo() {     
    $foo = "foo"
    $bar = GetBar
    $foo
    $bar
}

$NewScriptBlock = [scriptblock]::Create("function GetBar() { ${function:GetBar} } function GetFoo() { ${function:GetFoo} } GetFoo")
Invoke-Command -ComputerName localhost -ScriptBlock $NewScriptBlock

Alternatively you can spare a line using:

Invoke-Command -ComputerName localhost -ScriptBlock ([scriptblock]::Create("function GetBar() { ${function:GetBar} } function GetFoo() { ${function:GetFoo} } GetFoo"))

The trick is that the Create method of the ScriptBlock class adds the code inside the functions, but not the header and final bracket. You need to add that yourself, plus the final invoke.

1

I know this is an old thread, but I hope it helps someone.

[CmdletBinding()]
param()
$barScriptBlock = {
    function GetBar() {
        $bar = "bar"
        $bar
    }
}
$fooScriptBlock = {
    function GetFoo() {     
        $foo = "foo"
        $bar = GetBar
        $foo
        $bar
    }
}
# Concatenate all functions into a script block; add a parameter for extra credit.
$scriptBlockString = @"
param(`$hello)
$($barScriptBlock.ToString())
$($fooScriptBlock.ToString())
`$hello
GetFoo
"@
Write-Verbose $scriptBlockString
# Convert combined string to script block.
$finalScriptBlock = [ScriptBlock]::Create($scriptBlockString)
# Run synchronously, passing runtime parameter.
$result = & $finalScriptBlock "Hola"
# (Async results would not be reiably accessed here yet.)
Write-Host $result[0]
Write-Host $result[1]
Write-Host $result[2]
Joe Zamora
  • 186
  • 1
  • 4
0

Building off of Joe Zamora's answer, here's a command that I think fits the question better:

$result = Invoke-Command -Credential $cred -ScriptBlock ([ScriptBlock]::Create("${function:GetFoo}; ${function:GetBar}; GetFoo"))
0

This is how I do it:

#get my script path
$this_Script = $MyInvocation.MyCommand.path

#get get all my location functions and filters
$this_functions = Get-Item FUNCTION:\* | Where-Object { $_.ScriptBlock.File -eq $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath( $this_Script  ) }

$mything | ForeachObject -Parallel {
    
    $this_functions = $using:this_functions


    $this_functions | Foreach-Object {  
        #create function scriptblocks
        $sb = "$($_.CommandType) $($_.name) {$($_.ScriptBlock) }" ; 
    
        Invoke-Expression $sb ;
    }
}

The above only works in in Pwsh Core.

murfman
  • 11
  • 1