2

I want to compare 2 text files and output the difference in another text file.

$Location = "c:\temp\z.txt"
compare-object (get-content c:\temp\hostname_old.txt) (get-content c:\temp\hostname_new.txt) | format-list | Out-File $Location

hostname_old.txt

server02
server05
server04
server06
server01

hostname_new.txt

server04
server01
server02

Results

InputObject   : server05
SideIndicator : <=

InputObject   : server06
SideIndicator : <=

This is what I want : (get rid of both InputObject and SideIndicator)

server05
server06

Note: A related problem where one input file has duplicate entries is the subject of this question.

mklement0
  • 382,024
  • 64
  • 607
  • 775
Arbelac
  • 1,698
  • 6
  • 37
  • 90

3 Answers3

1

Just use the -PassThru parameter:

compare-object (get-content c:\temp\hostname_old.txt) (get-content c:\temp\hostname_new.txt) -PassThru | Out-File $Location

does exactly what you want.

mklement0
  • 382,024
  • 64
  • 607
  • 775
Palle Due
  • 5,929
  • 4
  • 17
  • 32
  • This solution doesn't care about duplicates. Each new line of text is considered unique. So, a duplicate in the "new" file, will be copied to out, unless it's also duplicated in the "old" file. – Palle Due Feb 07 '19 at 13:17
0

Update: Palle Due's helpful answer offers the best solution.
This answer may still be of interest for contrasting member-access enumeration with pipeline use, a discussion of output formatting, and contrasting Out-File with Set-Content.


In PSv3+ you can simply use member-access enumeration to extract the .InputObject values:

PS> (Compare-Object (Get-Content old.txt) (Get-Content new.txt)).InputObject
server05
server06

Note:

  • Member-access enumeration is convenient and fast, but at the expense of memory consumption, which can be a problem with very large collections (not here). The output from Compare-Object must be collected in memory as a whole in an array ([object[]]), and, similarly, the .InputObject property values are returned as an array.

  • For slower, but memory-friendly streaming (one-by-one processing), use the pipeline with Select-Object -ExpandProperty, as in TobyU's effective solution.

Re saving to a file: piping to Out-File $location (or, more succinctly, using output redirection: > $location) is sufficient - no need for Format-List.

In general, note that the purpose of the Format-* cmdlets is to produce output for display, not for programmatic processing and persistence.

That said, Out-File / > (effectively) uses the Format-* cmdlets behind the scenes to produce a string representation of the input objects, just like the default console output, which is why it's not the right command for persisting arbitrary input objects.

Use of Out-File / > with strings is safe, however, because they are output as-is. By contrast, even numbers are problematic if they have decimal places, because they are stringified with the current culture's decimal separator (e.g., , rather than . in some cultures).

If your input objects are strings, you can alternatively use Set-Content, which is faster than Out-File / >, but the caveat is that in Windows PowerShell the character encoding used by default differs: Out-File / > produces UTF-16LE files by default, whereas Set-Content uses the legacy system locale's "ANSI" code page (typically, a single-byte 8-bit encoding such as Windows-1252).
By contrast, in PowerShell Core both cmdlets produce BOM-less UTF-8.

Note that Set-Content, unlike Out-File, stringifies non-string objects simply by calling the .ToString() method on them.

mklement0
  • 382,024
  • 64
  • 607
  • 775
0

I guess you're looking for Select-Object -ExpandProperty InputObject

compare-object (get-content c:\temp\hostname_old.txt) (get-content c:\temp\hostname_new.txt) | Select-Object -ExpandProperty InputObject | Out-File $Location

Please note, that you cannot use format-list in the Pipeline before writing data into a file.

TobyU
  • 3,718
  • 2
  • 21
  • 32
  • 1
    Solid solution. Note that you _can_ use the `Format-*` cmdlets with `Out-File` / `>` (but not with `Set-Content`). In fact, `Out-File` / `>` (effectively) uses the `Format-*` cmdlets behind the scenes to produce a string representation of the input objects. That said, unless the input objects are strings, the resulting representation is suitable for human consumption only, not as a stable format for programmatic processing. – mklement0 Feb 07 '19 at 11:33