How do I loop through a dataset in PowerShell? - powershell

I am trying to output values of each rows from a DataSet:
for ($i=0;$i -le $ds.Tables[1].Rows.Count;$i++)
{
Write-Host 'value is : ' + $i + ' ' + $ds.Tables[1].Rows[$i][0]
}
gives the output ...
value is : +0+ +System.Data.DataSet.Tables[1].Rows[0][0]
value is : +1+ +System.Data.DataSet.Tables[1].Rows[1][0]
value is : +2+ +System.Data.DataSet.Tables[1].Rows[2][0]
value is : +3+ +System.Data.DataSet.Tables[1].Rows[3][0]
value is : +4+ +System.Data.DataSet.Tables[1].Rows[4][0]
value is : +5+ +System.Data.DataSet.Tables[1].Rows[5][0]
value is : +6+ +System.Data.DataSet.Tables[1].Rows[6][0]
How do I get the actual value from the column?

The PowerShell string evaluation is calling ToString() on the DataSet. In order to evaluate any properties (or method calls), you have to force evaluation by enclosing the expression in $()
for($i=0;$i -lt $ds.Tables[1].Rows.Count;$i++)
{
write-host "value is : $i $($ds.Tables[1].Rows[$i][0])"
}
Additionally foreach allows you to iterate through a collection or array without needing to figure out the length.
Rewritten (and edited for compile) -
foreach ($Row in $ds.Tables[1].Rows)
{
write-host "value is : $($Row[0])"
}

Here's a practical example (build a dataset from your current location):
$ds = new-object System.Data.DataSet
$ds.Tables.Add("tblTest")
[void]$ds.Tables["tblTest"].Columns.Add("Name",[string])
[void]$ds.Tables["tblTest"].Columns.Add("Path",[string])
dir | foreach {
$dr = $ds.Tables["tblTest"].NewRow()
$dr["Name"] = $_.name
$dr["Path"] = $_.fullname
$ds.Tables["tblTest"].Rows.Add($dr)
}
$ds.Tables["tblTest"]
$ds.Tables["tblTest"] is an object that you can manipulate just like any other Powershell object:
$ds.Tables["tblTest"] | foreach {
write-host 'Name value is : $_.name
write-host 'Path value is : $_.path
}

The parser is having trouble concatenating your string. Try this:
write-host 'value is : '$i' '$($ds.Tables[1].Rows[$i][0])
Edit: Using double quotes might also be clearer since you can include the expressions within the quoted string:
write-host "value is : $i $($ds.Tables[1].Rows[$i][0])"

Related

How to Extract the variable values from terraform variables.tf file using PowerShell commands

I have a variables.tf file which contains all the project variables and im trying to fetch a variable values using PowerShell.
variables.tf
variable "products" {
default = [
"Product-1",
"Product-2",
"Product-3",
"Product-4"
]
}
variable "product_unified_container" {
default = [
"cont-data",
"cont-data-2"
]
}
variable "location" {
default = "westeurope"
}
Using PowerShell i need to be able to fetch the variable values for any variable I want.
Example : the command should give me a array of all the products variables in variables.tf if it has multiple values.
write-host $product_list
Product-1
Product-2
Product-3
Product-4
if the variable has one value then it should give me that value like "location" variable.
write-host $deployed_location
westeurope
I was going through a similar problem so, I can share a way using which you can extract the values.
The problem is it is easy to extract and manipulate values in a json or other format but in tf files it is not the same. So, I have basically used a workaround where I have to set the given file in a structure that the values are filled in one single line
So, variables.tf will look
variable "products" {
default = ["Product-1", "Product-2", "Product-3", "Product-4"]
}
variable "product_unified_container" {
default = ["cont-data","cont-data-2"]
}
variable "location" {
default = "westeurope"
}
Next comes the PS code to extract the values of variables-
$paramsArray = #()
[system.array]$params = Select-String -Path "variables.tf" -Pattern "default =" -SimpleMatch
if (!([string]::IsNullOrEmpty($Params)))
{
[system.array]$paramsStrings = $params -split {$_ -eq "="}
foreach ($paramString in $paramsStrings)
{
if (($paramString -match "TF-Template") -or ($paramString -match "tf:"))
{
#Write-Output $paramString
}
else
{
If ($paramsArray -notcontains $paramString)
{
$paramsArray+=$paramString
}
}
}
}
write-host $paramsArray
The output generated is-
Since this is an array you can iterate and use it later in the script.
I was able to solve this by below approach hope this will help someone with similar requirement.
Following Command will fetch any variable values present in variables.tf file, in this case variable "products" and assign it to another array variable.
$product_list = (Get-Content "variables.tf" -Raw) | Foreach-Object {
$_ -replace '(?s)^.*products', '' `
-replace '(?s)variable.+' `
-replace '[\[\],"{}]+' `
-replace ("default =","") | Where { $_ -ne "" } | ForEach { $_.Replace(" ","") } | Out-String | ForEach-Object { $_.Trim() }
}
foreach ( $Each_Product in $product_list.Split("`n")) {
write-host "Each Product Name : "$Each_Product
}
Output :
Each Product Name : Product-1
Each Product Name : Product-2
Each Product Name : Product-3
Each Product Name : Product-4

