Check if parameter is missed - function

I have this code in my $profile in PS version 5:
function af_ {
Get-ChildItem function: | findstr.exe $args
if (! $args) {
return "nothing"
}
}
Calling e.g.
af_ tgit
Return:
Function tgit 0.7.3 posh-git
Calling
af_
Output:
FINDSTR: Syntaxfehler
nothing
Two questions:
How can I check if $args is not empty without "Syntaxfehler" (this is german..)?
Can I improve my idea to get the defnition of a custom function, as would be
declare -f $function
in Bash. It shows the definition which seems not possible in PS. I have to process the function: device and then look for the "Definition" output.

You get the syntax error because you call findstr.exe with the empty argument before checking it. Reversing the order should do it:
function af_ {
if (! $args) {
return "nothing"
}
Get-ChildItem function: | findstr.exe $args
}
As for your second question you can use Get-Command to get the command and it's definition:
(Get-Command af_).Definition

Related

How to call a Scriptblock as a function parameter in Powershell

I would like to have a function to run different ScriptBlocks. So, I need to use my Scriptblock as the parameter of the function. It does not work.
For example. This function returns the ScriptBlock as a string.
function Run_Scriptblock($SB) {
return $SB
}
These are the outputs from my tries:
# 1st try
Run_Scriptblock {systeminfo}
>> systeminfo
# 2nd try
Run_Scriptblock systeminfo
>> systeminfo
# 3rd try
Run_Scriptblock [scriptblock]systeminfo
>> [scriptblock]systeminfo
# 4th try
$Command = [scriptblock]{systeminfo}
Run_Scriptblock $Command
>> [scriptblock]systeminfo
# 5th try
[scriptblock]$Command = {systeminfo}
Run_Scriptblock $Command
>> systeminfo
If you want a function to run a scriptblock, you need to actually invoke or call that scriptblock, i.e.
function Run_Scriptblock($SB) {
$SB.Invoke()
}
or
function Run_Scriptblock($SB) {
& $SB
}
Otherwise the function will just return the scriptblock definition in string form. The return keyword is not needed, since PowerShell functions return all non-captured output by default.
The function would be called like this:
Run_Scriptblock {systeminfo}
As a side note, I would recommend you consider naming your function following PowerShell conventions (<Verb>-<Noun> with an approved verb), e.g.
function Invoke-Scriptblock($SB) {
...
}

Passing string in and back out of function

I am really new to Powershell but I have programmed in Java and other languages.
I am trying to pass a string to a function and then have that function return the string. Below is the simple code I am trying to run:
#funcpass.ps1
function test {
Param([string]$input)
$out = $input
return $out
}
$a = test hello world
Write-Host $a
I expect this to pass the string hello world then return the string into the variable $a to be printed. Instead my console returns this:
PS P:\test> .\funcpass.ps1
PS P:\test>
Is there some kind of scope error that I am encountering? Any help would be greatly appreciated. I am not sure if the version number helps, but here it is:
PS P:\test> $PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 1198
The $input parameter is reserved, so you have to change its name. Moreover, if you want to pass only one string to the function you have to enclose it with quotes:
function Run-Test {
Param([string]$inputValue)
$out = $inputValue
return $out
}
$a = Run-Test "hello world"
Write-Host $a
FYI the return keyword is optional but it makes your intentions more clear as other language use return to indicate that something is being returned from the function. Everything sent on the pipeline inside the function (like Write-Output) will be returned.

Calling a Function From Another Function in PowerShell

