Powershell - an empty pipe element is not allowed - powershell

I keep receiving the following error message - "An empty pipe element is not allowed" whenever I try to pipe out my results to a csv file. Any idea why this might be happening?
$apps = Import-CSV apps.csv
$computers = Import-CSV compobj.csv
foreach($computer in $computers) {
$computerLob = $computer.lob
$lobApps = $apps | ? {$_.lob -eq $computerLob}
foreach($app in $lobApps){
$appLocation = $app.location
$installed=Test-Path "\\$computerHostname\$appLocation"
$computerHostname = $computer.hostname
$results = new-object psobject -property #{Computer=$computer.hostname;App=$app.appname;Installed=$installed} | select Computer,App,Installed
$results
}
} | Export-csv "results.csv" -NoTypeInformation
I've tried doing this:
$results | Export-csv "results.csv" -NoTypeInformation
within the foreach loop but it only returns the last record.

A foreach loop doesn't ouput to the pipeline. You can make it do that by making the loop a sub-expression:
$apps = Import-CSV apps.csv
$computers = Import-CSV compobj.csv
$(foreach($computer in $computers) {
$computerLob = $computer.lob
$lobApps = $apps | ? {$_.lob -eq $computerLob}
foreach($app in $lobApps){
$appLocation = $app.location
$installed=Test-Path "\\$computerHostname\$appLocation"
$computerHostname = $computer.hostname
$results = new-object psobject -property #{Computer=$computer.hostname;App=$app.appname;Installed=$installed} | select Computer,App,Installed
$results
}
}) | Export-csv "results.csv" -NoTypeInformation

I believe the problem you are having is around the use of foreach and the the pipeline, you are processing your items in the foreach statement but still expecting them to be on the pipeline.
This is a common error and is explained in more detail in the article Essential PowerShell: Understanding foreach and Essential PowerShell: Understanding foreach (Addendum).
You should be able to achieve what you want like this:
$apps = Import-CSV apps.csv
$computers = Import-CSV compobj.csv
$computers | foreach {
$computer = $_
$computerLob = $computer.lob
$lobApps = $apps | ? {$_.lob -eq $computerLob}
$lobApps | foreach {
$app = $_
$appLocation = $app.location
$installed=Test-Path "\\$computerHostname\$appLocation"
$computerHostname = $computer.hostname
new-object psobject -property #{Computer=$computer.hostname;App=$app.appname;Installed=$installed}
}
} | Export-csv "results.csv" -NoTypeInformation

Some of flow-control expressions do not stream their output.
But they can be assigned to variables, but no output streaming.
You can put your expression output stream in a Subexpression $() and then pipe with another command.

You can't pipe from ANY powershell statements. I'm not sure why.
if .. else
Switch
Do .. while
ForEach
For
While
EDIT: Another way. You can stream from a call operator and a scriptblock (or a function). Put the foreach or any other statement inside it. You won't have to wait until it finishes.
& { foreach ($i in 1..3) { $i } } | measure
Count : 3
Average :
Sum :
Maximum :
Minimum :
StandardDeviation :
Property :

Related

Expanding System.Object[] for Export to CSV