Match Unique Value in Column and put all values on that row in to variables

I'm working with a CSV with 5 Columns, One of the Columns has unique Values.
Fruit, Number, Car, item, color
apple, 2, Chevy, ball, blue
apple, 1, Ford, ball, green
orange, 3, Ford, string, "red,green"
orange, 5, Mazda, key, red
Banana, 4, Tesla, desk, yellow
I need to search for 3 and have it return orange ford string "red,green" as their own variable
i.e. $fruit1 becomes orange $car1 becomes ford $item becomes string and $color bcomes red,green
I can do the search and have it tell me it found 3, but it still just puts runs everything through $fruit1 and if I tell it to write $fruit1 to a file it just get a repeating mess
I Need to Get output to a TXT file like so
for #3
FRUIT=orange
Car=Ford
ITEM = string
COLOR ="red,green"
whith each value in a different part of the file/newline
I can't post from the machine the script is on. So values changed to match my example
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}
LogWrite "Started execution of script.ps1"
$masterlist = Import-Csv ($filepath + "\" + "masterlistfile.csv" )
$FruitName = #()
$NumberName = #()
$Carname = #()
$ItemName = #()
$Colorname = #()
$masterlist |ForEach-Object {
$FruitName += $_.fruit
$NumberName += $_.number
$Carname += $_.car
$Itemname += $_.item
$Colorname += $_.color
}
$number = 3
$FruitIdentified
$CarIdentified
$ItemIdentified
$ColorIdentified
LogWrite "NUmber $number to be searched in masterlistfile "
if ($NumberName -eq $number)
{
LogWrite "Number found in the list..."
$Where = [array]::IndexOf($NumberName, $number)
LogWrite "Fruit Name : $FruitrName[$Where] "
$FruitIdentified = $FruitName[$Where]
$CarIdentified = $CarName[$Where]
$ItemIdentified = $ItemName[$Where]
}
You can use the following to read your CSV and then export the result with your expected output:
$number = 3
Import-Csv path/to/csv.csv | ForEach-Object {
if($_.number -eq $number) {
"for #$number"
foreach($prop in $_.PSObject.Properties.Name -ne 'Number') {
'{0}={2}{1}{2}' -f $prop, $_.$prop, ($null, '"')[$prop -eq 'color']
}
}
} | Set-Content path/to/file.ext
Note that Set-Content will overwrite the export file, if you want to append you would use Add-Content as in your function.
To give some context on what the code does:
Read the CSV and convert it to an object with Import-Csv
Loop over all objects and filter where the value of the Number property is equal to $number.
Output for #$number, in this example would be for #3".
Get all properties of the object using PSObject.Properties.Name and exclude the Number property using -ne 'Number'.
Loop over the Property Names and output '{0}={1}' -f $prop, $_.$prop, here we use the Format Operator -f, {0} would be the Property Name and {1} would be the Property Value. {2} will wrap the value with ".." if the Property Name is color.
The output you would be getting using your CSV for input would be:
for #3
Fruit=orange
Car=Ford
item=string
color="red,green"

PowerShell read column value from csv file [duplicate]

This question already has answers here:
How can you use an object's property in a double-quoted string?
(5 answers)
Closed 1 year ago.
This is my below input data which is in CSV format and roles,admin, accountant and security are columns.
roles, admin,accountant,security
Engineer, ,x , ,
I want to get value of rows using columns with below code, example , foreach for accountant column should return 'x', but I am getting something else.
$path = "$PSScriptRoot\Test.csv"
$csv = Import-Csv -path $path -Delimiter ","
$columns = $csv | Get-member -MemberType 'NoteProperty' | Select-Object -ExpandProperty 'Name'
$csv | ForEach-Object {
$row = $_
foreach ($col in $columns) {
Write-Host " vaalue of scripting is : $col "
Write-Host " vaalue of scripting row is : $row.$col "
Start-Sleep -s 10
}
}
Output I get is
vaalue of scripting is : accountant
vaalue of scripting row is : #{roles=Engineer; admin=; accountant=x ; security=}.accountant
vaalue of scripting is : admin
vaalue of scripting row is : #{roles=Engineer; admin=; accountant=x ; security=}.admin
vaalue of scripting is : roles
vaalue of scripting row is : #{roles=Engineer; admin=; accountant=x ; security=}.roles
How can I get 'x' for accountant column or any other column value using
Following from my comment, short answer was to use the Subexpression operator $( ), which would allow PowerShell to reference the property $col of the object $row.
Short extract from MS Docs:
Use this when you want to use an expression within another expression. For example, to embed the results of command in a string expression.
To give you a short explanation of why this is needed, using this as an example:
$object = [pscustomobject]#{
foo = 'var'
}
$property = 'foo'
When we do "$object.$property" or in simple terms, "$object.foo", the double quotes "..." are not allowing PowerShell to reference the foo property from $object because the dot . is interpreted as literal and not as a dot method. In addition, the quotes are converting $object to its stringified ? representation #{foo=var} followed by the literal dot . followed by the variable expansion of $property.
Another extract from about_Properties:
The most common way to get the values of the properties of an object is to use the dot method. Type a reference to the object, such as a variable that contains the object, or a command that gets the object. Then, type a dot (.) followed by the property name.
Lastly, what other alternatives do we have to get around this besides $(...):
String Formatting and String.Format method:
'Value of $object.$property is "{0}".' -f $object.$property
[string]::Format('Value of $object.$property is "{0}".', $object.$property)
Using + to concatenate strings is also a very known one:
'Value of $object.$property is "' + $object.$property + '".'
As a side note, and unrelated to the actual issue, this might be a more direct way of approaching your code:
#'
roles,admin,accountant,security
Engineer,,x,,
Operator,,y,,
'# |
ConvertFrom-Csv | ForEach-Object -Begin { $i = 1 } -Process {
foreach($Property in $_.PSObject.Properties.Name)
{
'Value of Row {0} Column "{1}" is "{2}"' -f
$i, $Property, (
'NULL', ($val = $_.$Property)
)[[int][bool]$val]
}
$i++
}
Note the use of .PSObject to access the object's properties and methods, an alternative to Get-Member.
The above would result in:
Value of Row 1 Column "roles" is "Engineer"
Value of Row 1 Column "admin" is "NULL"
Value of Row 1 Column "accountant" is "x"
Value of Row 1 Column "security" is "NULL"
Value of Row 2 Column "roles" is "Operator"
Value of Row 2 Column "admin" is "NULL"
Value of Row 2 Column "accountant" is "y"
Value of Row 2 Column "security" is "NULL"
I wrote this based on your csv file, let me know if it worked (change the folder path and file name).
$folderspath = 'C:\Test'
$csvfilename = 'info.csv'
$csvfilepath = $folderspath + "\" + $csvfilename
$csvfilepath = $csvfilepath.ToString()
$csvfile = Import-CSV -Path $csvfilepath -Delimiter ","
ForEach ($row in $csvfile) {
IF($row.security -eq "High") {
$Roles = $row.roles
$Admin = $row."admin"
$Accountant = $row.accountant
$Security = $row."security"
Write-Host "Roles: " $Roles "; Admin:" $Admin "; Accountant:" $Accountant "; `
Security:" $Security
}
}
The csv file I used
roles, admin,accountant,security
Engineer, ,x , ,
Engineer2,Yes ,x ,High,
Engineer3, No, , Low,

