PowerShell output contents of hash to file - powershell

I have code to output a sorted hashtable to the screen:
$h.getenumerator() | sort value -descending
It looks like this:
Name Value
---- -----
10.10.10.10 69566308 151
10.10.10.11 69566308 143
10.10.10.12 69566308 112
10.10.10.13 69566308 99
10.10.10.14 69566308 71
10.10.10.15 69566308 70
But I would like to output this to a file instead.
When I try to output to a file with
$h.getenumerator() | sort value -descending | out-string | Add-Content D:\Script\iis_stats.log
or
$h.getenumerator() | sort value -descending | Add-Content D:\Script\iis_stats.log
All I get is "System.Collections.DictionaryEntry" in D:\Script\iis_stats.log.
How do I fix it?

If you want it in the same format it displays on the screen (folded at the pipes for readability):
$h.getenumerator() |
Sort-Object value -descending |
Format-Table |
Out-String |
Add-Content D:\Script\iis_stats.log

You would get similar output on your screen if you did something like this as well:
$h.GetEnumerator() | sort value -Descending | ForEach-Object{
$_.GetType().FullName
}
For every entry you would see System.Collections.DictionaryEntry since it does not have a proper string equivalent.
Then you should just do what mjolinor suggests. PowerShell automatically appends the | Out-Default command to the console so that it displays in a way that is visually pleasing. When you send the output to Add-Content that prettification cannot occur.

Related

Collecting Unique Items from large data set over multiple text files

I am using PowerShell to collect lists of names from multiple text files. May of the names in these files are similar / repeating. I am trying to ensure that PowerShell returns a single text file with all of the unique items. In looking at the data it looks like the script is gathering 271/296 of the unique items. I'm guessing that some of the data is being flagged as duplicates when it shouldn't, any suggestions?
#Take content of each file (all names) and add unique values to text file
#for each unique value, create a row & check to see which txt files contain
function List {
$nofiles = Read-Host "How many files are we pulling from?"
$data = #()
for ($i = 0;$i -lt $nofiles; $i++)
{
$data += Read-Host "Give me the file name for file # $($i+1)"
}
return $data
}
function Aggregate ($array) {
Get-Content $array | Sort-Object -unique | Out-File newaggregate.txt
}
#SCRIPT BODY
$data = List
aggregate ($data)
I was expecting this code to catch everything, but it's missing some items that look very similar. List of missing names and their similar match:
CORPINZUTL16 MISSING FROM OUTFILE
CORPINZTRACE MISSING FROM OUTFILE
CORPINZADMIN Found In File
I have about 20 examples like this one. Apparently the Get-Content -Unique is not checking every character in a line. Can anyone recommend a better way of checking each line or possibly forcing the get-character to check full names?
Just for demonstration this line creates 3 txt files with numbers
for($i=1;$i -lt 4;$i++){set-content -path "$i.txt" -value ($i..$($i+7))}
1.txt | 2.txt | 3.txt | newaggregate.txt
1 | | | 1
2 | 2 | | 2
3 | 3 | 3 | 3
4 | 4 | 4 | 4
5 | 5 | 5 | 5
6 | 6 | 6 | 6
7 | 7 | 7 | 7
8 | 8 | 8 | 8
| 9 | 9 | 9
| | 10 | 10
Here using Get-Content with a range [1-3] of files
Get-Content [1-3].txt | Sort-Object {[int]$_} -Unique | Out-File newaggregate.txt
$All = Get-Content .\newaggregate.txt
foreach ($file in (Get-ChildItem [1-3].txt)){
Compare-Object $All (Get-Content $file.FullName) |
Select-Object #{n='File';e={$File}},
#{n="Missing";e={$_.InputObject}} -ExcludeProperty SideIndicator
}
File Missing
---- -------
Q:\Test\2019\05\07\1.txt 9
Q:\Test\2019\05\07\1.txt 10
Q:\Test\2019\05\07\2.txt 1
Q:\Test\2019\05\07\2.txt 10
Q:\Test\2019\05\07\3.txt 1
Q:\Test\2019\05\07\3.txt 2
there are two ways to achieve this one is using select-object -Unique which works when data is not sorted and can be used for small data or lists.
When dealing with large files we can use get-Unique command which works with sorted input, if input data is not sorted then it will give wrong results.
Get-ChildItem *.txt | Get-Content | measure -Line #225949
Get-ChildItem *.txt | Get-Content | sort | Get-Unique | measure -Line #119650
Here is my command for multiple files :
Get-ChildItem *.txt | Get-Content | sort | Get-Unique >> Unique.txt

Powershell - Creating Wide Tables