Hello again and sorry!
To keep it really short. What am i doing wrong?
Im attempting to export a list of users filtered by using a customobject to a CSV and it outputs it into the same block. Is there no way to change this? I only ask because, all the other pages ive looked at it keeps telling me to use -join, to join them as strings which does the exact same thing. Is it not possible to output it as multiple rows for each user?
$GPMem = Get-ADGroupMember -Identity security.group | Select-Object -ExpandProperty Name
[array]$TPpl = $GPMem | Where-Object {$_ -like "T*"}
[array]$RPpl = $GPMem | Where-Object {$_ -like "r*"}
[array]$CPpl = $GPMem | Where-Object {$_ -like "c*"}
[pscustomobject]#{
TPeople = (#($TPpl) |Out-String).Trim()
TPCount = $TPpl.Count
RPeople = (#($RPpl) |Out-String).ToString()
RPCount = $TPpl.Count
CPeople = $CPpl
CPCount = $TPpl.Count
} | Export-Csv -Path C:\Users\abraham\Desktop\csv.csv -NoTypeInformation -Force
here is how to insert a ; OR newline into the values of a column ... [grin]
$ThingList = #('One Thing', 'Two Thing', 'Three Thing')
$ThingList -join '; '
'=' * 20
$ThingList -join [System.Environment]::NewLine
output ...
One Thing; Two Thing; Three Thing
====================
One Thing
Two Thing
Three Thing
create 3 more arrays for the count (each array will be exported to a column), then find the array with the most count and generate a psobject for each line.
[array]$TPpl = #("T1" ,"T2", "T3")
[array]$TPpl_count = #($TPpl.Count)
[array]$RPpl = #("R1" ,"R2", "R3", "R4")
[array]$RPpl_count = #($RPpl.Count)
[array]$CPpl = #("C1" ,"C2", "C3", "C4","C5")
[array]$CPpl_count = #($CPpl.Count)
$leng = [array]$TPpl.Count,$RPpl.Count,$CPpl.Count
$max = ($leng | measure -Maximum).Maximum
$csv = for($i=0;$i -lt $max;$i++){
New-Object -TypeName psobject -Property #{
"TPeople" = $(if ($TPpl[$i]) { $TPpl[$i]})
"TPCount" = $(if ($TPpl_count[$i]) { $TPpl_count[$i]})
"RPeople" = $(if ($RPpl[$i]) { $RPpl[$i]})
"RPCount" = $(if ($RPpl_count[$i]) { $RPpl_count[$i]})
"CPeople" = $(if ($CPpl[$i]) { $CPpl[$i]})
"CPCount" = $(if ($CPpl_count[$i]) { $CPpl_count[$i]})
}
}
$csv | Export-Csv C:\Temp\test.csv -NoTypeInformation
result:
your final code should be:
$GPMem = Get-ADGroupMember -Identity security.group | Select-Object -ExpandProperty Name
[array]$TPpl = $GPMem | Where-Object {$_ -like "T*"}
[array]$RPpl = $GPMem | Where-Object {$_ -like "r*"}
[array]$CPpl = $GPMem | Where-Object {$_ -like "c*"}
[array]$TPpl_count = #($TPpl.Count)
[array]$RPpl_count = #($RPpl.Count)
[array]$CPpl_count = #($CPpl.Count)
$leng = [array]$TPpl.Count,$RPpl.Count,$CPpl.Count
$max = ($leng | measure -Maximum).Maximum
$csv = for($i=0;$i -lt $max;$i++){
New-Object -TypeName psobject -Property #{
"TPeople" = $(if ($TPpl[$i]) { $TPpl[$i]})
"TPCount" = $(if ($TPpl_count[$i]) { $TPpl_count[$i]})
"RPeople" = $(if ($RPpl[$i]) { $RPpl[$i]})
"RPCount" = $(if ($RPpl_count[$i]) { $RPpl_count[$i]})
"CPeople" = $(if ($CPpl[$i]) { $CPpl[$i]})
"CPCount" = $(if ($CPpl_count[$i]) { $CPpl_count[$i]})
}
}
$csv | Export-Csv C:\Temp\test.csv -NoTypeInformation

Converting CSV Column to Rows

