Piping Powershell multiple functions together - windows

I have this going on
Function A($data){
#Function accepts $data which is Get-Content of a file
#Function does some stuff and then return
#Function return data as String
}
Function B($data){
#This Function takes data given from Function A, manipulate it and return custom object
}
$Function C ($data1, $data2){
#This function Takes 2 custom objects created from Function B and prints out some data
}
#For this is to work i need to do this for example:
$file = 'c:\test.txt'
$data1 = A (Get-Content $file1)
$data1 = B ($data1)
#Same thing for data2 and then use function C:
C -data1 $data2 -data2 data2
though this works, I would like to use Piping, i must be using it wrong
Get-Content $file1 | A | B
Would give me errors obviously.
can someone help me pipe this?

Continued from my comment. For Example:
Building PowerShell Functions That Support the Pipeline
ValueFromPipeline Let's start off with a function to perform some
tests.
#region Test Function
Function Test-Object
{
[cmdletbinding()]
Param
(
[parameter(ValueFromPipeline)][int[]]$Integer
)
Process
{
$PSItem
}
}
#endregion Test Function

Related

How select-object cmdlet accepts input by pipeline?

Sorry for asking a novice question, as I am learning Powershell and I am confused how select-object -property parameter works with pipeline. As mentioned in help it doesnot accept the value through pipeline.
For e.g: the below code should have given an error:
get-process|select-object -property name,vm,pm
Can someone explain or guide me, thanks in advance.
To better understand how Select-Object works, here is a very simplified demo function that works similar to Select-Object:
function Select-ObjectSimplified {
[CmdletBinding()]
param (
# "ValueFromPipeline" means this parameter accepts pipeline input
[Parameter(Mandatory, ValueFromPipeline)] [PSObject] $InputObject,
# This parameter does NOT accept pipeline input
[Parameter(Mandatory)] [Object[]] $Property
)
# The process section runs for each object passed through the pipeline.
process {
# Create an ordered hashtable that will store the names and values
# of the selected properties.
$OutputObject = [ordered] #{}
# Loop over each property of $InputObject
foreach( $InputObjectProperty in $InputObject.PSObject.Properties ) {
# Check if the current property is listed in -Property argument.
if( $Property -contains $InputObjectProperty.Name ) {
# Add the current property to the output object.
$OutputObject[ $InputObjectProperty.Name ] = $InputObjectProperty.Value
}
}
# Convert the hashtable to a PSCustomObject and (implicitly) output it.
[PSCustomObject] $OutputObject
}
}
Demo:
# Create an array of two objects.
$example = [PSCustomObject]#{ Foo = 4; Bar = 8 },
[PSCustomObject]#{ Foo = 15; Bar = 16 }
# Pass the array as input to the pipeline.
$example | Select-ObjectSimplified -Property Foo | Format-List
Output:
Foo : 4
Foo : 15
Although the parameter -Property doesn't accept pipeline input, we can still use it when we process the pipeline input that binds to parameter -InputObject. There is no need for -Property to accept pipeline input, because it stays constant during the whole run of the pipeline.
The demo is executed by PowerShell like this:
$example | Select-ObjectSimplified -Property Foo | Format-List
The argument "Foo" gets bound to parameter -Property. The parameter -InputObject is not bound yet, because we didn't explicitly pass an argument to it.
The first element of the array $example is passed through the pipeline. The argument [PSCustomObject]#{ Foo = 4; Bar = 8 } gets bound to parameter $InputObject.
The process{} section runs. There we can get the current pipeline object from $InputObject and get the argument of parameter -Property from $Property. So $InputObject will be different for each run of process{}, but $Property does not change, it is constant.
The second element of the array $example is passed through the pipeline. The argument [PSCustomObject]#{ Foo = 15; Bar = 16 } gets bound to parameter $InputObject.
Like 3), but with different value for $InputObject.
Hope that shed some light on the topic. To get an even better understanding I suggest to read About Pipelines and then follow a tutorial to write your own pipeline function. The concept only did really click for me, once I successfully wrote my first real pipeline functions.

powershell pass array to another function

I have a function that gets all the processes accessing a particular folder and split it's process id. In this function, all the ids are saved in an array. I want to give this array to another function. The code i have is:
$CLRJson = Get-Content -Raw -Path "C:\Users\Lokal-keeran\Documents\Ausbildung\Aufgaben\Powershell\PI-Kill\PIKill.json" | ConvertFrom-Json
##Directory path to be scanned to see if there are processes accessing the files in the given directory.
$CLRDIRProd = $CLRJson.CLRExtproc_PARA_PROD.CLRDIR
$PIDGF = #()
#Get all Processes, accessing a folder
function Get-FileHandle ($HPath){
$handle = handle.exe $HPath
[System.Collections.ArrayList]$Process = $handle | Select-String -Pattern '.*?(?= +type:)' | Select-String -Pattern 'mstsc.exe' -NotMatch
Foreach ($pro in $Process.Matches.Value) {
$AddtoArray = $pro -split(":") | Select-Object -Last 1
$PIDGF += $AddtoArray
}
}
#Stop the Processes accessing that folder
function stop-FileHandle ([string[]]$HPathtoGFH) {
Get-FileHandle -HPath $CLRDIRProd
"The Third Value of this Array is: $PIDGF[2]"
#Stop Command......
}
stop-FileHandle -HPathtoGFH $CLRDIRProd
If i revoke stop-filehandle no output will be shown. Also the global array doesn't contain any value. But if i run the commands in the function Get-Filehandle, it shows me some process ids one below the another, just like it should be.....
Why array could not be given to another function ?
Inside function Get-FileHandle, stuff is added to a function local variable called $PIDGF.
Since you have declared your array outside the function, you should use script-scoping inside the function:
$script:PIDGF += $AddtoArray
Then in the stop-FileHandle function you need to use a subexpression ($())around the value you want to output:
"The Third Value of this Array is: $($script:PIDGF[2])"
Much better would be if the Get-FileHandle simply outputs the array so the calling function can capture that in a variable:
function Get-FileHandle ($HPath){
$handle = handle.exe $HPath
[System.Collections.ArrayList]$Process = $handle |
Select-String -Pattern '.*?(?= +type:)' |
Select-String -Pattern 'mstsc.exe' -NotMatch
foreach ($pro in $Process.Matches.Value) {
# just output the values, so the calling function receives an array
$pro -split(":") | Select-Object -Last 1
}
}
#Stop the Processes accessing that folder
function Stop-FileHandle ([string[]]$HPathtoGFH) {
$PIDGF = Get-FileHandle -HPath $CLRDIRProd
"The Third Value of this Array is: $($PIDGF[2])"
#Stop Command......
}
Stop-FileHandle -HPathtoGFH $CLRDIRProd
This way you do not use += concatenation to an existing array which is both time and memory consuming, plus you do not have to worry about scoping.

