Exit a PowerShell function but continue the script - function

This might seem like a very very stupid question, but I can't really figure it out. I'm trying to have the function stop when it finds its first hit (match) and then continue with the rest of the script.
Code:
Function Get-Foo {
[CmdLetBinding()]
Param ()
1..6 | ForEach-Object {
Write-Verbose $_
if ($_ -eq 3) {
Write-Output 'We found it'
# break : Stops the execution of the function but doesn't execute the rest of the script
# exit : Same as break
# continue : Same as break
# return : Executes the complete loop and the rest of the script
}
elseif ($_ -eq 5) {
Write-Output 'We found it'
}
}
}
Get-Foo -Verbose
Write-Output 'The script continues here'
Desired result:
VERBOSE: 1
VERBOSE: 2
VERBOSE: 3
We found it
The script continues here
I've tried using break, exit, continue and return but none of these get me the desired result. Thank you for your help.

As was mentioned, Foreach-object is a function of its own. Use regular foreach
Function Get-Foo {
[CmdLetBinding()]
Param ()
$a = 1..6
foreach($b in $a)
{
Write-Verbose $b
if ($b -eq 3) {
Write-Output 'We found it'
break
}
elseif ($b -eq 5) {
Write-Output 'We found it'
}
}
}
Get-Foo -Verbose
Write-Output 'The script continues here'

The scriptblock you are passing to ForEach-Object is a function in its own right. A return in that script block just returns from the current iteration of the scriptblock.
You'll need a flag to tell future iterations to return immediately. Something like:
$done = $false;
1..6 | ForEach-Object {
if ($done) { return; }
if (condition) {
# We're done!
$done = $true;
}
}
Rather than this, you may be better using a Where-Object to filter the pipeline objects to only those that you need to process.

Related

How to handle the result of a Powershell Object to call a function

Here is my code:
function setParameterb () {
$Name = $args[0]
"Arg1: $Name"
}
Write-Output "### Starte AlertlogRotate ###"
$folders = #('d:', 'e:', 'f:', 'g:', 'h:', 'i:', 'j:', 'k:', 'l:')
foreach ($i in $folders) {
$filenames = Get-ChildItem -Path $i\*\log -ErrorAction SilentlyContinue -Recurse -Filter alert_*.log | Select-Object FullName
}
The setParametersb function is just for test now and should only print the result. Later, I will use it to zip logfiles which get too big.
I need to get the result of this powershell object into a string to call a function for every line.
The Object looks like this:
FullName
--------
D:\AREA\log\diag\rdbms\area\area\trace\alert_area.log
D:\CONS\log\diag\rdbms\cons\cons\trace\alert_cons.log
D:\DEV01\log\diag\rdbms\dev01\dev01\trace\alert_dev01.log
G:\TEST01\LOG\diag\rdbms\test01\test01\trace\alert_test01.log
G:\TEST02\log\diag\rdbms\test02\test02\trace\alert_test02.log
I know, that I have to crop the headline "FullName", the row"--------" and some empty lines, but this is not my problem now.
My problem is to transfer the object $filenames into an array to be able to call the function setParameterb with every single line from the output.
tried lots of other stuff, but finally this solved my problem:
$filenames |ForEach-Object { setParameterb $_.FullName }
try stuff like
function setParameterb () {
param (
[Parameter(Mandatory=$true)]
$names,
)
foreach($name in $names){
"Arg1: $name"
}
}

How to handle some function with if statement using Powershell?

