Anonymous Functions in PowerShell


A function is a collection of program statements bound to a function name. Then, when the function name is invoked, the statements are run with any provided input.


In principle, anonymous functions are functions without names. Here, the statements are used immediately. For example, this can be useful for something like a mouse click where a particular action is associated with the mouse click. The same action is not used elsewhere. And a function name is not necessary.


Background


I maintain a list of bookmarks for my file system. This is a hash table of strings.


$bookmark = @{
  journal    = "/data/Michael/Journal"
  powershell = "/data/Michael/PowerShell"
}


We can use these strings as input for the Set-Location cmdlet. Issuing the command below will change the current directory.


Set-Location -Path $bookmark.powershell


Editor's Note: Since I'm mixing commands and output below, I include the prompt symbol > before the commands I typed. If you copy these commands, omit the leading >. ^C indicates where I pressed Ctrl-C which normally stops execution. Here, I use Ctrl-C to skip lines with comments.


One problem with my process is that if I mistype the key name for the hash table, the command fails silently. So, I am in the habit of checking where I am.


> # Mistyped name. ^C
> Set-Location -Path $bookmark.powershel
> Get-Location

Path
----
/home/michael

> # Correct hash table key name. ^C
> Set-Location -Path $bookmark.powershell
> Get-Location

Path
----
/data/Michael/PowerShell


This process could be improved in two ways:



For the first point, I would prefer to use only the hash table and key without any proceeding cmdlet.


> # Example ^C
> $bookmark.journal

> Get-Location

Path
----
/data/Michael/Journal


I would need to store anonymous functions in the hash table values for this to work.


Do anonymous functions exist in PowerShell?


No.


PowerShell can store code in script blocks. Script blocks are objects.


Specifying an object on the command line outputs a value:


> $a = Get-ChildItem
> $a

   Directory: /data/Michael

UnixMode   User    Group     LastWriteTime  Size Name
--------   ----    -----     -------------  ---- ----
drwxr-xr-x michael michael  9/1/2022 05:02  4096 Journal
drwxr-xr-x michael michael  9/1/2022 15:33  4096 PowerShell


In the case of script blocks, specifying a script block on the command line prints the commands without executing the code.


Here, I use semicolons ; to explicitly terminate my statements.


> $b = { Set-Location ~ ; Get-Location ; }
> $b
 Set-Location ~ ; Get-Location ;
> # Literal string output. ^C


Running Script Blocks


The closest you can get to an anonymous function in PowerShell is to execute the contents of a script block.


Use the Call Operator


You can use the call operator &


> $c = { Set-Location ~ ; Get-Location ; }
> & $c

Path
----
/home/michael


Note that since I intend to use a hash table later, I use the variable $c in the example above. But the variable is not required. You can invoke script blocks directly.


> Get-Random
866754637

> { Get-Random }
 Get-Random 

> & { Get-Random }
1481025786


Use the Invoke() Method


Use the Invoke() method on the script block:


> $d = { Get-ChildItem ; }
> $d.Invoke()

   Directory: /home/michael

UnixMode   User    Group      LastWriteTime Size Name
--------   ----    -----      ------------- ---- ----
drwxr-xr-x michael michael  8/30/2022 05:26 4096 Desktop
drwxr-xr-x michael michael 12/23/2021 07:14 4096 Documents
drwxr-xr-x michael michael  8/29/2022 17:32 4096 Downloads


Investigate Script Blocks with Get-Member


You can use Get-Member to discover the Invoke() method if you forget.


The Invoke method name will appear under the Name column. And the MemberType will be Method.


> $e = { Get-Process ; }
> $e | Get-Member

   TypeName: System.Management.Automation.ScriptBlock

Name                    MemberType Definition
----                    ---------- ----------
CheckRestrictedLanguage Method     void CheckRestrictedLanguage(System.Collections.…
Equals                  Method     bool Equals(System.Object obj)
...
Invoke                  Method     System.Collections.ObjectModel.Collection[psobje…


Remember to include the trailing parentheses () on method calls.


> # This returns the method definition. ^C
> $e.Invoke

OverloadDefinitions
-------------------
System.Collections.ObjectModel.Collection[psobject] Invoke(Params System.Object[] args)


> # This invokes the code. ^C
> $e.Invoke()

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
      0     0.00      22.46       3.10  764292 …22 vim
      0     0.00      21.59       4.51  860098 …30 vim
      0     0.00     316.77     341.40  838982 …54 vivaldi-bin --enable-crashpad
      0     0.00      36.67       0.00  859799 …54 vivaldi-bin --type=broker


Updated Bookmarks


After researching script blocks in PowerShell, I updated my bookmark hash table with script blocks { ... }


$bookmark = @{
  journal    = { Set-Location -Path "/data/Michael/Journal" -PassThru }
  powershell = { Set-Location -Path "/data/Michael/PowerShell" -PassThru }
}


Now, I can change directory by placing the call operator & before the hash table. The -PassThru parameter outputs the current location; now I know if my command succeeds. And, if I mistype a key name, I receive an error.


PassThru is a parameter of Set-Location.


> $bookmark = @{
>>   journal    = { Set-Location -Path "/data/Michael/Journal" -PassThru }
>>   powershell = { Set-Location -Path "/data/Michael/PowerShell" -PassThru }
>> }

> & $bookmark.powershel
InvalidOperation: The expression after '&' in a pipeline element produced an
object that was not valid. It must result in a command name, a script block, or
a CommandInfo object.

> & $bookmark.powershell

Path
----
/data/Michael/PowerShell


References


Hash Tables | PowerShell | Microsoft Docs

Strings | C# | Microsoft Docs

Set-Location | PowerShell | Microsoft Docs

Script Blocks | PowerShell | Microsoft Docs

Get-Member | PowerShell | Microsoft Docs

Running Executables | PowerShell | TechNet Wiki

Appending Text on Multiple Lines | Vim Help


Created: Thursday, September 1, 2022

Updated: Thursday, September 1, 2022




/gemlog/