Powershell Adding items from text file to 2 dimensional array - powershell

I am used to coding in java and I am brand new to Powershell. I have a text file that contains Windows server info displayed like this.
14.0.3026.27,None,CU7,4229789,SQL Server 2017,0
14.0.3025.34,None,CU6,4101464,SQL Server 2017,0
14.0.3023.8,None,CU5,4092643,SQL Server 2017,0
I am trying to throw this info into a 2 dimensional array and want it to look like this.
[14.0.3026.27],[None],[CU7],[4229789],[SQL Server 2017],[0]
[14.0.3025.34],[None],[CU6],[4101464],[SQL Server 2017],[0]
[14.0.3023.8],[None],[CU5],[4092643],[SQL Server 2017],[0]
The code I have is giving this error message:
Cannot index into a null array. At line:9 char:9
+ $array[$i][$j] = $word
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Here is my code:
$file = Get-Content "C:\Users\UserName\Desktop\sqlServers.txt"
$array = #(), #()
$i = 0
$j = 0
foreach ($line in $file){
$j=0
foreach ($word in $line.split(",")){
$array[$i][$j] = $word
$j+=1
}
$i+=1
}

PowerShell (and .NET) arrays are fixed size so assigning to an element beyond the array bounds won't grow the array. Instead, let PowerShell build the arrays for you. The following will produce what you want (an array of arrays btw, not an actual 2-d array)
$result = get-content data.txt | foreach { , ($_ -split ',')}
In this code, reading the data will give you the rows, splitting the rows will give you the columns. The trick is the comma before the split operation. Without it, all of the elements would be streamed into a single flat array. The comma preserves the nested array so you get the desired array of arrays.

As your file is comma separated (it's a CSV with a .txt extension) you could instead use Import-Csv to create the array.
You will need to manually specify headers as you example input doesn't include them.
Code with example headers:
$array = Import-Csv "C:\folder\sqlServers.txt" -Header Version,Something,CU,Number,Product,Another
You can then reference the items by index and property name:
PS > $array[0].CU
CU7
PS > $array[2].Product
SQL Server 2017

Related

Trying to use += for Get-DistributionGroupMember results in empty variable [duplicate]

pracl is a sysinternal command that can be used to list the ACLs of a directory. I have a list of shares and I want to create a csv file such that for each ACL entry, I want the share path in one column and share permission in the next. I was trying to do that by using the following code
$inputfile = "share.txt"
$outputFile = "out.csv"
foreach( $path in Get-Content $inputfile)
{
$results=.\pracl.exe $path
{
foreach ($result in $results) {write-host $path,$line}
}
$objResult = [pscustomobject]#{
Path = $Path
Permission = $line
}
$outputArray += $objResult
$objresult
}
$outputArray | Export-Csv -Path $outputfile -NoTypeInformation
It failed with the following error :-
Method invocation failed because [System.Management.Automation.PSObject] does not contain a method named 'op_Addition'.
At C:\Users\re07393\1\sample.ps1:14 char:1
+ $outputArray += $objResult
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Any suggestions ?
You're trying to create an array of [pscustomobject]s in your $outputArray variable iteratively, using +=, but you're not initializing $outputArray as an array - see the bottom section for an explanation of the resulting behavior.
Thus, the immediate solution to your problem is to do just that:
# Do this before your `foreach` loop, then `+=` will work for appending elements.
$outputArray = #()
However, using += to add to arrays is inefficient, because in reality a new array instance must be created every time, because arrays are immutable data structures. That is, every time += is used, PowerShell creates a new array instance behind the scenes to which the existing elements as well as the new element are copied.
A simpler and much more efficient approach is to let PowerShell create an array for you, by using the foreach loop as an expression and assigning it to a variable as a whole:
That is, whatever is output in every iteration of the loop is automatically collected by PowerShell:
A simplified example:
# Create an array of 10 custom objects
[array] $outputArray = foreach ($i in 1..10) {
# Create and implicitly output a custom object in each iteration.
[pscustomobject] #{
Number = $i
}
}
Note the use of type constraint [array] to the left of $outputArray, which ensures that the variable value is always an array, even if the loop happens to produce just one output object (in which case PowerShell would otherwise just store that object itself, and not wrap it in an array).
Note that you can similarly use for, if, do / while / switch statements as expressions.
In all cases, however, these statements can only serve as expressions by themselves; regrettably, using them as the first segment of a pipeline or embedding them in larger expressions does not work - see GitHub issue #6817.
As for what you tried:
$outputArray += $objResult
Since you didn't initialize $outputArray before the loop, the variable is implicitly created in the loop's first iteration:
If the LHS variable doesn't exist yet, += is effectively the same as =: that is, the RHS is stored as-is in the LHS variable, so that $outputArray now contains a [pscustomobject] instance.
In the second iteration, because $outputArray now has a value, += now tries to perform a type-appropriate + operation (such as numeric addition for numbers, and concatenation for strings), but no + (op_Addition()) operation is defined for type [pscustomobject], so the operation fails with the error message you saw.

