Empty parameter is not Null in function - 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" }

Related

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

Exit a PowerShell function but continue the script

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.

remove only blanks line by line with user input

Yesterday I asked this question. Now I would like to do almost the same exercise with a small change: if there is a blank character on a line (go line by line in the CSV) ask the user if the blank character should be removed or not; the difference is, ONLY the blank and not the whole line.
The code which works for my previous question is:
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Retain line."
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Delete line."
$n = #()
$f = Get-Content .\test.csv
foreach ($item in $f) {
if($item -like "* *"){
$res = $host.ui.PromptForChoice("Title", "want to keep this line? `n $item", [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no), 0)
switch ($res) {
0 {$n+=$item}
1 {}
}
} else {
$n+=$item
}
}
$n | Set-Content .\test.csv
What I think I should use is the Trim() function to achieve this.
So, I think I should modify in the if clause like below (aplogies for the silly syntax mistakes which I might do):
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Retain blank."
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Delete blank."
$n = #()
$f = Get-Content .\test.csv
foreach ($item in $f) {
if ($item -like "* *") {
$res = $host.ui.PromptForChoice("Title", "want to keep the blank on this line? `n $item", [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no), 0)
switch ($res) {
0 {$n+=$item.Trim()}
1 {}
}
} else {
$n+=$item.Trim()
}
}
$n | Set-Content .\test.csv
This runs, but still deteles the line, so it shouldn't matter if I trimmed it or not first, I need to fix it so it will be kept or trimmed but not discarded .
EDIT:
Adjusting the switch ($res) like this doesn't work:
switch ($res) {
0 {$n+=$item.Trim()}
1 {$n+=$item}
}
} else {
$n+=$item.Trim()
}
Trim() without parameter removes all whitespace (not just spaces) from beginning and end of a string. You can't use it for removing spaces anywhere else in a string. Instead use the -replace operator:
$_ -replace ' '
Note that this time you need to output the unmodified string not only if it doesn't contain a space, but also if the user chooses to keep the existing space(s).

Using Set-ADUser within a Function

Im having issues setting an attribute value in AD when using a function. When I use Set-ADUser under the same conditions without using a function I do not get an issue, it works great. When using Set-ADUser within a function I am getting an invalid argument error. I need to use a function as I am comparing a lot of data values. Alot of data is going to be compared this the need for a function. Im stumped.
function compareandset($value_ad, $value_csv, $userid, $propdata) {
$id = $userid.SamAccountName
IF($value_ad -eq $value_csv) {
Write-Host "The values were the same!"
}
ELSEIF($value_ad -ne $value_csv) {
Write-Host "AD value changed"
get-aduser -filter {SamAccountName -eq $userid} | Set-ADUser -$propdata $value_csv
}
}
$userid = "jsmith"
$value_ad = "A city"
$value_csv = "Not a city"
$propdata = "Office"
compareandset $Office $office_csv_value $userid $propdata
What does the full error message say? You can try using splatting and change:
...
Write-Host "AD value changed"
get-aduser -filter {SamAccountName -eq $userid} | Set-ADUser -$propdata $value_csv
}
...
to:
...
Write-Host "AD value changed"
$params = #{$propdata=$value_csv}
get-aduser -filter {SamAccountName -eq $userid} | Set-ADUser #params
}
...
More about splatting here: http://technet.microsoft.com/en-us/library/jj672955.aspx

output function result in powershell write-host

i'm attempting to write the result of the
$x = [System.Net.Dns]::GetHostAddresses($name) statement into my write-host string but am encounter some issues with getting the result from the function into the output.
here's the relevant code:
Import-Module activedirectory
function fu($name)
{
$x = [System.Net.Dns]::GetHostAddresses($name).value
if ($x -ne $null){
Write-Host{ $x }
}
else{
Write-Host{"Null"}
}
}
Get-ADComputer -SearchBase 'OU=CorpServers,DC=corp,DC=com,DC=net' -Server "corp.com.net" -Filter * -Properties * |ForEach-Object{write-host "add filter filterlist=""L2-Windows Servers"" srcaddr=any dstaddr=$(fu $_.Name) description=""$($_.Name)"""}
currently it just outputs the string as is, but when it reaches the fu subexpression seems to not properly perform the logic and only outputs "$x" literally, where my intent was to have it output the IP of the current obj in the foreach-object statement.
It's because you put $x in curly brackets {}.
Just do Write-Host $x
I expand a bit the code for the explanation but try this :
function fu($name)
{
$res = $null
$x = [System.Net.Dns]::GetHostAddresses($name)
if ($x -ne $null)
{
$res = $x
}
return $res
}
$a = fu "localhost"
$a
$a.gettype().fullname
It does what you want, $a is an array of data. But you have to understand that the following functions gives different results
function fu($name)
{
$res = $null
$x = [System.Net.Dns]::GetHostAddresses($name)
if ($x -ne $null)
{
$res = $x
}
Write-Host $res
}
Clear-Host
$a = fu "localhost"
$a
$a | Get-Member
The las one give again the good result. return and write-out both write data in the output of the function. Write-host just write to the host.
function fu($name)
{
$res = $null
$x = [System.Net.Dns]::GetHostAddresses($name)
if ($x -ne $null)
{
$res = $x
}
Write-output $res
}
Clear-Host
$a = fu "localhost"
$a
$a | Get-Member
[System.Net.Dns]::GetHostAddresses(<<hostname>>) | Get-Member does not show any value property ?
You may try this function instead.