I'm trying to create a PowerShell script that transpose a CSV file from column to rows.
I found examples of doing the opposite (converting row based CSV to column) but I found nothing on column to rows. My problem being that I don't know exactly how many column I'll have. I tried adapting the row to column to column to rows but unsuccessfully.
$a = Import-Csv "input.csv"
$a | FT -AutoSize
$b = #()
foreach ($Property in $a.Property | Select -Unique) {
$Props = [ordered]#{ Property = $Property }
foreach ($Server in $a.Server | Select -Unique){
$Value = ($a.where({ $_.Server -eq $Server -and
$_.Property -eq $Property })).Value
$Props += #{ $Server = $Value }
}
$b += New-Object -TypeName PSObject -Property $Props
}
$b | FT -AutoSize
$b | Out-GridView
$b | Export-Csv "output.csv" -NoTypeInformation
For example my CSV can look like this:
"ID","DATA1"
"12345","11111"
"54321","11111"
"23456","44444"
or this (number of column can vary):
"ID","DATA1","DATA2","DATA3"
"12345","11111","22222","33333"
"54321","11111",,
"23456","44444","55555",
and I would like the script to convert it like this:
"ID","DATA"
"12345","11111"
"12345","22222"
"12345","33333"
"54321","11111"
"23456","44444"
"23456","55555"
The trick is to query the members of the table to get the column names. Once you do that then the rest is straightforward:
function Flip-Table ($Table) {
Process {
$Row = $_
# Get all the columns names, excluding the ID field.
$Columns = ($Row | Get-Member -Type NoteProperty | Where-Object Name -ne ID).Name
foreach ($Column in $Columns) {
if ($Row.$Column) {
$Properties = [Ordered] #{
"ID" = $Row.ID
"DATA" = $Row.$Column
}
New-Object PSObject -Property $Properties
}
}
# Garbage collection won't kick in until the end of the script, so
# invoke it every 100 input rows.
$Count++;
if (($Count % 100) -eq 0) {
[System.GC]::GetTotalMemory('forceFullCollection') | out-null
}
}
}
Import-Csv input.csv | Flip-Table | Export-Csv -NoTypeInformation output.csv
Well, here is mine. I'm not as fancy as the rest:
$in = Get-Content input.csv | Select -Skip 1
$out = New-Object System.Collections.ArrayList
foreach($row in $in){
$parts = $row.Split(',')
$id = $parts[0]
foreach($data in $parts[1..$parts.Count]){
if($data -ne '' -AND $data -ne $null){
$temp = New-Object PSCustomObject -Property #{'ID' = $id;
'Data' = $data}
$out.Add($temp) | Out-Null
}
}
}
$out | Export-CSV output.csv -NoTypeInformation
You can do something like this
# Convert csv to object
$csv = ConvertFrom-Csv #"
"ID","DATA1","DATA2","DATA3"
"12345","11111","22222","33333"
"54321","11111",,
"23456","44444","55555"
"#
# Ignore common members and the ID property
$excludedMembers = #(
'GetHashCode',
'GetType',
'ToString',
'Equals',
'ID'
)
$results = #()
# Iterate around each csv row
foreach ($row in $csv) {
$members = $row | Get-Member
# Iterate around each member from the 'row object' apart from our
# exclusions and empty values
foreach ($member in $members |
Where { $excludedMembers -notcontains $_.Name -and $row.($_.Name)}) {
# add to array of objects
$results += #{ ID=$row.ID; DATA=$row.($member.Name)}
}
}
# Write the csv string
$outstring = "ID,DATA"
$results | foreach { $outstring += "`n$($_.ID),$($_.DATA)" }
# New csv object
$csv = $outstring | ConvertFrom-Csv
Probably not the most elegant solution, but should do what you need
I left some comments explaining what it does
If you only want to accept a limited number DATA columns (e.g. 5), you could do:
ForEach ($i in 1..5) {$CSV | ? {$_."Data$i"} | Select ID, #{N='Data'; E={$_."Data$i"}}}
And if you have a potential unlimited number of DATA columns:
ForEach ($Data in ($CSV | Select "Data*" -First 1).PSObject.Properties.Name) {
$CSV | ? {$_.$Data} | Select ID, #{N='Data'; E={$_.$Data}}
}

Check Multiple Computers if Path Exists and Export to CSV

I want to make a script that will check whether or not a directory exists on each computer in "computers.csv".
This is what I've come up with:
$results = #()
$computers = Get-Content "C:\Users\me\Desktop\Notes\Computers.csv"
foreach ($computer in $computers) {
$path = Test-Path "\\$computer\c$\Program Files\Folder\"
if ($path -eq $true)
$Output = "True"
else
$Output = "False"
}
$details = #{
Computer_Name = $computer
Output = $Output
}
$results += New-Object PSObject -Property $details
$results |
Select-Object -Property Computer_Name,Output |
Export-Csv c:\results.csv -NoTypeInformation
Script is failing and I'm not entirely sure why. I need the script to export to a CSV due to how many computers are being queried.
You've got several syntax errors. You're missing brackets with if and else, and your foreach closing bracket is in the wrong place. Try this:
$results = #()
$computers = Get-Content "C:\Users\me\Desktop\Notes\Computers.csv"
foreach ($computer in $computers) {
$path = Test-Path "\\$computer\c$\Program Files\Folder\"
If ($path -eq $true) {
$Output = "True"
}
Else {
$Output = "False"
}
$details = #{
Computer_Name = $computer
Output = $Output
}
$results += New-Object PSObject -Property $details
}
$results | select-object -property Computer_Name, Output | Export-csv c:\results.csv -NoTypeInformation
That said, this pattern is one that should be avoided:
$results = #()
foreach ($item in $set) {
$results += $item
}
$results
The problem is that $results += $item copies the entire array into a new array and then adds the new item. It's a huge overhead as the size of the array increases.
Try something like this instead:
Get-Content "C:\Users\me\Desktop\Notes\Computers.csv" | ForEach-Object {
[PSCustomObject]#{
Computer_Name = $_
Output = Test-Path "\\$_\c$\Program Files\Folder\"
}
} | Export-Csv -Path C:\results.csv -NoTypeInformation

Powershell Script assistance required