Why is the Export-Csv not working with PSCustomObject? [duplicate]

pracl is a sysinternal command that can be used to list the ACLs of a directory. I have a list of shares and I want to create a csv file such that for each ACL entry, I want the share path in one column and share permission in the next. I was trying to do that by using the following code
$inputfile = "share.txt"
$outputFile = "out.csv"
foreach( $path in Get-Content $inputfile)
{
$results=.\pracl.exe $path
{
foreach ($result in $results) {write-host $path,$line}
}
$objResult = [pscustomobject]#{
Path = $Path
Permission = $line
}
$outputArray += $objResult
$objresult
}
$outputArray | Export-Csv -Path $outputfile -NoTypeInformation
It failed with the following error :-
Method invocation failed because [System.Management.Automation.PSObject] does not contain a method named 'op_Addition'.
At C:\Users\re07393\1\sample.ps1:14 char:1
+ $outputArray += $objResult
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Any suggestions ?
You're trying to create an array of [pscustomobject]s in your $outputArray variable iteratively, using +=, but you're not initializing $outputArray as an array - see the bottom section for an explanation of the resulting behavior.
Thus, the immediate solution to your problem is to do just that:
# Do this before your `foreach` loop, then `+=` will work for appending elements.
$outputArray = #()
However, using += to add to arrays is inefficient, because in reality a new array instance must be created every time, because arrays are immutable data structures. That is, every time += is used, PowerShell creates a new array instance behind the scenes to which the existing elements as well as the new element are copied.
A simpler and much more efficient approach is to let PowerShell create an array for you, by using the foreach loop as an expression and assigning it to a variable as a whole:
That is, whatever is output in every iteration of the loop is automatically collected by PowerShell:
A simplified example:
# Create an array of 10 custom objects
[array] $outputArray = foreach ($i in 1..10) {
# Create and implicitly output a custom object in each iteration.
[pscustomobject] #{
Number = $i
}
}
Note the use of type constraint [array] to the left of $outputArray, which ensures that the variable value is always an array, even if the loop happens to produce just one output object (in which case PowerShell would otherwise just store that object itself, and not wrap it in an array).
Note that you can similarly use for, if, do / while / switch statements as expressions.
In all cases, however, these statements can only serve as expressions by themselves; regrettably, using them as the first segment of a pipeline or embedding them in larger expressions does not work - see GitHub issue #6817.
As for what you tried:
$outputArray += $objResult
Since you didn't initialize $outputArray before the loop, the variable is implicitly created in the loop's first iteration:
If the LHS variable doesn't exist yet, += is effectively the same as =: that is, the RHS is stored as-is in the LHS variable, so that $outputArray now contains a [pscustomobject] instance.
In the second iteration, because $outputArray now has a value, += now tries to perform a type-appropriate + operation (such as numeric addition for numbers, and concatenation for strings), but no + (op_Addition()) operation is defined for type [pscustomobject], so the operation fails with the error message you saw.

Appending data from one variable onto another in powershell

