I have some pseudo code similar to the below
$funcValues = #(New-Object -TypeName psobject -Property #{'Function' = ''; 'Value' = '';})
function func1(){
while($i -lt 5){
$funcValues += #(New-Object -TypeName psobject -Property #{'Function' = 'func1'; 'Value' = $i;})
$i++
}
}
function func2(){
while($i -lt 3){
$funcValues += #(New-Object -TypeName psobject -Property #{'Function' = 'func2'; 'Value' = $i;})
$i++
}
}
func1
func2
$funcValues | Export-CSV C:\path\results.csv -noType
The goal is to have both functions add to the array and after calling the functions export the array to a csv. However when this code is inside a function, it doesn't write anything to the array, but if the code is outside a function, it works.
I'm guessing this has to do with variable scoping, but I'm very unfamiliar with how scoping works in powershell.
Your guess is correct. Try:
function func1(){
while($i -lt 5){
$script:funcValues += #(New-Object -TypeName psobject -Property #{'Function' = 'func1'; 'Value' = $i;})
$i++
}
}
Note that you are creating an array of arrays. If that's not what you wanted, then use:
$script:funcValues += New-Object -TypeName psobject -Property #{'Function' = 'func1'; 'Value' = $i;}
And if you are using V3, you can simplify some more:
$script:funcValues += [pscustomobject]#{'Function' = 'func1'; 'Value' = $i;}
One last comment on the code - using += on an array isn't very fast. Arrays can't be resized, so += will create a new array, copying the elements from the original array and adding the elements at the end. If the array is small, then the syntax is clear and convenient, but if the arrays get large and performance matters, you might consider using a different data structure like ArrayList.
Related
I want insert some AD attributes with PowerShell into a SQL table. So far so good:
$insert = #'
INSERT INTO [mdb].[dbo].[import](id,userid)
VALUES ('{0}','{1}')
'#
Try {
$connectionString = 'Data Source=serverdb;Initial Catalog=mdb;Integrated Security=SSPI'
$conn = New-Object System.Data.SqlClient.SqlConnection($connectionString)
$conn.Open()
$cmd = $conn.CreateCommand()
$counter = 0
Get-ADUser -Filter * -SearchBase "OU=company,DC=company,DC=state,DC=de" | Select #{Name="ID";Expression={ $global:counter; $global:counter++ }},SamAccountName |`
ForEach-Object {
$cmd.CommandText = $insert -f $counter,$_.SamAccountName
$cmd.ExecuteNonQuery()
}
$conn.Close()
}
Catch {
Throw $_
}
The output from get-ADUser is right, but the insert throws an error, that the primary key has duplicates. The incrementing must be wrong.
Can anybody help? THANKS!
ID is starting from 1 and not 0, is this normal ?
Furthermore, why are you creating an user defined property (ID) and don't use it ?
You can avoid the ugly global scope too (starting counter from 0 here) :
#### $counter = 0 <--- No more usefull
Get-ADUser -Filter * -SearchBase "OU=company,DC=company,DC=state,DC=de" | Select SamAccountName |`
ForEach-Object -Begin { $counter = 0 } -Process {
$cmd.CommandText = $insert -f $counter,$_.SamAccountName
$cmd.ExecuteNonQuery()
$counter++
}
I am not an SQL specialist, so this is probably not an anwser. But, you have here a nicer code ;)
You should always use parameters when inserting data with SQL. (Why?) In short: It's more secure, more performant, more robust and easier to use.
Put parentheses around the operation (++$counter) to return the value after increasing it. (Use $counter++ if you want zero-based ids, ++$counter if you want 1-based ids.)
$cmd.CommandText = "
INSERT INTO [mdb].[dbo].[import](id,userid)
VALUES (#id, #userId)
"
$counter = 0
Get-ADUser -Filter * -SearchBase "OU=company,DC=company,DC=state,DC=de" | foreach {
$cmd.Parameters.Clear()
$cmd.Parameters.AddWithValue("id", (++$counter))
$cmd.Parameters.AddWithValue("userId", $_.SamAccountName)
$cmd.ExecuteNonQuery()
}
I'm using Powershell to query the _Validation table from an MSI Code as follows...
$Script:WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer
$Script:MSIDatabase =
$WindowsInstaller.GetType().InvokeMember("OpenDatabase",
"InvokeMethod", $null, $WindowsInstaller, #($MSIPath, 0))
$Query = "SELECT Table, Column FROM _Validation"
$Script:View = $MSIDatabase.GetType().InvokeMember("OpenView",
"InvokeMethod", $null, $MSIDatabase, ($Query))
$Script:View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View,
$null)
$Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null,
$View, $null)
$ValidationTable =#()
While($Record -ne $null){
$Col1 = $Record.GetType().InvokeMember("StringData", "GetProperty",
$null, $Record, 1)
$Col2 = $Record.GetType().InvokeMember("StringData", "GetProperty",
$null, $Record, 2)
$ValidationTable += New-Object PsObject -Property #{Table = $Col1;
Column = $Col2}
$Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null,
$View, $null)
}
}
Now everything is working except for when I use "Table" in the query. From research I understand it may be an escape word in SQL so I've tried wrapping it in ` I've tired [ I've tried _Validation.Table and nothing works.
As I'm not good with SQL can anyone give me a hand?
Many thanks
Holy badger... the issue wasn't with the query it was with the fact i wrapped the query in double quotes not single quotes...
$Query = 'SELECT `Table`, Column FROM _Validation'
Works great.
I'm trying to change a variable inside a ScriptBlock.
What am I doing wrong?
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load($reader)
$Window.Add_SourceInitialized( {
$timer = new-object System.Windows.Threading.DispatcherTimer
$timer.Interval = [TimeSpan]"0:0:0.25"
$timer.Add_Tick( $updateBlock )
$timer.Start()
} )
$count = 0
$updateBlock = { Write-Host $count; $count++; Write-Host $count}
The Output is a repeating sequence of 0 and 1. So how do I access the variable and not only a copy of it?
When you modify $count inside the scope of the ScriptBlock, a local copy is created, and the original $Count variable in the parent scope is left untouched.
There are a few ways to modify $count in the parent scope, either with an explicit scope qualifier:
$updateBlock = { Write-Host $count; $script:count++; Write-Host $count}
Or by retrieving the variable with Get-Variable and the relative -Scope parameter (-Scope 1 refers to the immediate parent scope):
$updateBlock = { Write-Host $count; (Get-Variable -Scope 1 -Name count).Value++; Write-Host $count}
Or (as pointed out by #PetSerAl), use the [ref] keyword:
$updateBlock = { Write-Host $count; ([ref]$count).Value++; Write-Host $count}
So I got this code :
$t1, $t2, $t3, $t4, $t5, $t6, $t7, $t8, $t9, $t10, $t11 = $list[0].split(" ")
but is too much, and I think I can automate the creation of $t variables.
How is the correct procedure? with loop but to have the same result.
for ($i=0;$i -le 21; $i++) {
$objResult += New-Object -TypeName PSObject -Property #{"C$i" = ($list[0].split(" "))[$i]}
}
or with
set-variable -name "T$i" -value ($list[0].split(" "))[$i]
If these are simple variables, Set-Variable works perfectly. like so:
for ($i = 0; $i -lt 3; $i++) {
set-variable -name "test$i" -value $i
}
$test0
$test1
$test2
PS > .\test.ps1
0
1
2
If you are looking to work with somewhat more complicated variables (such as COM objects for instance) then New-Object is what you need.
I have this code in PowerShell, that executes SQL query to UPDATE my table:
$Connection=new-object data.sqlclient.sqlconnection "server=server;database=mydb;trusted_connection=true;"
$Connection.open()
For ( $i = 0; $i -le $ActID.Length; $i ++ ) {
$cmd = New-Object System.Data.SqlClient.SqlCommand
$cmd.Connection = $Connection
$cmd.CommandText =
"
update Table
set Note = #PATH
"
$cmd.Parameters.Add("#PATH", $ActID[$i].Values) | Out-Null
$cmd.ExecuteNonQuery()
}
I tried to update the table with the variable defined in this string:
$cmd.Parameters.Add("#PATH", $ActID[$i].Values) | Out-Null
But when I execute the script the error log says that there is no value passed in $ActID[$i]
Are there other methods to pass parameters (variables) in powershell queries?
What could be the mistake:
$i -le $ActID.Length;
it should be probably
$i -lt $ActID.Length;
You could also use piping which simplifies the code:
$actId | % { ..... $cmd.Parameters.Add("#PATH", $_.Values) | Out-Null .... }
Besides that the property you use is Values - is it really what you wanted? Values looks like a collection of something. Maybe you wanted to use a single value.