Is there a better approach for passing information into script blocks in where-object
filter scripts than using parent-scoped variables?
Background:
I have a script that looks for un-checked-in and/or modified source files vs. source control and has a parameter that allows it to do a more exhaustive search. I use where-object
in a couple of places with a script block object contained in a script-scoped variable that I customize based on the input parameters to the script.
So, if you ask for a thorough search, the filter will compare the candidate file against all TFS files to see if the file isn't in source control, if you choose the less-thorough search, the filter will only compare against checked-out files to see if the file is modified but not checked out.
The customized script blocks refer to script-scoped variables containing the results of doing queries against source control.
So my problem is that I'd like to get rid of a global (script-level) variable and pass all the necessary information into the script blocks as parameters to the script blocks. If I was using invoke-command
, I'd use the ArgumentList
parameter to do this. Where-Object
doesn't seem to have that. One downside of using parent-scoped variable references in the script blocks is that I can't change those variables, so I can't do lazy initialization (or at least I haven't figured out how yet, not being an expert on the scoping rules for Powershell.)
The Where-Object cmdlet selects objects that have particular property values from the collection of objects that are passed to it. For example, you can use the Where-Object cmdlet to select files that were created after a certain date, events with a particular ID, or computers that use a particular version of Windows.
You can run scripts with parameters in any context by simply specifying them while running the PowerShell executable like powershell.exe -Parameter 'Foo' -Parameter2 'Bar' . Once you open cmd.exe, you can execute a PowerShell script like below.
$_ is an alias for automatic variable $PSItem (introduced in PowerShell V3. 0; Usage information found here) which represents the current item from the pipe. PowerShell (v6. 0) online documentation for automatic variables is here.
'?' is an alias to the Where-Object cmdlet. Where-Object takes a scriptblock (e.g '{...}') and evaluates its code. If the code evaluates to $true, the current object is written to the pipeline and is available to the next command in chain, otherwise ($false) the object is discarded.
Just to expand a bit on what Keith mentioned, you could do it like this:
ps> $x=2
ps> 1..5 | where (& {param($v); { $_ -eq $v }.getnewclosure() } $x )
2
I tried closing over implict $args
to save the param declaration but $args
seems exempt from capture. More likely it is being captured but just getting stomped on.
$x
could easily be replaced with another function call like (get-x)
.
Essentially I'm calling a scriptblock that returns a scriptblock which closes over the outer scriptblocks parameter. Same implementation as Keiths essentially, just a little more succint [and obtuse.] Lambdas for the win.
I only wish there was a more pithy way to get closure semantics. That said, I'm glad the method got put in rather than not.
-Oisin
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With