How does one Transform a Collection of Objects into a Collection of new Objects of another Type in Powershell?

Supposing I have a method that gets a collection of enums of my enum type ProjectName and a have a collection of Server objects I want to associate with those ProjectNames in a type called Project; how would I do this in a Powershell Select-Object (or some other equivalent of LINQ's Select).
The C# equivalent of what I want to produce in Powershell is this:
var servers = new[]
{
new Server(/*someOtherProjectsCollection goes here*/),
new Server(/*someOtherProjectsCollection goes here*/),
new Server(/*someOtherProjectsCollection goes here*/)
};
var projects = GetProjectNames().Select(projectName => new Project(projectName, servers.Where(server => server.Projects.Any(serverProject => serverProject.Name == projectName))));
But what I have is this:
$servers = [Server]::new(/*someOtherProjectsCollection goes here*/), [Server]::new(/*someOtherProjectsCollection goes here*/), [Server]::new(/*someOtherProjectsCollection goes here*/)
$projects = (GetProjectNames()) | Select-Object {
$selectedProjectName = $_
return [Project]::new($_, ($servers | Where-Object { $_.projects.Where({ $_ -eq $selectedProjectName }).Count -gt 0 }))
}
When I try and read $projects back in Powershell LSE (whilst on a breakpoint after this last line), it just returns the code as a string and I can't even cast it to [Project[]]. I think the problem might be with the use of curly braces with Select-Object but I'm not sure how else to create a new Project object within the Select-Object.
You want ForEach-Object instead of Select-Object to return a new [Project] instance for each project name; also, your code can be streamlined:
$projects = GetProjectNames | ForEach-Object {
$projectName = $_
[Project]::new(
$projectName,
$servers.Where({ $_.projects -eq $projectName })
)
}
Select-Object is for creating new custom objects based on select properties from the input objects; by contrast, you're constructing a new, specific type instance from each input object, which must be done in a ForEach-Object call, where you explicitly control the output.
$_.projects -eq $projectName as a conditional relies on PowerShell's ability to use -eq with an array as the LHS, in which case filtering is performed, and a filtered subarray is returned; since .Where() interprets the script block's output as a Boolean, an empty subarray is interpreted as $false, whereas one with at least one element is interpreted as $true.
Also note that you don't need an explicit return, given PowerShell's implicit output behavior: since the newly constructed [Project] instance isn't assigned to a variable or sent elsewhere, it is automatically returned.

Output not displayed when assigning the return value of a function to a variable

If I have a very simple script that contains a function that just does the following:
function saySomething() {
Write-Output "Saying someting..."
}
If I call this function from the console the output is displayed. If I, however, do the following:
$something = saySomething
Then no output is displayed. Obviously my working example is much more complicated than this, but in short, how do I display the output within a function if that function is used to set a variable?
Try the following:
saySomething | Tee-Object -Variable something
This should work exactly as you want.

string as xpath selector

I have a function that takes the result set as element and a string as args and I want to use this string as a selector inside the function.
function abc ($results as element()*, $element as xs:string)
{
for $g in distinct-values($results//*[name() = $element]) (: $results//genre :)
let $c := //$results[? = $g] (: //$results[genre=$g] :)
}
what should be in place of '?' in variable '$c'
function abc ($results as element()*, $element as xs:string)
{
for $g in distinct-values($results//*[name() = $element]) (: $results//genre :)
let $c := //$results[? = $g] (: //$results[genre=$g] :)
}
what should be in place of '?' in
variable '$c'
It is a syntax error to write:
//$results
This question is rather vague, but it seems to me that you want to group the elements, contained in $results according to their genre (or whatever element name defined by $element -- BTW this name sucks (an element isn't a string) -- better use $elementName).
The other concerning fact is that you check the complete subtrees topped by each of the elements in $results -- this means that they may have multiple genre (or whatever) descendents.
To finalize my guessing spree, it seems to me that you want this:
function grouping-Keys ($elems as element()*, $subElementName as xs:string) as element()*
{
for $g in distinct-values($elems//*[name() = $subElementName ])
return
$elems[//*[name() = $subElementName ] = $g][1]
}
If it is known that $subElementName is a name of a child that any of the elements in $elems have, then the above should be better written as:
function grouping-Keys ($elems as element()*, $childElementName as xs:string) as element()*
{
for $g in distinct-values($elems/*[name() = $childElementName ])
return
$elems[/*[name() = $subElementName ] = $g][1]
}
Both of the above functions return one element per a (distinct value of) genre (or whatever). If it is known (guaranteed) that every element in $elems has exactly one child (descendent) named genre (or whatever), then the results of these functions are not redundant.

Resources