First time in PowerShell 5 and I'm having trouble calling a function that writes messages to a file from another function. The following is a simplified version of what I'm doing.
workflow test {
function logMessage {
param([string] $Msg)
Write-Output $Msg
}
function RemoveMachineFromCollection{
param([string]$Collection, [string]$Machine)
# If there's an error
LogMessage "Error Removing Machine"
# If all is good
LogMessage "successfully remove machine"
}
$Collections = DatabaseQuery1
foreach -parallel($coll in $Collections) {
logMessage "operating on $coll collection"
$Machines = DatabaseQuery2
foreach($Mach in $Machines) {
logMessage "Removing $Mach from $coll"
RemoveMachineFromCollection -Collection $coll -Machine $Mach
}
}
}
test
Here's the error it generates:
The term 'logMessage' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
+ CategoryInfo : ObjectNotFound: (logMessage:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : [localhost]
I've tried moving the logMessage function around in the file and even tried Global scope.
In any other language I would be able to call logMessage from any other function. As that's the purpose of a function.
What's the "Workflow way" of reusing a block of code?
Do I need to create some logging module that gets loaded into the Workflow?
You could move the functions and function call to an InlineScript (PowerShell ScriptBlock) inside the workflow like below.
workflow test {
InlineScript
{
function func1{
Write-Output "Func 1"
logMessage
}
function logMessage{
Write-Output "logMessage"
}
func1
}
}
Would Output:
Func 1
logMessage
As #JeffZeitlin mentioned in his answer, workflows are not PowerShell and are much more restrictive. The InlineScript block allows for normal PowerShell code to be interpreted however the scope will be tied to the InlineScript block. For instance, if you define the functions in the script block then attempt to call the func1 function outside of the InlineScript block (but still within the workflow) it will fail because it is out of scope.
The same would happen if you define the two functions either outside of the workflow or inside of the workflow but not in an InlineScript block.
Now for an example of how you can apply this to running a foreach -parallel loop.
workflow test {
## workflow parameter
param($MyList)
## parallel foreach loop on workflow parameter
foreach -parallel ($Item in $MyList)
{
## inlinescript
inlinescript
{
## function func1 declaration
function func1{
param($MyItem)
Write-Output ('Func 1, MyItem {0}' -f $MyItem)
logMessage $MyItem
}
## function logMessage declaration
function logMessage{
param($MyItem)
Write-Output ('logMessage, MyItem: {0}' -f $MyItem)
}
## func1 call with $Using:Item statement
## $Using: prefix allows us to call items that are in the workflow scope but not in the inlinescript scope.
func1 $Using:Item
}
}
}
Example call to this workflow would look like this
PS> $MyList = 1,2,3
PS> test $MyList
Func 1, MyItem 3
Func 1, MyItem 1
Func 1, MyItem 2
logMessage, MyItem: 3
logMessage, MyItem: 2
logMessage, MyItem: 1
You will notice (and as expected) the output order is random since it was run in parallel.
Powershell requires that functions be defined before use ('lexical scope'). In your example, you are calling the logMessage function before you have defined it.
You have also structured your example as a Powershell workflow. Workflows have some restrictions that ordinary scripts do not; you need to be aware of those differences. I did this search to find some descriptions and discussions of the differences; the first "hit" provides good information. I have not (yet) found anything saying whether functions can be defined in workflows, but I would be very wary of defining functions within functions (or workflows) in the first place.
Your logMessage function is not visible from within func1 function. It's valid even though logMessage function is declared above func1 one.
For this simple case, you could use nested functions as follows:
workflow test {
function func1 {
function logMessage {
Write-Output "logMessage"
}
Write-Output "Func 1"
logMessage
}
func1
}
test
Output:
PS D:\PShell> D:\PShell\SO\41770877.ps1
Func 1
logMessage

Create a function with optional call variables

Is there a way to create a parameter in a PowerShell function where you have to call it in order to have it considered?
An example given by commandlet (the bold being what I want to do):
Invoke-Command -computername Server01 -Scriptblock {...}
Here is an example of what I want to do with the function
Function DoStuff($computername, -arg2, -domain $domain)
Test-parameter(-domain) if (-domain -eq $true) {
use $domain
}
Else {
$domain = "Domain1"
}
test-parameter($arg2) {
if ($arg2 -eq $true) {
Do something
}
else {
Do the opposite
}
}
So in summary:
If "-arg2" is present, I want something to happen in the script. If "-Domain" is present and has an argument with it, I want that to be used rather then the set argument.
Powershell provides a lot of built-in support for common parameter scenarios, including mandatory parameters, optional parameters, "switch" (aka flag) parameters, and "parameter sets."
By default, all parameters are optional. The most basic approach is to simply check each one for $null, then implement whatever logic you want from there. This is basically what you have already shown in your sample code.
If you want to learn about all of the special support that Powershell can give you, check out these links:
about_Functions
about_Functions_Advanced
about_Functions_Advanced_Parameters
I don't think your question is very clear, this code assumes that if you're going to include the -domain parameter, it's always 'named' (i.e. dostuff computername arg2 -domain domain); this also makes the computername parameter mandatory.
Function DoStuff(){
param(
[Parameter(Mandatory=$true)][string]$computername,
[Parameter(Mandatory=$false)][string]$arg2,
[Parameter(Mandatory=$false)][string]$domain
)
if(!($domain)){
$domain = 'domain1'
}
write-host $domain
if($arg2){
write-host "arg2 present... executing script block"
}
else{
write-host "arg2 missing... exiting or whatever"
}
}
Not sure I understand the question correctly.
From what I gather, you want to be able to assign a value to Domain if it is null and also what to check if $args2 is supplied and according to the value, execute a certain code?
I changed the code to reassemble the assumptions made above.
Function DoStuff($computername, $arg2, $domain)
{
if($domain -ne $null)
{
$domain = "Domain1"
}
if($arg2 -eq $null)
{
}
else
{
}
}
DoStuff -computername "Test" -arg2 "" -domain "Domain2"
DoStuff -computername "Test" -arg2 "Test" -domain ""
DoStuff -computername "Test" -domain "Domain2"
DoStuff -computername "Test" -arg2 "Domain2"
Did that help?

Function parameters order in PowerShell

I have this code in one of my PowerShell scripts:
function callCommandWithArguments([String] $arg1, [String] $arg2)
{
[string]$pathToCommand = "C:\command.exe";
[Array]$arguments = "anArg", "-other", "$arg2", "$arg1";
# the real code is
# & $pathToCommand $arguments;
# but was not working, so I change it to debug
Write-Host $pathToCommand $arguments;
}
callCommandWithArguments("1", "2");
As the arguments order is changed in the $arguments array, I would expect this output:
C:\command.exe anArg -other 2 1
But instead I receive a strange:
C:\command.exe anArg -other 1 2
Am I missing something obvious?
try call your function like this:
callCommandWithArguments "1" "2"
In powershell you pass arguments to function without () and just separated by space.
In your code you are passsing a single argument array of type object[]