Adding Left-Padding to PowerShell Out-String - powershell

I'm trying to making a small library of PowerShell functions to make things easier for myself and my team. For example:
$toReturn=Get-ADUser $name -Properties msRTCSIP-UserEnabled| Select msRTCSIP-UserEnabled | Format-List | Out-String
Where the output is like:
msRTCSIP-UserEnabled : True
Now, all I want to do is add a few empty spaces before the output (msRTCSIP-UserEnabled).
Basically, I want to add a certain amount of spaces to any Out-String.
Note: I'm not pulling just one value, its more like a bunch of them, so the output looks more like:
msRTCSIP-UserEnabled : True
msRTCSIP-DeploymentLocator : SRV:
msRTCSIP-PrimaryUserAddress : SIP:XXXX#XX.com
userPrincipalName : XXXX#XX.com
msRTCSIP-InternetAccessEnabled : True
msRTCSIP-FederationEnabled : True
Terminal Output
I'd like to indent the whole output a little more to the right in the terminal.

There are multiple ways of doing what you want. For example:
$data = #(
"msRTCSIP-UserEnabled : True",
"msRTCSIP-DeploymentLocator : SRV:",
"msRTCSIP-PrimaryUserAddress : SIP:XXXX#XX.com",
"userPrincipalName : XXXX#XX.com",
"msRTCSIP-InternetAccessEnabled : True",
"msRTCSIP-FederationEnabled : True"
)
$data | %{ "`t" + $_ } | Out-String
$data | %{ " {0}" -f $_} | Out-String
$data | %{ [string]::Format(" {0}", $_) }

Related

Format-Table out of Two Lists?

I've had this idea about getting the output from 2 separate functions, that return a PSCustomObject as a list, and formatting them into one table. My problem is simple... I don't know how to do it. lol
With the various of combinations that I tried, here's whats given me some promising results:
$Var1 = [PSCustomObject]#{
UserName = $env:USERNAME
Stuff1 = 'stuff1'
} | Format-List | Out-String -Stream
$Var2 = [PSCustomObject]#{
ComputerName = $env:COMPUTERNAME
Stuff2 = 'stuff2'
} | Format-List | Out-String -Stream
[PSCustomObject]#{
TableOne = $Var1.Trim().Foreach({$_})
TableTwo = $Var2.Trim()
} | Format-Table -AutoSize
The output:
TableOne TableTwo
-------- --------
{, , UserName : Abraham, Stuff1 : stuff1...} {, , ComputerName : DESKTOP-OEREJ77, Stuff2 : stuff2...}
I say promising in the respect that it shows the actual content of $var1 and 2, whereas my other attempts didn't. I also left the .foreach() operator there to show one of the many many different tricks I tried to get this working. For a quick second I thought the Out-String cmdlet would've done the trick for me, but was unsuccessful.
Has anyone ever done something similar to this?
EDIT:
Nevermind, I figured it out.
Used a for loop to iterate through each line assigning it the the PSCustomObject one at a time. Also used the .Where() operator to remove white spaces, and compared the two arrays to find the largest number to use it as the count.
$Var1 = $([PSCustomObject]#{
UserName = $env:USERNAME
Stuff1 = 'stuff1'
} | Format-List | Out-String -Stream).Where{$_ -ne ''}
$Var2 = $([PSCustomObject]#{
ComputerName = $env:COMPUTERNAME
Stuff2 = 'stuff2'
ExtraStuff = 'More'
} | Format-List | Out-String -Stream).Where{$_ -ne ''}
$Count = ($Var1.Count, $Var2.Count | Measure-Object -Maximum).Maximum
$(for($i=0;$i -lt $Count; $i++) {
[PSCustomObject]#{
TableOne = $Var1[$i]
TableTwo = $Var2[$i]
}
}) | Format-Table -AutoSize
Output:
TableOne TableTwo
-------- --------
UserName : Abraham ComputerName : DESKTOP-OEREJ77
Stuff1 : stuff1 Stuff2 : stuff2
ExtraStuff : More
It's an interesting way to format two collections with corresponding elements.
To indeed support two collections with multiple elements, a few tweaks to your approach are required:
# First collection, containing 2 sample objects.
$coll1 =
[PSCustomObject] #{
UserName = $env:USERNAME
Stuff1 = 'stuff1'
},
[PSCustomObject] #{
UserName = $env:USERNAME + '_2'
Stuff1 = 'stuff2'
}
# Second collection; ditto.
$coll2 =
[PSCustomObject] #{
ComputerName = $env:COMPUTERNAME
Stuff2 = 'stuff2'
ExtraStuff = 'More'
},
[PSCustomObject]#{
ComputerName = $env:COMPUTERNAME + '_2'
Stuff2 = 'stuff2_2'
ExtraStuff = 'More_2'
}
# Stream the two collections in tandem, and output a Format-List
# representation of each object in a pair side by side.
& {
foreach ($i in 0..([Math]::Max($coll1.Count, $coll2.Count) - 1)) {
[PSCustomObject] #{
TableOne = ($coll1[$i] | Format-List | Out-String).Trim() + "`n"
TableTwo = ($coll2[$i] | Format-List | Out-String).Trim() + "`n"
}
}
} | Format-Table -AutoSize -Wrap
The above ensures that multiple objects are properly placed next to each other, and yields something like the following:
TableOne TableTwo
-------- --------
UserName : jdoe ComputerName : WS1
Stuff1 : stuff1 Stuff2 : stuff2
ExtraStuff : More
UserName : jdoe_2 ComputerName : WS1_2
Stuff1 : stuff2 Stuff2 : stuff2_2
ExtraStuff : More_2