Good afternoon!
I am a Powershell novice trying to understand how data output works. I am trying to find the differences between two users in Active Directory. I found a solution that worked (Compare-Object on two AD user accounts), but the data some of in the relevant fields was truncated with an ... which didn't help.
I found a solution which seems very elegant at the bottom of the page here: http://poshoholic.com/2010/11/11/powershell-quick-tip-creating-wide-tables-with-powershell/
I attempted to combine these two into a single script. This is what I have:
$user1 = get-aduser jdoe -Properties *
$user2 = get-aduser jsmith -Properties *
$Usercomparison = #()
$user1.GetEnumerator() | ForEach-Object {
If ($User2.($_.Key) -eq $_.Value)
{
$Comparison = 'Equal'
}
else
{
$Comparison = 'Different'
}
$UserObj = New-Object PSObject -Property ([ordered]#{
Property = $_.Key
User1 = $_.Value
User2 = $User2.($_.Key)
Comparison = $Comparison
})
$UserComparison += $UserObj
}
$UserComparison
| Format-Table -Property * -AutoSize `
| Out-String -Width 4096 `
| Out-File C:\Users\USER\Desktop\differences.txt
This produces an error that "an empty pipe element is not allowed". If I delete a line return to put the first pipe line after the $UserComparison variable...
$UserComparison | Format-Table -Property * -AutoSize `
| Out-String -Width 4096 `
| Out-File C:\aliases.txt
...then the text file is created, but it's badly formatted. Only the first two columns appear in the output and there is a ton of wasted whitespace to the right and several blank line returns after each line... nothing like the example on the website.
Is this because the script I found writes the data to a variable and then just prints the variable on screen instead of using a command that can be output properly? I feel like I have all the pieces that I need, just not in the right configuration to get the output I want.
Thanks!
So, #1 the line:
$UserComparison
| Format-Table -Property * -AutoSize `
| Out-String -Width 4096 `
| Out-File C:\Users\USER\Desktop\differences.txt
Doesn't work because you are first executing
$UserComparison
Which outputs the contents of $UserComparison. Next, you execute
| Format-Table -Property * -AutoSize `
Which errors out because nothing is being piped into Format-Table. The "ticks" ( ` ) at the end of the Format-Table statement is a continue line statement i.e. the second version:
$UserComparison | Format-Table -Property * -AutoSize `
| Out-String -Width 4096 `
| Out-File C:\aliases.txt
Is correct because it will be interpreted as one giant line.
Second question, the reason why you are having issues is because 4096 characters is not enough space to hold everything, and so is truncated. Remember, -AutoSize will calculate the width of the longest item, and make that the width of the column. There are some items that are too long. For ex. For me, the thumbnailPhoto (which happened to be item 140 in my array):
$UserComparison[140]
Gives something like this (truncated depending on width):
Property User1
-------- -----
thumbnailPhoto {255 216 255 224 0 16 74 70 73 70 0 1 1 1 0 96 0...
When I calculate the width of this, it gives me:
#Calculate width of User1
($UserComparison[140].User1 | Out-String).Length
7555
#Calculate width of full field
($UserComparison[140] | Out-String).Length
12297
Yes, User1 is 7,555 characters long. That means that Format-Table -Autosize will make the User1 column at least 7,555 characters wide, which obviously is truncated by the 4,096 width limit that you specified on the Out-String, and then won't display the User2 or Comparison columns. In this case, your Out-String needs to have a width of at least 12,297 wide in order to display the full field.
The workaround is to specify an even bigger width on the Out-String that is guaranteed to be wider, like, say, 50,000 so your code would be:
$UserComparison | Format-Table -Property * -AutoSize `
| Out-String -Width 50000 `
| Out-File C:\Users\USER\Desktop\differences.txt
Now, the downside to doing things this way is that every line in the text file will be the full width of the longest item, and so (in my case) every line will be 12,297 characters long. This makes things harder to read.
Other ways to output things would be to:
Limit things to just displaying the Property and Comparison columns:
$UserComparison | Select Property, Comparison `
| Format-Table -Property * -AutoSize `
| Out-String -Width 4096 `
| Out-File SimpleCompare.txt
Or if you need to see what the full values are, chop each property up into a separate table with a ForEach-Object, and then pass that through so that would be easier to read, and each property is limited to it's specific width:
$UserComparison | Select Property, Comparison, User1, User2 `
| ForEach-Object { $_ | Format-Table -Property * -AutoSize `
| Out-String -Width 50000 `
| Out-File EasyToRead.txt -Append }

Powershell to reformat the file content

I have a script which gets content from one file and checks for its ip. Then that is added to some other text file.
[System.Collections.ArrayList]$hlist1 = Get-Content -Path "C:\Timezone\Update\host.txt"
$hiplist = New-Object System.Collections.ArrayList
$hlist2 = New-Object System.Collections.ArrayList
ForEach ($h in $hlist1)
{
$hip = Resolve-DnsName $h
$hiplist.Add($hip)
}
$hiplist | Out-File "C:\Timezone\Update\hiplist.txt"
The file which is getting created is as shown below:
---- ---- --- ------- --------
WIN-JB2A2FS84MQ.domain.com A 1200 Answer 10.3.0.4
8
WIN-QP0BH4SD2H9.domain.com A 1200 Answer 10.3.1.1
9
I need to:
get rid of the first -------- lines.
get the entire ip in the same line (10.3.0.10)
Have tried Format-Table -Autosize, then Select -Skip 1 etc, but no luck.
How can this be achieved.? Please note that the code works fine as expected when it is ran manually, but throws this issue when executed using task scheduler.
Edit Based on Matt's answer
Now the text file contains:
"Address","IPAddress","QueryType","IP4Address","Name","Type","CharacterSet","Section","DataLength","TTL"
"10.3.0.48","10.3.0.48","A","10.3.0.48","WIN-JB2A2FS84MQ.domain.com","A","Unicode","Answer","4","1200"
"10.3.1.19","10.3.1.19","A","10.3.1.19","WIN-QP0BH4SD2H9.domain.com","A","Unicode","Answer","4","1200"
Peter-sal's reply output:
Name Type TTL Section IPAddress
---- ---- --- ------- ---------
WIN-JB2A2FS84MQ.domain.com A 1200 Answer 10.3.0.48
WIN-QP0BH4SD2H9.domain.com A 1200 Answer 10.3.1.19
But again on top of Name there's one space. I need to delete everything present before WIN-JB2.....
I cannot test perfectly but I would like to come back to an earlier comment of mine. Resolve-DNSName returns objects so their output is better destined for something object aware. Export-CSV should be preferable here.
$hlist1 = Get-Content -Path "C:\Timezone\Update\host.txt"
$hlist1 | ForEach-Object{Resolve-DnsName $_} |
Export-Csv "C:\Timezone\Update\hiplist.txt" -NoTypeInformation
I normally don't like this but if you prefer you should be able to use the Format-table output now. This seems to be more inline with what you are looking for.
$hlist1 = Get-Content -Path "C:\Timezone\Update\host.txt"
$hlist1 | ForEach-Object{Resolve-DnsName $_} |
Format-Table -HideTableHeaders | Select-Object -Skip 1 |
Out-File "C:\Timezone\Update\hiplist.txt" -Width 200
Perhaps you prefer that output. The header should be removed now as well as a blank line in the beginning.
That creates some white-space before and after the output. Simple solution is to wrap that up in a Trim()
$hlist1 = Get-Content -Path "C:\Timezone\Update\host.txt"
$results = ($hlist1 | ForEach-Object{Resolve-DnsName $_} |
Format-Table -HideTableHeaders |
Out-string).Trim()`
$results | Out-File "C:\Timezone\Update\hiplist.txt" -Width 200`

How do I get filename and line count per file using powershell

I have the following powershell script to count lines per file in a given directory:
dir -Include *.csv -Recurse | foreach{get-content $_ | measure-object -line}
This is giving me the following output:
Lines Words Characters Property
----- ----- ---------- --------
27
90
11
95
449
...
The counts-per-file is fine (I don't require words, characters, or property), but I don't know what filename the count is for.
The ideal output would be something like:
Filename Lines
-------- -----
Filename1.txt 27
Filename1.txt 90
Filename1.txt 11
Filename1.txt 95
Filename1.txt 449
...
How do I add the filename to the output?
try this:
dir -Include *.csv -Recurse |
% { $_ | select name, #{n="lines";e={
get-content $_ |
measure-object -line |
select -expa lines }
}
} | ft -AutoSize
I can offer another solution :
Get-ChildItem $testPath | % {
$_ | Select-Object -Property 'Name', #{
label = 'Lines'; expression = {
($_ | Get-Content).Length
}
}
}
I operate on the. TXT file, the return value is like this ↓
Name Lines
---- ----
1.txt 1
2.txt 2
3.txt 3
4.txt 4
5.txt 5
6.txt 6
7.txt 7
8.txt 8
9.txt 9
The reason why I want to sort like this is that I am rewriting a UNIX shell command (from The Pragmatic Programmer: Your Journey to Mastery on page 145).
The purpose of this command is to find out the five files with the largest number of lines.
At present, my progress is the above content,i'm close to success.
However, this command is far more complicated than the UNIX shell command!
I believe there should be a simpler way, I'm trying to find it.
find . -type f | xargs wc -l | sort -n | tail -5
I have used the following script that gives me lines in files of all sub directories in folder c:\temp\A. The output is in lines1.txt file. I have applied a filer to choose only file types of ".TXT".
Get-ChildItem c:\temp\A -recurse | where {$_.extension -eq ".txt"} | % {
$_ | Select-Object -Property 'Name', #{
label = 'Lines'; expression = {
($_ | Get-Content).Length
}
}
} | out-file C:\temp\lines1.txt

Getting character count for each row in text doc

I am trying to get the character count for each row in a text doc. The contents of my text doc are:
1
15
69
124
300
I've been trying variants of the PS script:
get-content c:\serverlist.txt | foreach-object {measure-object -character}
But the best I can get returned is:
Lines Words Characters Property
------- -------- -------------- -----------
0 0 0 0 0
Not sure what I'm missing here, but any help would be appreciated!
Thanks!
You have to pipe directly into Measure-Object:
Get-Content c:\serverlist.txt | Measure-Object -Character
Otherwise you'd have to do either
| ForEach-Object { $_ | Measure-Object -Character }
which would be a bit of weird use of the pipeline or
| ForEach-Object { Measure-Object -Character -InputObject $_ }
which would be just about the same as the variant above.