Writing value from variable to file - file

$info = Invoke-SQLCmd -ServerInstance $Server -Database $Database -inputfile "$queriespath/compare_quantity.sql"
$changes = $info.length
for($i=0; $i-le$changes; $i++) {
$text = "Revise,$info[$i].quantity,$info[$i].itemid" | Set-Content 'C:\Users\pmaho\Dropbox\SAMPOWERSPORTS\CustomChrome\revision.txt'
}
I'm trying to write the value of $info[$i].quantity,$info[$i].itemid to the file.
This is what my code outputs now
Revise,System.Data.DataRow System.Data.DataRow[2].quantity,System.Data.DataRow System.Data.DataRow[2].itemid
I want it to say the actual value of the varables how can I do this?
EDIT:
for($i=0; $i-le$changes; $i++) {
$text = $($info[$i].quantity) | Set-Content 'C:\Users\pmaho\Dropbox\SAMPOWERSPORTS\CustomChrome\revision.txt'
}
The above is printing nothing.

for($i=0; $i-le$changes; $i++) {
$text = "$(Revise,$info[$i].quantity,$info[$i].itemid)" | Set-Content 'C:\Users\pmaho\Dropbox\SAMPOWERSPORTS\CustomChrome\revision.txt'
}
The $() just tells powershell to interpret the expression contained in the parentheses, you still need the quotes to assign a string value to the variable.

You're running into an issue of String Expansion here.
Take this property, for instance:
$e = [psobject]#{Name='StackOverFlow';URL='http://stackoverflow.com'}
$e.URL
> http://stackoverflow.com
This works as expect. However, when I try to wrap the whole thing in quotes, look what happens:
"the URL of StackOverflow is $e.Url"
>the URL of StackOverflow is System.Collections.Hashtable.Url
Notice the lame 'lil .Url appended to the end there? PowerShell is doing String expansion for me, but it does this by scanning left to right and dropping in variable values for us, but it doesn't check to see if I'm really after a property or method name. It just thinks I see $e, let me replace it with what I've got for $e. Annoying!
So the easy way around this is to use the Order of Operation symbols (remember Please-Excuse-My-Dear-Aunt-Sally? Parenthesis, Exponent, Multiplication, Division, Addition, Subtraction) and just wrap the property references in parenthesis.
In my example then, I'd do this.
"the URL of StackOverflow is $($e.Url)"
> the URL of StackOverflow is http://stackoverflow.com
Going back to your question, try this instead:
"Revise,$($info[$i].quantity),$($info[$i].itemid)" |
Set-Content 'C:\Users\pmaho\Dropbox\SAMPOWERSPORTS\CustomChrome\revision.txt'

Related

How do I access properties of nested arrays in powershell within foreach loops?

I'm trying to write a script that checks whether a group of registry keys are set individually and then either sets them or modifies them conditional on thier current state.
The keys that I want to check/set contain a mix of Strings and DWORDs
I want to loop through an array that contains the key I want to set paired with the value.
I've tried as a hashtable/splatting but the input to Get-ItemProperty fails because of the value parameter so I tried basic arrays instead with no luck.
They are all at the same registry path but I was attempting to do something similar to this:
$Path = "HKLM:\Software\path\to\keys"
$Properties = (
('key', value),
('key2', value2),
('key3', 'value3')
)
foreach ($item in $Properties){
$exist = Get-ItemProperty -Path $Path -name $item[0]
if ($exist) {
Set-ItemProperty -Path $Path -Name $item[0] -Value $item[1]
} else {
New-ItemProperty -Path $Path -Name $item[0] -Value $item[1]
}
}
But no matter what I've tried I cannot retrieve the individual elements of the inner arrays.
I realize I could probably do this long-form and just do it line by line rather than attempting to iterate through, but this is definitely a more elegant way, and would be a great template if I need to do something similar in the future.
Holy moly. Nothing breaks you out of a rut like posting the question to an online forum.
The .GetValue() method is what I needed, not the raw index number.
...So $Item.GetValue(0) for the key name and $Item.GetValue(1) for the value.
Feel silly answering my own question but hopefully it helps someone else!

Possible to have different array names when write-output PSCustomObject]#{ in my function?