Format-Table for array of hashtables

I am trying to use the Format-Table command to output an array of hash tables of all files checked out from our TFS repo.
My code thus far:
$arr = #();
#Take the string from the tf command, parse it and build an array of hash tables
(tf stat /recursive /user:* /format:detailed | Select-String -Pattern '^\$' -NotMatch | Select -SkipLast 3 | Out-String) -split '(\r\n){2}' | ForEach-Object {
$ht = #{};
if ($_ -ne '') {
$str = $_ | Out-String;
$str -split '\r?\n'| ForEach-Object {
$key, $value = $_ -split '\s*:\s*';
#Write-Host $key, $Value;
try {
$ht.Add($key, $value);
} catch [ArgumentException] {
Write-Host "Caught exception";
}
}
$arr += ($ht);
}
}
Edit
Looks like I'm erroring out here.
$arr.ForEach({[PSCustomObject]$_}) | Format-Table -AutoSize
Full Error:
Cannot convert value "System.Collections.Hashtable" to type
"System.Management.Automation.LanguagePrimitives+InternalPSCustomObject". Error: "Cannot process argument because the
value of argument "name" is not valid. Change the value of the "name" argument and run the operation again."
At C:\Dev\Tools\powershell\Convert-TfsOutput.ps1:21 char:15
+ $arr.ForEach({[PSCustomObject]$_}) | Format-Table -AutoSize
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastConstructorException
Edit2
Here is sample output when i replace the above line with:
$arr.ForEach({ $_ | Out-String })
Name Value
---- -----
Workspace work1
Date {Wednesday, September 5, 2018 1, 38, 48 PM}
Local item file1
File type Windows-1252
User user1
Lock none
Change edit
Name Value
---- -----
Workspace work2
Date {Monday, September 10, 2018 12, 14, 56 PM}
Local item file2
User user2
Lock none
Change edit
Edit 3
Output of the below command
Write-Host $str;
User : User1
Date : Wednesday, September 5, 2018 1:38:48 PM
Lock : none
Change : edit
Workspace : Work1
Local item : File1
File type : Windows-1252
User : User2
Date : Monday, September 10, 2018 12:14:56 PM
Lock : none
Change : edit
Workspace : Work2
Local item : File2
Would like the output in a tabular format with rows below the column names:
Workspace | Date | Local item | File type | User | Lock | Change
Tried to use the code in another answer but it does not output correctly.
Format-Table on Array of Hash Tables
Convert your hashtables to custom objects before passing them to Format-Table.
... | Where-Object { $_ } | ForEach-Object {
$ht = #{};
($_ | Out-String) -split '\r?\n'| ForEach-Object {
...
}
New-Object -Type PSObject -Property $ht
} | Format-Table
Edit: Looks like your input data has blank lines which lead to keys with empty strings in your hashtables, which then cause the error you observed, because objects can't have a property with an empty string for a name.
Change your hashtable/object creation to something like this:
... | Where-Object { $_ } | ForEach-Object {
$ht = ($_ | Out-String).Trim() -replace '\s+:\s+', '=' |
ConvertFrom-StringData
New-Object -Type PSObject -Property $ht
} | Format-Table