Concatenating a variable and a string literal without a space to an array using PowerShell

I'm trying add to a variable and a string in an array dynamically but i'm not getting expected output.
(1) I'm getting env name
(2) Concatinating the string and variable in an array
Code is as follows.
$env = $env:COMPUTERNAME.Substring(0,2)
$servers = { $env+"server1.test.com",$env+"server2.test.com" }
$serverCount = $servers -split(",") | measure | % { $_.Count }
For ($i=0; $i -lt $serverCount; $i++)
{
$ServerName = $servers -split(',') -replace '\[\d+\]'
$server = $ServerName[$i]
Write-Host $server
}
output i'm getting as
$env+"server1.test.com"
$env+"server2.test.com"
Values are not getting concatenated properly and variable value is not getting displayed. Any help.
$servers = { $env+"server1.test.com",$env+"server2.test.com" }
This is a scriptblock, not an array. {} is like a function, you have to run it for it to do anything (such as evaluating $env).
When you force it into a string using -split(",") what you get is text representation of the source code in the scriptblock, including the variable names.
As #Olaf comments, the right way to create an array of names is
$servers = ($env + "server1.test.com"), ($env + "server2.test.com")
This might be how I'd write it:
$env = $env:COMPUTERNAME.Substring(0,2)
"server1.test.com", "server2.test.com" | foreach-object {
"$env$_" -replace '\d+'
}