I'm piping a file's contents to Select-Object and creating two properties for each name, ComputerName and FileExists, where the latter value is the result of Test-Path
Get-Content c:\Users\Admin\Documents\Scripts\Serverlist.txt | `
Select-Object #{Name='ComputerName';Expression={$_}},#{Name='FolderExist';Expression={ Test-Path "\\$_\c$\Data\Repository"}},
#{Name='Size';Expression={$_.Sum}}
I want to return the size of this folder if it exists on each server. How would you do it ?
Tried adding
#{Name='Size';Expression={$_.Sum}}
to my select-object but that does not return any value
I think this is what you are looking for
Get-Content c:\Users\Admin\Documents\Scripts\Serverlist.txt | Select-Object #{Name='ComputerName';Expression={$_}},#{Name='FolderExist';Expression={ Test-Path "\\$_\c$\Data\Repository"}}, #{Name='Size';Expression={ "{0:n2}" -f ((gci -path "\\$_\c$\Data\Repository" -recurse | measure-object -property length -sum).sum /1mb) + " mb" }}
Since at least one calculated property depends on the other, you would have to do at least 2 Select-Object statements, but I would probably use ForEach-Object and New-Object instead:
Get-Content c:\Users\Admin\Documents\Scripts\Serverlist.txt | ForEach-Object {
New-Object psobject -Property #{
'ComputerName' = $_
'FileExists' = ($FileExists = Test-Path ($FilePath = "\\$_\c$\Data\repository"))
'Size' = if($FileExists){ (Get-Item $FilePath).Length } else { $null }
}
}
(Get-Item $FilePath).Length won't get you far if C:\Data\repository is a directory, but you can substitute your own statement inside the if block
For the sake of my future coworkers I would probably avoid doing a one-liner like your example, and write multiple statements like so:
Get-Content c:\Users\Admin\Documents\Scripts\Serverlist.txt | ForEach-Object {
$ComputerName = $_
$FilePath = "\\$ComputerName\c$\Data\repository"
$FileExists = Test-Path $FilePath
if($FileExists){
$Size = Get-Item $FilePath | Select-Object -ExpandProperty Length
} else {
$Size = $null
}
New-Object psobject -Property #{
'ComputerName' = $ComputerName
'FileExists' = $FileExists
'Size' = $Size
}
}

ForEach-Object piped to CSV is only showing last element

I'm trying to export to csv the scheduled tasks for multiple remote machines. I'm using a modified version of this script. I'm trying to export one csv per machine that lists all of the scheduled tasks. Currently my code just exports the last task for each machine.
foreach ($computerName in $computerNames) {
$Schedule.connect($computerName)
$AllFolders = Get-AllTaskSubFolders
foreach ($Folder in $AllFolders) {
if ($Tasks = $Folder.GetTasks(0)) {
$TASKS | % {[array]$results += $_}
$Tasks | Foreach-Object {
New-Object -TypeName PSCustomObject -Property #{
'Name' = $_.name
'Path' = $_.path
'Server' = $computername
} | Export-Csv $("C:\Users\MyName\Desktop\" + $computername + ".csv")
}
}
}
}
I've tried putting the Export-Csv at the end of each of the curly braces and none output what I want. Any ideas what I'm doing wrong?
EDIT
I'm not sure this fully addresses the issue, as some parts of the code seem weired to me such as $tasks = $folder.GetTasks. However OP title clearly says that the CSV is showing the last element meaning to me that his code mostly works except for the Export-CSV part.
Export-Csv expects an array as input, so the code below uses the elements in $task to generate the array of objects. Select-Object is there to decide in which order the properties are stored in the CSV.
Try this:
foreach ($computerName in $computerNames) {
$Schedule.connect($computerName)
$AllFolders = Get-AllTaskSubFolders
$result = #()
foreach ($Folder in $AllFolders) {
if ($Tasks = $Folder.GetTasks(0)) {
$TASKS | % {[array]$results += $_}
$Tasks | Foreach-Object {
$result += New-Object -TypeName PSCustomObject -Property #{
Name = $_.name;
Path = $_.path;
Server = $computername
}
}
}
}
$result | select Name, Path, Server | Export-Csv $($exportPath + $computername + ".csv")
}
This new version saves results on a per computer basis.
You are not changing either $exportPath or $computername in your loop, so each time through your outer loop, the csv file is being overwritten. Also, this
if ($Tasks = $Folder.GetTasks(0)) {
may be wrong. If you're trying to determine if $Tasks is equal to $Folder.GetTasks(0), you'd need this:
if ($Tasks -eq $Folder.GetTasks(0)) {