I want to handle my function with if statement. I tried this code, but it always return me the value of $End_F which is "BB" even my file contains of "#AB#CD" . Anyone can help, please.
The file that I look for "#AB#CD" is like this.
Config
; Date="2019/06/12" Time="10:25:02" UTC="0"
;
Number
123456#AB#CD
$Get_SKU = Get-Content '.\Number.txt' | Where-Object {$_.Contains("#AB#CD")}
$Get_SKU
if($Get_SKU)
{$ML = "1"
AUTO_SELECT
}
else
{
END_Proc
}
Function AUTO_SELECT
{
$AT = "AA"
$AT
}
Function END_Proc
{
$End_F = "BB"
$End_F
}
$FE_UB = "4"
if($ML = "1" -and $FE_UB -eq $true)
{
G_BEGIN
}
if($ML = "1" -and $FE_UB -eq $false)
{
G_END
}
else
{
END_Proc
}
Function G_BEGIN
{
$begin = "Ready"
$begin
}
Function G_END
{
$ending = "Stop"
$ending
}
Some things need to be corrected to make your code work as expected.
Function AUTO_SELECT
{
$AT = "AA"
$AT
}
Function END_Proc
{
$End_F = "BB"
$End_F
}
Function G_BEGIN
{
$begin = "Ready"
$begin
}
Function G_END
{
$ending = "Stop"
$ending
}
$Get_SKU = Get-Content '.\Number.txt' | Where-Object {$_.Contains("#AB#CD")}
$Get_SKU
if($Get_SKU)
{
$ML = "1"
AUTO_SELECT
}
else
{
END_Proc
}
$FE_UB = "4"
if($ML -eq "1" -and $FE_UB)
{
G_BEGIN
}
if($ML -eq "1" -and !$FE_UB)
{
G_END
}
else
{
END_Proc
}
Explanation of Changes:
$Get_SKU will store either $null or a string depending on whether the Where-Object condition finds a match. As a result, I swapped out if ($Get_SKU -eq $true) in favor of if ($Get_SKU). This change will result in a $true evaluation if $Get_SKU is not $null.
I moved the functions to the top of the script because PowerShell executes the code starting from top to bottom. It is not compiled first. So you can't make a function call BEFORE the function has been read into memory and defined.
if ($ML = "1" -and $FE_UB -eq $true) has been updated to if ($ML -eq "1" -and $FE_UB) because variable assignment variable = value should not happen in an if statement condition. If you are comparing values, the proper operator here is -eq. Regarding $FE_UB, the same explanation applies as in the $Get_SKU changes.
$FE_UB -eq $false was changed to !$FE_UB. The removal of the -eq $false operator is based on the explanation given for $Get_SKU. The ! character is used to effectively -not the result. This will turn the value into a boolean value and then output the opposite boolean response. For example, !"string data" will output $False. !$null will output $True. I hope this part is clear.
Further Insight:
$True and $False evaluations
You can make just about anything return a boolean value. Three such ways include using casting, the -as operator, and !. There are many other ways and hacks to do this.
Casting:
$get_sku = "data"
[boolean]$get_sku
True
-as Operator:
$get_sku = $null
$get_sku -as [boolean]
False
Fun with !:
$get_sku = 4
!$get_sku
False
!!$get_sku
True

Empty parameter is not Null in function

Given this basic function:
Function TestFunction {
Param ( [int]$Par1, [string]$Par2, [string]$Par3 )
If ($Par1 -ne $Null) { Write-Output "Par1 = $Par1" }
If ($Par2 -ne $Null -or $Par2 -ne '') { Write-Output "Par2 = $Par2" }
If ($Par3 -ne $Null) { Write-Output "Par3 = $Par3" }
}
TestFunction -Par1 1 -Par3 'par3'
...the output is:
Par1 = 1
Par2 =
Par3 = par3
Even though I didn't pass anything into the $Par2 variable, it still isn't Null or empty. What happened, and how can I rewrite the statement so that the second If-statement evaluates as False and the script-block does not get executed?
(I added the -or $Par2 -ne '' just to test, it behaves the same with and without it.)
You have a logic error in your program: $Par2 will always be not equal to $null or not equal to ''.
To fix the logic, you should use -and instead of -or here:
If ($Par2 -ne $Null -and $Par2 -ne '') { Write-Output "Par2 = $Par2" }
However, because you casted the $Par2 argument to a string in the function's argument list:
Param ( [int]$Par1, [string]$Par2, [string]$Par3 )
^^^^^^^^
the check for $Par2 -ne $Null is unnecessary since $Par2 will always be of type string (if you do not give it a value, it will be assigned to ''). So, you should actually write:
If ($Par2 -ne '') { Write-Output "Par2 = $Par2" }
Or, because '' evaluates to false, you might just do:
If ($Par2) { Write-Output "Par2 = $Par2" }
You can check that (check if $variablename has $null as value):
if (!$variablename) { Write-Host "variable is null" }
And if you wanna check if $variablename has any value except $null:
if ($variablename) { Write-Host "variable is NOT null" }

Passing a cmdlet as a parameter