Why is an empty PowerShell pipeline not the same as null?

I am trying to understand the behavior of the #() array constructor, and I came across this very strange test.
It seems that the value of an empty pipeline is "not quite" the same as $null, even though it is -eq $null
The output of each statement is shown after the ###
$y = 1,2,3,4 | ? { $_ -ge 5 }
$z = $null
if ($y -eq $null) {'y is null'} else {'y NOT null'} ### y is null
if ($z -eq $null) {'z is null'} else {'z NOT null'} ### z is null
$ay = #($y)
$az = #($z)
"ay.length = " + $ay.length ### ay.length = 0
"az.length = " + $az.length ### az.length = 1
$az[0].GetType() ### throws exception because $az[0] is null
So the $az array has length one, and $az[0] is $null.
But the real question is: how is it possible that both $y and $z are both -eq $null, and yet when I construct arrays with #(...) then one array is empty, and the other contains a single $null element?
Expanding on Frode F.'s answer, "nothing" is a mostly magical value in PowerShell - it's called [System.Management.Automation.Internal.AutomationNull]::Value. The following will work similarly:
$y = 1,2,3,4 | ? { $_ -ge 5 }
$y = [System.Management.Automation.Internal.AutomationNull]::Value
PowerShell treats the value AutomationNull.Value like $null in most places, but not everywhere. One notable example is in a pipeline:
$null | % { 'saw $null' }
[System.Management.Automation.Internal.AutomationNull]::Value | % { 'saw AutomationNull.Value' }
This will only print:
saw $null
Note that expressions are themselves pipelines even if you don't have a pipeline character, so the following are roughly equivalent:
#($y)
#($y | Write-Output)
Understanding this, it should be clear that if $y holds the value AutomationNull.Value, nothing is written to the pipeline, and hence the array is empty.
One might ask why $null is written to the pipeline. It's a reasonable question. There are some situations where scripts/cmdlets need to indicate "failed" without using exceptions - so "no result" must be different, $null is the obvious value to use for such situations.
I've never run across a scenario where one needs to know if you have "no value" or $null, but if you did, you could use something like this:
function Test-IsAutomationNull
{
param(
[Parameter(ValueFromPipeline)]
$InputObject)
begin
{
if ($PSBoundParameters.ContainsKey('InputObject'))
{
throw "Test-IsAutomationNull only works with piped input"
}
$isAutomationNull = $true
}
process
{
$isAutomationNull = $false
}
end
{
return $isAutomationNull
}
}
dir nosuchfile* | Test-IsAutomationNull
$null | Test-IsAutomationNull
The reason you're experiencing this behaviour is becuase $null is a value. It's a "nothing value", but it's still a value.
PS P:\> $y = 1,2,3,4 | ? { $_ -ge 5 }
PS P:\> Get-Variable y | fl *
#No value survived the where-test, so y was never saved as a variable, just as a "reference"
Name : y
Description :
Value :
Visibility : Public
Module :
ModuleName :
Options : None
Attributes : {}
PS P:\> $z = $null
PS P:\> Get-Variable z | fl *
#Our $null variable is saved as a variable, with a $null value.
PSPath : Microsoft.PowerShell.Core\Variable::z
PSDrive : Variable
PSProvider : Microsoft.PowerShell.Core\Variable
PSIsContainer : False
Name : z
Description :
Value :
Visibility : Public
Module :
ModuleName :
Options : None
Attributes : {}
The way #() works, is that it guarantees that the result is delievered inside a wrapper(an array). This means that as long as you have one or more objects, it will wrap it inside an array(if it's not already in an array like multiple objects would be).
$y is nothing, it's a reference, but no variable data was stored. So there is nothing to create an array with. $z however, IS a stored variable, with nothing(null-object) as the value. Since this object exists, the array constructor can create an array with that one item.