Is there any way to name the variable for an array something in my Catch { and then have another array name for the array inside Try/script part of my function?
Cause when i try doing like this $computerObject = [PSCustomObject]#{
and then doing Write-Output $computerArray i can only get either my variables inside Try/script array being displayed inside Powershell window. Or only get the $error message from my Catch.. Is there any way to name each array something so i can do like below.
write-host "Results"
Write-Output $computerArray - display my first array here
write-host "Failed: computerlist" -foregroundcolor red
Write-Output $computerArray2 - display $error computers here. $error should just include computers who did not answer to ping and other stuff from my invoke-command computerlist.txt
The only true answer to why i need this separately is that sometimes i want my array in a CSV file. And sometimes i just want to copy info directly from Powershell window. And then its more practical to have failed computers separated and not in the same array output.
This function (as mentioned in comments) doesn't leverage the CIM cmdlets parallel capabilities, would recommend some tweaks to it but to answer the actual question, how can you "split" the output between success and fail:
The function as-is, doesn't require any modification to achieve this, it's try and catch blocks are outputting objects with the same properties and luckily one of those properties is Error and it's value is a boolean so you can simply first query all the computers and then split the result using .Where with Split mode.
The code would be like this:
$computers = 'computer1', 'computer2', ....
$computerArray = foreach($computer in $computers) {
Get-ComputerInformation -ComputerName $computer
}
# now we can split between FAIL and SUCCESS
$fail, $success = $computerArray.Where({ $_.Error }, 'Split')
$success | Export-Csv path\to\success.csv -NoTypeInformation
$fail | Export-Csv path\to\fail.csv -NoTypeInformation

Extract partial line of text from text file

I need to keep the first 5 characters from data being pulled from a text file.
Data looks like this:
S1831KWT0081
S2004KWT0083
S2351KWT0085
S0054KWT0087
Results should looks like this:
S1831
S2004
S2351
S0054
I can get it working when setting the variable within PowerShell:
PS> $a = "S1831KWT0081"
PS> $a.Substring(0, $a.IndexOf('K'))
S1831
but I'm stuck when trying to pull from a text file.
To solve this, you will need to parse the text file on a line-by-line basis. Basically treating each line as a value in an array.
Get-Content location.txt | foreach { $_.Substring(0, $_.IndexOf('K')) }
Another option would be a regular expression replacement:
(Get-Content 'C:\path\to\input.txt') -replace '^(.{5}).*', '$1'
That would also allow you more specific matches, e.g. like this:
$re = '^([a-z]\d{4}).*'
(Get-Content 'C:\path\to\input.txt') -match $re -replace $re, '$1'
Just to show there always is more than one PoSh way ;-)
gc .\input.txt|%{$_.split('K')[0]}
Or the more verbose version
Get-Content .\input.txt |
ForEach-Object { $_.split('K')[0] }

Powershell: Assign properties to existing array

