In PowerShell I can pass a parameter if I create a closure with param syntax:
$hello = { param($name) "Hello $name"}
& $hello "World!"
>hello.ps1
Hello World!
When I try this with function syntax I get into trouble:
function hello($n) { { "Hello $n" }.GetNewClosure() }
$doit = hello
& $doit "World!"
>functionclosure.ps1
Hello
I managed to fix this by giving the parameter earlier:
function hello($n) { {"Hello $n"}.GetNewClosure() }
$doit = hello "World"
& $doit
>functionclosure2.ps1
Hello World
Is there a way to pass a parameter to a function from the & call operator line?
Is there a way to pass a parameter to a function from the & call operator line?
The call (also known as invocation or &) operator is generally used to execute content in a string, which we do when there is a long file path or a path with spaces in the name, or when we are dynamically building a string to execute.
Now, in this case, Using GetNewClosure() alters a function to instead return a scriptblock as the output type. That Scriptblock must be invoked using the Call operator, so this is a valid usage of the Call operator.
Back to your question then, yes, you can control the order of execution using paranthesis and pass a parameter to a function which returns a closure from the call line like this:
& (hello stephen)
However, this is pretty confusing in action as closures maintain their own separate scope and in more than ten years of enterprise automation projects, I never saw them used. It might be more confusion than it's worth to go down this route.
Prehaps a simpler approach might be:
function hello($name) { "WithinFunction: Hello $name"}
$do = 'hello "world"'
PS> $do
hello "world"
#invoke
PS> invoke-expression $do
WithinFunction: Hello world
Additional reading on closures by the man himself who implemented it in PowerShell here. https://devblogs.microsoft.com/scripting/closures-in-powershell/
Jeroen Mostert wrote this as a comment:
You've written a function that takes a parameter and then creates a closure using that parameter. It seems like you want a function that returns a closure that takes a parameter -- but that's the same as your first example, and
function hello { { param($n) "Hello $n"} }
would do that. You'd invoke that as
& (hello) "world"
On the other hand, if you want to have a function as a closure you can invoke,
${function:hello}
would do that, i.e.
function hello($n) { "Hello $n" };
$doit = ${function:hello};
& $doit "World"
Related
I am trying to create a function in Groovy that inputs a string and returns a modified string. The problem I believe is within an addon, which is a specific software environment I am working with i.e. logic.script.Microblock. The error message I am receiving is:
No signature of method: com.controlj.addonsupport.logic.script.Microblock.capAbbreviate() is applicable for argument types: (java.lang.String) values: [OAT Dewpoint bavo].
I have tried dispName = capAbbreviate(dispName.toString()), dispName = capAbbreviate(dispName), and capAbbreviate(dispName).
The software environment is using some sort of addon. I am still fairly new to Groovy/Java so this seems like it could be something simple but it's not clicking in my head just yet.
The code simplified below is:
def exceptions = ['Ac':'AC','Oat':'OAT','Vav':'VAV']
def exceptionNonAlpha = '(?=[^a-zA-Z])'
def dispName
def capAbbreviate(String mbText)
{
// Iterates through 'exceptions' map and finds abbreviations and recapitalizes them
for (hvacName in exceptions.keySet()) {
mbText = mbText.replaceAll(hvacName + exceptionNonAlpha, exceptions[hvacName])
}
return mbText
}
logic.microblocks
{
dispName = prop.'display name'
dispName = capAbbreviate(dispName.toString()) // also tried capAbbreviate(dispName)
println dispName
}
The solution has two parts:
Similar to what #AndrejIstomin mentioned, removing the def to make a list or variable global resolved one part of the issue
The second part to the solution is that this. needed to be used to call the method. i.e. this.capAbbreviate(dispName)
I'm actually scripting and a part of my script is a FTP file download. The code words without any problem, but I don't understand one specific part. Its the only part from the code I haven't written by myself. I understand how the code works, but I actually dont understand how the function return its value. You're may confused what I mean, so let me explain it with a bit of code:
function get-ftp {
try {
$ftprequest = [system.net.ftpwebrequest]::Create($uri)
$ftprequest.Credentials = New-Object system.net.networkcredential($user,$pass)
$ftprequest.Proxy = $null
$ftprequest.Method = [System.Net.WebRequestMethods+FTP]::ListDirectory
$ftpresponse = $ftprequest.GetResponse()
$reader = New-Object IO.StreamReader $ftpresponse.GetResponseStream()
$reader.ReadToEnd()
$reader.Close()
$ftpresponse.Close()
}
catch {
Write-host "Error while reading filenames"
}
}
So this is my function to get all directories from the FTP server. I call this function with this code:
$allXmlFiles = get-ftp
So after the call, my $allXmlFiles contains a string (tested with getType on $allXmlFiles) with all the filenames on the server. Now my question: How is the answer from the FTP passed to this variable? There's no return in the function, so I'm quite confused how this works. I tried so take the try/catch out of the function and access the answer directly, but that didin't work. I tried to find it in $reader and in $ftpresponse - no success.
It would be really cool if someone can explain me whats going on here. As said, the code works, but I would like to understand whats going on here.
It's
$reader.ReadToEnd()
StreamReader.ReadToEnd() method outputs string and since it's result is not assigned to variable it will be the function output.
Idiomatic way would be to write it like this:
Write-Output $reader.ReadToEnd()
In PowerShell, the result of every command / statement is returned as output if you don't assign or pipe it to anything.
The return keyword only exits the current scope. You rarely use return in PowerShell.
As beatcracker mentioned, $reader.ReadToEnd() is producing the output.
I have noticed that variables inside a module function do not remain in scope after execution returns to the script. I came across Export-ModuleMember but that didn't seem to help, perhaps i'm using it wrong.
FunctionLibrary.psm1
Function AuthorizeAPI
{
# does a bunch of things to find $access_token
$access_token = "aerh137heuar7fhes732"
}
Write-Host $access_token
aerh137heuar7fhes732
Export-ModuleMember -Variable access_token -Function AuthorizeAPI
Main script
Import-Module FunctionLibrary
AuthorizeAPI # call to module function to get access_token
Write-Host $access_token
# nothing returns
I know as an alternative I could just dot source a separate script and that would allow me to get the access_token but I like the idea of using modules and having all my functions therein. Is this doable? Thanks SO!
As per #PetSerAl's comment, you can change the scope of your variable. Read up on scopes here. script scope did not work for me when running from console; global did.
$global:access_token = "aerh137heuar7fhes732"
Alternatively, you can return the value form the function it and store in a variable; no scope change needed.
Function
Function AuthorizeAPI
{
# does a bunch of things to find $access_token
$access_token = "aerh137heuar7fhes732"
return $access_token
}
Main Script
Import-Module FunctionLibrary
$this_access_token = AuthorizeAPI # call to module function to get access_token
Write-Host $this_access_token
I'm trying to write a powershell function that receives a list of files from the get-content commandlet by piplining and processes them.
The pipeline looks like this:
get-content D:\filelist.txt | test-pipeline
For simplicity sake the function below should just show each line of the textfile.
function test-pipeline
{
<#
.Synopsis
#>
[CmdletBinding( SupportsShouldProcess=$true)]
Param([Parameter(Mandatory = $true,
ValueFromPipeLine = $true)]
[array]$filelist
)
foreach ($item in $filelist)
{
$item
}
}
My filelist is a normal .txt file and looks like this.
line 1
line 2
line 3
line 4
line 5
No matter what type of parameter I pipe to the function, it never works and shows only the last line of the text file in the $filelist variable. Can anybody help? Powershell Version is v2
Thanks in advance
The reason you see only your last line requires digging a bit into the nature of pipeline-able PowerShell functions. In the absence of explicit begin/process/end blocks that Swonkie alluded to, all code in the function operates as if it is in the end block, i.e. it is as if you wrote:
function test-pipeline
{
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeLine = $true)][array]$filelist
)
END {
$filelist
}
}
But $filelist, being a pipeline variable, has only the current value in it when fed pipeline data; the end block runs when the pipeline input is exhausted, thus $filelist contains just the last value. Simply changing that end block to a process block--where it runs for each value in the pipeline, will give you the desired output:
function test-pipeline
{
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeLine = $true)][array]$filelist
)
PROCESS {
$filelist
}
}
And notice that you do not need any kind of loop there--the pipeline is already providing the "loop".
That is just one of several ways to process pipeline data. Here's a close variant, which is a bit shorter: use filter instead of function, because filter with no explicit blocks operates as if all code is in the process block.
filter test-pipeline
{
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeLine = $true)][array]$filelist
)
$filelist
}
To delve further into the fascinating and arcane world of writing functions for pipelining, take a look at the in-depth analysis I wrote, Down the Rabbit Hole: A Study in PowerShell Pipelines, Functions, and Parameters, published on Simple-Talk.com. Enjoy your PowerShell adventures!
Instead of function try filter:
filter Test-Pipeline {
...
}
This is basically a function with a processing block (which you need to process pipeline objects). Alternatively you can write a function with this block and optional begin and end blocks:
function Test-Pipeline {
begin {
}
process {
...
}
end {
}
}
More info: http://technet.microsoft.com/en-us/magazine/hh413265.aspx
I am making an extension (A) for Chrome that communicates with another extension (B). I want A to provide the B a function, but it won't send. I can send strings just fine.
A has the following code. rect is the function in this code.
chrome.extension.onRequestExternal.addListener(
function(request, sender, sendResponse) {
obj = {}
obj.permisions = "all"
obj.rect = Rect
alert(obj.permisions+","+obj.rect)
sendResponse(obj);
});
...this code works just fine. The alert shows a box that says "all", then prints out the function.
B has the following code.
chrome.extension.sendRequest(ext[i].id, {}, function(lib) {
alert(lib.permisions+","+lib.rect)
});
The alert on this one shows "all,undefined". Can functions not be passed between extensions?
While you can certainly communicate between extensions, you can only pass valid JSON. Unfortunately, valid JSON only includes simple data types(String, Number, Boolean, Array, Object* or Null).
One way to do it would be to pass the function as a String and use eval on the receiving end. Could be unsafe, but is doable.
* While a function is technically an Object, in this context Object refers to name:value pairs of the aforementioned simple data types.