I need to restart a service in a powershell script. The problem is that this service is a bit buggy and frequently needs to be shut down several times before it gets into the "stopped" state. Because of that I can't seem to use the Restart-Service cmdlet, instead I need to retry the Stop-Service cmdlet a few times. The same applies to starting the service.
So I figure this is a good place to write a function that will take an action (start or stop) and retry it a few times until it works. The problem is I'm not sure how to pass the action in as a parameter. I could just have the action be a String and then say if action == "start" do starcAction, but that won't be very clean. Is there any way I could pass a cmdlet like Stop-Service in as a parameter?
For the scenario you described you'd normally do something like this:
$maxTries = 5
switch ($args(0)) {
'start' {
Start-Service 'MySvc'
}
'stop' {
$i = 0
do {
Stop-Service 'MySvc'
$i++
} until ((Get-Service 'MySvc').Status -eq 'Stopped' -or $i -ge $maxTries)
if ((Get-Service 'MySvc').Status -ne 'Stopped') {
Write-Error "Cannot stop service."
exit 1
}
}
default {
Write-Error "Unknown action: $_"
exit 1
}
}
If you really want to avoid string arguments, you could use parameter sets like this:
[CmdletBinding(DefaultParameterSetName='none')]
Param(
[Parameter(Mandatory=$true,ParameterSetName='start')]
[Switch][bool]$Start = $false,
[Parameter(Mandatory=$true,ParameterSetName='stop')]
[Switch][bool]$Stop = $false
)
$maxTries = 5
switch ($PSCmdlet.ParameterSetName) {
'start' {
Start-Service 'MySvc'
}
'stop' {
$i = 0
do {
Stop-Service 'MySvc'
$i++
} until ((Get-Service 'MySvc').Status -eq 'Stopped' -or $i -ge $maxTries)
if ((Get-Service 'MySvc').Status -ne 'Stopped') {
Write-Error "Cannot stop service."
exit 1
}
}
'none' {
Write-Error "Usage: $($MyInvocation.MyCommand.Name) {-Start|-Stop}"
exit 1
}
}
Param([Parameter(Mandatory)] [ValidateSet('Start','Stop')] [string] $Action)
This allows the user to press Tab to select the possible values and will automatically reject all invalid input.
Passing in a well defined parameter (doesn't matter if it's a string or not) is actually cleaner than "passing in a commandlet" would be if there was such a thing.

Printing the error in try catch block in powershell

Here is my script which returns a boolean
param($fileName, $path, $contextMenuItem, $automationDLLPath)
function CloseWindowsExplorer()
{
(New-Object -comObject Shell.Application).Windows() | foreach-object {$_.quit()}
}
Try
{
Import-Module $automationDLLPath
# Open the explorer window in a maximized form
Start-Process explorer $path -WindowStyle Maximized
Start-Sleep 1
Get-UIAActiveWindow
# Get the "Items View" in Explorer to go through all the lements
$list = Get-UIAList -Name 'Items View' -TimeOut 30000;
# Get the file specified in the feature file from the Items View
# Added a sleep because the VM takes time to perform the functions
Start-Sleep 1
$file = $list | Get-UIAListItem -Name $fileName;
# Perform a single click on the file to invoke a right click on it
Invoke-UIAListItemSelectItem -InputObject $file -ItemName $fileName;
# Added a sleep because the VM takes time to perform the functions
Start-Sleep 1
# Invoke the right click on the selected file
$menu = Invoke-UIAControlContextMenu -InputObject $file;
Start-Sleep 1
# select our context menu item
$menuItem = Get-UIAMenuItem -InputObject $menu $contextMenuItem -TimeOut 30000;
# Display error if the required item in the context menu is not found
if( $null -eq $menuItem){
%{ Write-Host 'cannot find menuItem' }
}
# Invoke the item if found in the context menu
else{
Invoke-UIAMenuItemClick -InputObject $menuItem
}
# close the windows explorer and return true
CloseWindowsExplorer
Write-Output "true"
}
Catch
{
# close the explorer window as a part of teardown and return false to reflect test failure
Write-Output "false"
CloseWindowsExplorer
}
I want the script to print the exact exception that was caught as well as return a boolean but in this case it is just returning false when the script fails. Any help is appreciated
Basically I need to print the exception as if the try catch block does not exist.
You need to use the special variable $_
This small example shows how it works:
try {
testmenow
} catch {
Write-Host $_
}
$_ is an object so you can do
$_|gm
in the catch block in order to see the methods you can call.