Good evening. I'm totally newbie to powershell and I have surely a silly question but I can't find an answer on my own.
I have a txt file like this
192.168.1.1|2
192.168.1.2|4
192.168.1.3|3
My function takes an IP as a parameter and it returns the integer values after the pipe. The function works but I don't know how to sum a value to the function result.
$client = "192.168.1.2"
function file-update($client) {
$clientrow = gc "C:\clients.txt" | ? {$_ -match $client}
if ($clientrow) {
$filesupdated = $clientrow.Split("|")[1]
return $filesupdated
}
else {
return 0
}
}
file-update $client
# it returns 4
file-update $client + 1
# it returns 4 too instead of 5
What'is my mistake?
Thanks in advance.
You need your function to execute and return a value before performing the addition. You can simply use () to group the function call. Since your function returns a [string] when a client is found, you will have to do a conversion to a numeric type to support addition. Having an integer on the left-hand side (LHS) of the operator (+) will convert the RHS value to [int] automatically if possible.
1 + (file-update $client)
You can write the function differently to minimize the amount of work done to extract the integer value:
# Best practice is to use verb-noun for naming functions
# Added file parameter (for file path) to not hard code it inside the function
function Update-File {
Param(
$client,
$file
)
# Casting a null value to [int] returns 0
# Delimiter | to override default ,
# Named headers are needed since the file lacks them
[int](Import-Csv $file -Delimiter '|' -Header IP,Number |
Where IP -eq $client).Number
}
$client = '192.168.1.2'
$file = 'c:\clients.txt'
Update-File $client $file # returns 4
(Update-File $client $file) + 1 # returns 5
Related
Passing switch parameter thru pipeline in PowerShell
Problem
I am trying to make a function that has a switch parameter, but also I want to able to pass all function parameters thru pipeline in a script, and I don't know ho to do that. Is it that even possible? I my case I load parameters from .csv file in witch values are string values.
Exposition
To simplify my problem and to make it easier for others to use answers of this question, I am not going to use my code but an abstract version of my code. Let us call my function New-Function that has a -StringParameter, a -IntParameter and a -SwitchParameter parameters. And just to be clear in my .csv file all fields are named same as the New-Function parameters.
Using the function
Normally I you can use the New-Function this way:
New-Function -StringParameter "value" -IntParameter 123 -SwitchParameter
But I also want to use the New-Function this way:
$Data = Import-Csv -Path "$PSScriptRoot\Data.csv" -Delimiter ';'
$Data | New-Function
My attempts
I have tried to convert the string values in pipe line to boolean but it seems like the function's -SwitchParameter does not accept boolean($true, $false) values, because it skipping the process block completely when I debug it.
$Data | ForEach-Object -Process {
if ($_.SwitchParameter -eq "true") {
$_.SwitchParameter = $true
}
else {
$_.SwitchParameter = $false
}
} | New-Function
My temporary workaround
I have settled to use a string parameter instead of a switch parameter, so I can feed the New-Function with data thru pipeline from a .csv file with no problem.
function New-Function {
param (
[Parameter(Position = 0, Mandatory, ValueFromPipelineByPropertyName)]
[string]
$StringParameter,
[Parameter(Position = 1, Mandatory, ValueFromPipelineByPropertyName)]
[int]
$IntParameter,
[Parameter(Position = 2, ValueFromPipelineByPropertyName)]
[string]
$SwitchParameter = "false"
)
#----------------------------------------------------------------------------
}
You have to convert values for switch parameter to boolean type.
It works to me:
function Out-Test
{
param
(
[Parameter(ValueFromPipelineByPropertyName)]
[String]
$Label,
[Parameter(ValueFromPipelineByPropertyName)]
[Switch]
$Show
)
process
{
$Color = if ($Show) { 'Yellow' } else { 'Gray' }
Write-Host -ForegroundColor $Color $Label
}
}
$row1 = '' | select Label, Show
$row1.Label = 'First'
$row1.Show = 'True'
$row2 = '' | select Label, Show
$row2.Label = 'Second'
$row1.Show = 'False'
$rows = $row1, $row2
$rows |% { $_.Show = [bool]$_.Show }
$rows | Out-Test
Result:
You can convert your string to a Boolean object while leaving your parameter as type [switch] in your function. The Boolean type will be coerced into [switch] during binding.
$Data | Foreach-Object {
$_.SwitchParameter = [boolean]::Parse($_.SwitchParameter)
$_
} | New-Function
Alternatively, you can update all of your objects first and then pipe to your function. It matters how your function handles the input objects.
$Data | Foreach-Object {
$_.SwitchParameter = [boolean]::Parse($_.SwitchParameter)
}
$Data | New-Function
Part of the issue with your Foreach-Object attempt is that you never output the updated object $_ before piping into your 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.
Here is a short example (my actual code requires me to output many more tables during the function run, and get a single returned output from it):
Function Select-RowFromCSV ($CSV)
{
$CSV
return $CSV[(read-host "select row # from CSV")]
}
Instead of outputting $CSV within the function it gets appended to the return and is getting into the variable that the function inserted to.
PS C:\Windows\system32> $ROW = Select-RowFromCSV -CSV (Import-Csv "C:\scripts\csv.csv")
select row # from CSV: 0
PS C:\Windows\system32> $ROW
Name Phone
Dan 111111
Dave 5555555
Oliver 666666
Dan 111111
PS C:\Windows\system32>
I tried multiple ways to try and print it to the screen, however unlike write-host that do work as expected for strings, none of the other one i tried works for non strings objects (FT, write-output, echo).
If you want to output something to the console without affecting the output of the Function, Write-Host is probably the simplest solution (but is considered harmful). Your code needs to be as follows:
Function Select-RowFromCSV ($CSV)
{
Write-Host ($CSV | Format-Table | Out-String)
Return $CSV[(read-host "select row # from CSV")]
}
$ROW = Select-RowFromCSV -CSV (Import-Csv "raid.csv")
As you observed, because your object isn't a string you need to Format it as you'd like and then convert it to a String (| Format-Table | Out-String).
However, you might want to consider using Write-Verbose which will write output to the Verbose stream instead, only when the -Verbose switch is used. To use this you need to add [cmdletbinding()] and a Param() block to your function, like this:
Function Select-RowFromCSV
{
[cmdletbinding()]
Param($CSV)
Write-Verbose ($CSV | Format-Table | Out-String)
Return $CSV[(read-host "select row # from CSV")-1]
}
Then execute your function with the -Verbose switch to see the extra output:
$ROW = Select-RowFromCSV -CSV (Import-Csv "raid.csv") -Verbose
Further Explanation:
In your original function you were outputting all of $CSV because it appeared on a line by itself, then also returning a row of it. The Return keyword is a little misleading in PowerShell, it doesn't define what is only returned, it just triggers the Function to immediately end and anything that has been output will go in to the output stream.
You should also note that the row number your user enters needs to start from 0, because Array indexes start from 0.
If you wanted the first row to be 1 rather than 0, you might want to do this:
$CSV[(read-host "select row # from CSV")-1]
You could also drop the Return keyword entirely as it's not necessary. If you want to be more explicit, I personally favour Write-Output.
Just use the following method:
$counter = 0
$table = #()
XXX | foreach-object {
do something;
$table +=[pscustomobject]# {
XXXX
}
$table[$counter]
$counter++
}
It seems that PowerShell adds an additional variable to the return value of a function.
The function subfoo2 itself delivers the correct values, but as soon as PowerShell jumps back to the postion where I called the function (in foo1), value contains the value of an other variable ($msg)
(Have a look at the comments in the code)
writeMessageLog($msg){
...
Add-Content $msg
...
}
subfoo2{
writeMessageLog($msg)
return $UserArrayWithValues #During Debug, $Array is fine (1)
}
foo1{
$var = subfoo2 $UserArray # $var has now the value of $msg and $UserArrayWithValues (2)
#do something with var
}
Realcode:
function WriteLog
{
param ( [string] $severity , $msgNumber, [string] $msg )
...
$msgOut = $date + ... + $msg
Add-Content $msgout ( $msgOut )
...
}
function getFeatures
{
writelog 'I' 1002 $true $true "Load Features"
$Features = importCsv -pPath $FeatureDefintionFilePath
Writelog 'I' 1000 $true $true "Features Loaded"
return $Features # $Features has value as expected (1)
}
function GetUserFeatures ($pUserObject)
{
$SfBFeatures = ""
$SfBFeatures = getFeatures #SfBFeaures has Value of $msg and $Features (2)
...
}
Do I use the functions/return values wrong? What could lead to such behavior? Is it an issue if i call a function within a function?
If I remove $msgOut = $date + ... + $msg in writeMessageLog, the values are fine.
I'm pretty lost right now, and have no ideas where this comes from. Any ideas welcome.
This is how powershell works, basically everything that you print out will be returned as the function output. So don't output extra stuff. To force something to not output stuff you can do:
$null = some-command_that_outputs_unwanted_things
since everybody is obsessed with Out-Null I'll add this link showing several other ways to do that.
Within a function, everything you don't assign or pipe to a consuming cmdlet will get put to the pipeline and returned from the function - even if you don't explicit return it. In fact the return keyword doesn't do anything in PowerShell so the following is equivalent:
function Test-Func
{
"Hello World"
}
function Test-Func
{
return "Hello World"
}
So it looks like your writeMessageLog puts anything on the pipeline thus you have to either assign the value to anything:
$notUsed = writeMessageLog($msg)
or (prefered) pipe it to the Out-Null cmdlet:
writeMessageLog($msg) | Out-Null
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[]