I have a single dimensional array that I get from either a get-content command or from multi-line text box input. I want to assign a property to the entries in this array, then add more properties to use later in my script.
Something like:
$items = new-object psobject
$items | add-member -membertype NoteProperty –name Name –value NotSet
$items | add-member -membertype NoteProperty –name Percent –value NotSet
$names = #($textboxInputText.Lines)
$names | % { $items | Add-Member noteproperty $_.Name $temp.($_.Name) }
foreach ($item in $items)
{
$percent = {script block}
$item.percent = $percent
}
I know this is broken code, but I wanted to give an example of where I was headed. I've searched far and wide but haven't been able to find exactly what I was looking for.
EDIT:
Code Goal: Get input from a text box or text file (single line entries). Have those entries be assigned to the "name" property, then add a second property to the array (Percent) that will need to be filled in with another block of code.
EDIT 2:
Collection is being used in the following code:
foreach ($item in $collection) {
$psConsoleFile = "PATH TO FILE.pc1"
$variable1 = "something"
$variable2 = "something else"
$command = ".`"Command1 $item.name | Command2 -Switch $variable1 -Switch2 $variable2`""
$OutputScriptBlock = "powershell.exe -PSConsoleFile $psConsoleFile -command $command"
}
The output of this is as follows:
powershell.exe -PSConsoleFile "PATH TO FILE.psc1" -command ."Command1 #{Name=name1; Percentage=}.name | Command2 -Switch1 something"
Why is the code outputting the full row instead of the name?
Also, I'm using PS 4.0 for all implementations of this script.
Ok, I see the problem here. So you have an array of Strings that you got either from a multi-line text box form object, or from a text document with the Get-Content command, but what you really want is an array of PSObjects.
A string object can not have additional properties added to it like you want (well, not conventionally, let's just not go there because you won't be happy with where things end up, trust me on this one). Instead let's take that array of strings, and for each string create a PSObject for it like you want. You will want a ForEach-Object loop for this to be simple. Either way you will want to pipe your input (either the textbox or the get-content command) to a ForEach loop, and you can assign the whole thing to a variable that will collect all of the objects to be worked with later (to update the Percent property). Something like this should accomplish what you want:
[Array]$Collection = $textboxInputText.Lines | ForEach{
New-Object PSObject -Property #{
'Name' = $_
'Percentage' = $null
}
}
I specified $Collection as the type [Array] so that if you wanted to index into it later there wouldn't be any issues should your input only be a single item. Then if you want to update the percentages you can do that by running $Collection through a ForEach loop (either inline or not)
$Collection | ForEach{ $_.Percentage = {Script Block} }
or
ForEach($Item in $Collection){
$Item.Percentage = {Script Block}
}
Now, things to note here... You are not going to be able to just assign $Collection back to your textbox. You could probably assign $Collection.Name, but that may require a newer version of PS since I don't know how backwards compatible that is. If you use a Get-Content command instead of referencing the textbox, simply change $textboxInputText.Lines | ForEach{ to Get-Content "C:\Path\To\File.txt" | ForEach{ and you should be all set.
Edit: Ok, the problem you have now isn't with the object but with how you're trying to expand a property of it within a double quotes. To access the name you would have to create a sub expression within the double quotes by wrapping $Item.Name within $(). So that line for you would look like:
$command = ".`"Command1 $($item.name) | Command2 -Switch $variable1 -Switch2 $variable2`""

Passing array to another script with Invoke-Command

I'm stuck trying to figure this out. I've seen the other articles and have tried everything, but I'm not getting anywhere. I am trying to pass an array as an argument to another PS script. Here's what I'm trying:
$IPArray = Get-Content C:\ListofIps.txt
Invoke-Command -Computername $server -Credential $cred -FilePath "C:\script.ps1" -ArgumentList (,$IPArray)
The 5 values in $IPArray aren't being passed to the script that I call.
Thanks in advance, guys, I really appreciate any help you can give...
Use:
... -ArgumentList (,$IPArray)
Because the ArgumentList parameter expects an array you've correctly noted that you need to wrap the array in another array. However, the correct syntax in this scenario (specifying a parameter value) is to use a grouping expression () to create the nested array.
This is an old question, but I'm going to re-iterate #keith-hill on the answer--add a comma at the beginning of the array declaration (even when including an existing array).
It's stupid, but I'm answering again because I tried all alternatives and that's the only thing that works--even with PowerShell 3.0+. You can use #require for anything from at least v3.0+, but nothing will work unless you do what #keith-hill suggests--even though it's an array, and the parameter is an array, PS sucks in this case (and I love PS)...do what he said (posting didn't work, so sorry but working answers are better):
\
... -ArgumentList (,$IPArray)
It makes no sense, but that works. Hands down to the PS team for dropping the bomb on this one, but if I hadn't done that my script would be null and void. And I've become the "scripting guy"...so here you go.
If you are just trying to pass one array, you can treat the $args variable itself as the array in your remote command and you get the same result as passing it as (, $IPArray) and then accessing that array in the scriptblock as $args[0]. I didn't test whether this works with multiple arrays or not.
That is to say,
$MyScriptBlock = { $args | % { Do-Stuff -Thing $PSItem } }
Invoke-Command -Session $MySession -ScriptBlock $MyScriptBlock -ArgumentList $IPArray
will return the same values as
$MyScriptBlock = { $args[0] | % { Do-Stuff -Thing $PSItem } }
Invoke-Command -Session $MySession -ScriptBlock $MyScriptBlock -ArgumentList (,$IPArray)

Resources