I've got two variables that have two separate list with exactly the same amount of lines:
$server = $agentserver.name
$type = $agentserver.agentservertype
Example data output
$server $type
server1 windows
server2 ndmp
I want to append the data from $type onto $server with a ","
so the output would look like this:
server1,windows
server2,ndmp
I have been trying to use foreach loops and increment one onto the other but I can't get it to work properly this is my code I am using:
$agentserver = foreach($i in $server){foreach($j in $type){$i + ($j++)}}
How can I get this to append properly in powershell?
so $agentserver is an object with the columns name and agentservertype - you can do the following:
$commaList = foreach($as in $agentserver) {
$as.name,$as.agentservertype -join ','
}

Not able to call out array headers in a foreach loop

Okay - this is a really weird issue - and it probably has a really stupid solution.
But i import a csv
$csv = import-csv c:\users\hello.csv
Then i have an array of words, for which i am wanting to use to search through the csv - and if there's a match in the csv - populate an adjacent column in the csv.
here's the array:
$newhandles
hi
hello
now - if i run a foreach loop with an if statement inside of it - it doesn't recognize one of the headers.
foreach ($newhandle in $newhandles)
{if ($csv.name -eq $newhandle) {$csv.redundant = $newhandle}}
however it gives me this error:
The property 'redundant' cannot be found on this object. Verify that the property exists
and can be set.
At line:1 char:69
+ ... andles) {if ($csv.name -eq $newhandle) {$csv.redundant = $newhandle}}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyAssignmentException
I know the property exists - because if i call it directly - it shows three empty slots - and if i call something to it directly - the element will populate. such as
> $csv[0].redundant = 'hi'
> $csv[0]
name : hi
description : don't settle
system : sight
redundant : hi
tags :
Any ideas?
try using this foreach loop :
foreach ($rec in $csv){
if($newhandles -contains $rec.name){
$rec.redundant = $rec.name
}
}
if you check ($csv.redundant).GetType(),you can see that it returns an array instead of the property you want but when you are assigning value to $csv[0].redundant you are accessing the exact property and that's why it works when you tested manually
try this
import-csv "c:\users\hello.csv" | select * , #{N="redundant";E={if ($newhandles -contains $_.Name) {$_.Name} else {$null}}} -ExcludeProperty redundant

pull text between characters

How do I pull text between two words? I know regex can do this and I have been looking around but the code I try just does not work for me at all...as clueless as a brick with regex...so probably am doing it totally wrong...
I have a text file and want to query whatever is displayed between these text strings:
[Problem Devices]
Device PNP Device ID Error Code
[USB]
I tried doing this but getting no where!
$devices = Get-Content c:\temp\dev.txt | out-string [regex]::match($devices,'(?<=\<Problem Devices\>).+(?=\<USB\>)',"singleline").value.trim()
You cannot call a method on a null-valued expression.
At line:1 char:141
+ $devices = Get-Content c:\temp\dev.txt | out-string [regex]::match($devices,'(?<=\<Problem Devices\>).+(?=\<USB\>)',"
singleline").value.trim <<<< ()
+ CategoryInfo : InvalidOperation: (trim:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Piping to out-string is not needed; get-content is sending each line of the file into the pipeline as a separate object. So you want to iterate through those objects with a foreach-object.
$devices = Get-Content c:\temp\dev.txt | foreach-object{[regex]::match($devices,'(?<=\<Problem Devices\>).+(?=\<USB\>)',"singleline").value.trim()}
However, you are still left with the problem of attempting to trim() a null object - if your regex match doesn't find a match, you can't call value.trim().
Your regex tries to match on <Problem Devices> when your input file has [Problem Devices].
Rather than try to do everything in a single set of pipeline steps, break your problem down:
For each line in the file, check for [Problem Devices]
For each subsequent line, if it is [USB], exit the loop. If it is not [USB], capturing each line into a variable (build an array of these lines)
After the loop, iterate over each element of the array you just built to parse each value out (creating a collection of PSObjects (one per device), or a collection of hashes (one per device), depending on your needs).
If you're not comfortable with regex, there are other ways:
$test = $false
$devices = get-content file.txt |
foreach {
if ($_.trim() -eq '[Problem Devices]'){$test = $true}
elseif ($_.trim() -eq '[USB]') {$test = $false}
elseif ($test){$_}
} | where {$_.trim()}