How do I iterate through JSON array in powershell

How do I iterate through JSON array which is converted to PSCustomObject with ConvertFrom-JSON? Using foreach does not work.
$jsonArray ='[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"}]'
$json = convertfrom-json $jsonArray
$json | foreach {$_}
Returns
privateKeyLocation
------------------
C:\ProgramData\docker\certs.d\key.pem
Enumerator though says there are 3 members of array
>$json.Count
3
The problem that you are having is not specific to it being a JSON array, it has to do with how custom objects in an array are displayed by default. The simplest answer is to pipe it to Format-List (or FL for short).
PS C:\Users\TMTech> $JSON|FL
privateKeyLocation : C:\ProgramData\docker\certs.d\key.pem
publicKeyLocation : C:\ProgramData\docker\certs.d\cert.pem
publicKeyCALocation : C:\ProgramData\docker\certs.d\ca.pem
Aside from that, when PowerShell outputs an array of objects it bases the columns that it displays upon the properties of the first object in the array. In your case that object has one property named 'privateKeyLocation', so that is the only column that appears, and since the other two objects do not have that property it does not display anything for them. If you want to keep it as a table you could gather all potential properties, and add them to the first item with null values, and that would allow you to display it as a table, but it still wouldn't look very good:
$json|%{$_.psobject.properties.name}|select -Unique|?{$_ -notin $json[0].psobject.Properties.Name}|%{Add-Member -InputObject $JSON[0] -NotePropertyName $_ -NotePropertyValue $null}
Then you can output as a table and get everything:
PS C:\Users\TMTech> $json
privateKeyLocation publicKeyLocation publicKeyCALocation
------------------ ----------------- -------------------
C:\ProgramData\docker\certs.d\key.pem
C:\ProgramData\docker\certs.d\cert.pem
C:\ProgramData\docker\certs.d\ca.pem
Edit: To get the value of each object in this case is tricky, because the property that you want to expand keeps changing for each object. There's two ways to do this that I can think of, what I would consider the right way, and then there's the easy way. The right way to do it would be to determine the property that you want to expand, and then reference that property directly:
$JSON |%{
$PropName = $_.PSObject.Properties.Name
$_.$PropName
}
That'll do what you want, but I think easier would be to pipe to Format-List, then Out-String, wrap the whole thing in parenthesis, split on new lines and replace everything up to : which should just leave you with the paths you want.
($JSON|FL|Out-String) -split '[\r\n]+' -replace '(?m)^.+ : '|?{$_}
Interesting enough. I responded to this exact question from the same OP on another forum. Though my response was just RegEx and be done with it, with no additional conversion.
Of course there are several ways to do this. The below is just what I came up with.
$jsonArray = '[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"}]'
([regex]::Matches($jsonArray,'(?<=\").:\\[^\"]+(?=\")').Value) -replace '\\\\','\' `
| ForEach {
If (Test-Path -Path $_)
{"path $_ found"}
Else {Write-Warning "Path $_ not found"}
}
WARNING: Path C:\ProgramData\docker\certs.d\key.pem not found
WARNING: Path C:\ProgramData\docker\certs.d\cert.pem not found
WARNING: Path C:\ProgramData\docker\certs.d\ca.pem not found
So, maybe not as elegant as what was posted here, but it would get the OP where they wanted to be.
So, consolidating everything TheMadTechnician gave and what the OP is after, and attempting to make it as concise as possible, would give the OP the below (I added a element to show a positive response):
Clear-Host
($jsonArray = #'
[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"},
{"publicKeyTestFileLocation" : "D:\\Temp\\test.txt"}]
'# | ConvertFrom-Json | Format-List | Out-String) -split '[\r\n]+' -replace '(?m)^.+ : '`
| Where-Object {$_} | ForEach {
If(Test-Path -Path $_){"The path $_ was found"}
Else{Write-Warning -Message "The path $_ was not found}"}
}
WARNING: The path C:\ProgramData\docker\certs.d\key.pem was not found}
WARNING: The path C:\ProgramData\docker\certs.d\cert.pem was not found}
WARNING: The path C:\ProgramData\docker\certs.d\ca.pem was not found}
The path D:\Temp\test.txt was found
Which one is more to his liking is a matter of the OP choice of course.
The performance between the two varied on each test run, but the fastest time using the straight RegEx approach was:
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 43
Ticks : 439652
TotalDays : 5.08856481481481E-07
TotalHours : 1.22125555555556E-05
TotalMinutes : 0.000732753333333333
TotalSeconds : 0.0439652
TotalMilliseconds : 43.9652
and the fastest on the consolidated version here was:
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 54
Ticks : 547810
TotalDays : 6.34039351851852E-07
TotalHours : 1.52169444444444E-05
TotalMinutes : 0.000913016666666667
TotalSeconds : 0.054781
TotalMilliseconds : 54.781
Updating to add iRon's take on this topic
So this...
$jsonArray ='[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"}]'
$json = convertfrom-json $jsonArray
$json | ForEach {
$Key = $_.psobject.properties.name;
"Testing for key " + $_.$Key
Test-Path -Path $_.$Key
}
Testing for key C:\ProgramData\docker\certs.d\key.pem
False
Testing for key C:\ProgramData\docker\certs.d\cert.pem
False
Testing for key C:\ProgramData\docker\certs.d\ca.pem
False
... and this:
('[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"}]' `
| convertfrom-json) | ForEach {
$Key = $_.psobject.properties.name;
"Testing for key " + $_.$Key
Test-Path -Path $_.$Key
}
Testing for key C:\ProgramData\docker\certs.d\key.pem
False
Testing for key C:\ProgramData\docker\certs.d\cert.pem
False
Testing for key C:\ProgramData\docker\certs.d\ca.pem
False
Most simple way, should be like this
$ret ='[your json]'
$ret | ConvertFrom-Json
$data = $ret | ConvertFrom-Json
foreach($data in $ret | ConvertFrom-Json) {
Write-Host $data;
}
You can index into the array. Check out $json.GetType()
$jsonArray ='[{"privateKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\key.pem"},
{"publicKeyLocation" : "C:\\ProgramData\\docker\\certs.d\\cert.pem"},
{"publicKeyCALocation" : "C:\\ProgramData\\docker\\certs.d\\ca.pem"}]'
$json = convertfrom-json $jsonArray
foreach($i in 0..($json.Count-1)){
$json[$i] | out-host
$i++
}
You can use ForEach-Object i.e:
$json | ForEach-Object -Process { Write-Hoste $_; }
That's I believe the simplest way and gives you easy access to properties if array contains objects with other properties.

How to get the value of a particular propery from the result of a powershell command

I have a variable $ results which has the value :
SESSIONNAME USERNAME ID STATE TYPE DEVICE
rdp-tcp#1 account17 7 Active rdpwd
I want to get the value of ID alone and use it in a different query.
I tried the following ways :
1.$idValue = #($result | %{ $_.ID }) - but it was not getting the value.
2.$result |Select -ExpandProperty ID - I was getting the error 'Select-Object : Property "ID" cannot be found.'
How to get the value of the property ID alone from the result?
The output of the qwinsta/query commands are strings, not objects, so there isn't a property ID to print. You need to transform the strings into objects if you want the fields as properties:
query session | ? { $_ -match '^[ >](\S+) +(\S*?) +(\d+) +(\S+)' } |
select #{n='Service';e={$matches[1]}},
#{n='Username';e={$matches[2]}},
#{n='ID';e={$matches[3]}},
#{n='Status';e={$matches[4]}} | % {
$_.ID
}
Or, if you're just interested in the ID, you could do a regular expression replacement like this:
$account = 'account17'
$pattern = '^[ >]\S+ +\S*? +(\d+) +\S+.*'
(query session $account | select -Skip 1) -replace $pattern, '$1'
This is the format to refer to a single property properly. I don't see your command to create your RDP $result, so I'll example get-process, encapsulate it with () and tack an ().ID to the end. Works with any property, not just.ID
(get-process | where {$_.Name -eq "Powershell"}|select ID).ID
# or
$MYID = (get-process | where {$_.Name -eq "Powershell"}|select ID).ID
$MYID
Another option is -split:
One solution, using V4:
($result).ForEach({($_ -split '\s+')[2]}) -match '\d'

I need help formatting output with PowerShell's Out-File cmdlet

I have a series of documents that are going through the following function designed to count word occurrences in each document. This function works fine outputting to the console, but now I want to generate a text file containting the information, but with the file name appended to each word in the list.
My current console output is:
"processing document1 with x unique words occuring as follows"
"word1 12"
"word2 8"
"word3 3"
"word4 4"
"word5 1"
I want a delimited file in this format:
document1;word1;12
document1;word2;8
document1;word3;3
document1;word4;4
document1;word1;1
document2;word1;16
document2;word2;11
document2;word3;9
document2;word4;9
document2;word1;13
While the function below gets me the lists of words and occurences, I'm having a hard time figuring out where or how to insert the filename variable so that it prints at the head of each line. MSDN has been less-than helpful, and most of the places I try to insert the variable result in errors (see below)
function Count-Words ($docs) {
$document = get-content $docs
$document = [string]::join(" ", $document)
$words = $document.split(" `t",[stringsplitoptions]::RemoveEmptyEntries)
$uniq = $words | sort -uniq
$words | % {$wordhash=#{}} {$wordhash[$_] += 1}
Write-Host $docs "contains" $wordhash.psbase.keys.count "unique words distributed as follows."
$frequency = $wordhash.psbase.keys | sort {$wordhash[$_]}
-1..-25 | %{ $frequency[$_]+" "+$wordhash[$frequency[$_]]} | Out-File c:\out-file-test.txt -append
$grouped = $words | group | sort count
Do I need to create a string to pass to the out-file cmdlet? is this just something I've been putting in the wrong place on the last few tries? I'd like to understand WHY it's going in a particular place as well. Right now I'm just guessing, because I know I have no idea where to put the out-file to achieve my selected results.
I've tried formatting my command per powershell help, using -$docs and -FilePath, but each time I add anything to the out-file above that runs successfully, I get the following error:
Out-File : Cannot validate argument on parameter 'Encoding'. The argument "c:\out-file-test.txt" does not bel
ong to the set "unicode,utf7,utf8,utf32,ascii,bigendianunicode,default,oem" specified by the ValidateSet attribute. Sup
ply an argument that is in the set and then try the command again.
At C:\c.ps1:39 char:71
+ -1..-25 | %{ $frequency[$_]+" "+$wordhash[$frequency[$_]]} | Out-File <<<< -$docs -width 1024 c:\users\x46332\co
unt-test.txt -append
+ CategoryInfo : InvalidData: (:) [Out-File], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.OutFileCommand
I rewrote most of your code. You should utilize objects to make it easier formatting the way you want. This one splits on "space" and groups words together. Try this:
Function Count-Words ($paths) {
$output = #()
foreach ($path in $paths) {
$file = Get-ChildItem $path
((Get-Content $file) -join " ").Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries) | Group-Object | Select-Object -Property #{n="FileName";e={$file.BaseName}}, Name, Count | % {
$output += "$($_.FileName);$($_.Name);$($_.Count)"
}
}
$output | Out-File test-out2.txt -Append
}
$filepaths = ".\test.txt", ".\test2.txt"
Count-Words -paths $filepaths
It outputs like you asked(document;word;count). If you want documentname to include extension, change $file.BaseName to $file.Name . Testoutput:
test;11;1
test;9;2
test;13;1
test2;word11;5
test2;word1;4
test2;12;1
test2;word2;2
Slightly different approach:
function Get-WordCounts ($doc)
{
$text_ = [IO.File]::ReadAllText($doc.fullname)
$WordHash = #{}
$text_ -split '\b' -match '\w+'|
foreach {$WordHash[$_]++}
$WordHash.GetEnumerator() |
foreach {
New-Object PSObject -Property #{
Word = $_.Key
Count = $_.Value
}
}
}
$docs = gci c:\testfiles\*.txt |
sort name
&{
foreach ($doc in dir $docs)
{
Get-WordCounts $doc |
sort Count -Descending |
foreach {
(&{$doc.Name;$_.Word;$_.Count}) -join ';'
}
}
} | out-file c:\somedir\wordcounts.txt
Try this:
$docs = #("document1", "document2", ...)
$docs | % {
$doc = $_
Get-Content $doc `
| % { $_.split(" `t",[stringsplitoptions]::RemoveEmptyEntries) } `
| Group-Object `
| select #{n="Document";e={$doc}}, Name, Count
} | Export-CSV output.csv -Delimiter ";" -NoTypeInfo
If you want to make this into a function you could do it like this:
function Count-Words($docs) {
foreach ($doc in $docs) {
Get-Content $doc `
| % { $_.split(" `t",[stringsplitoptions]::RemoveEmptyEntries) } `
| Group-Object `
| select #{n="Document";e={$doc}}, Name, Count
}
}
$files = #("document1", "document2", ...)
Count-Words $files | Export-CSV output.csv -Delimiter ";" -NoTypeInfo