PowerShell で diff (テキスト ファイルを Compare-Object)

# PowerShell にも `diff` がある?

あるにはありますが、初心者なかせです。わたしははまりました。

たとえばつぎのふたつのテキスト ファイルで PS でも `diff` ってみますと、個人的には残念な結果をえます。

```
PS> cat .\poem.txt
Great fleas have little fleas
  upon their backs to bite 'em,
And little fleas have lesser fleas,
  and so ad infinitum.
And the great fleas themselves, in turn,
  have greater fleas to go on;
While these again have greater still,
  and greater still, and so on.
```

```
PS> cat .\new_poem.txt
Great fleas have little fleas
  upon their backs to bite them,
And little fleas have lesser fleas,
  and so on ad infinitum.
And the great fleas themselves, in turn,
  have greater fleas to go on;
While these again have greater still,
  and greater still, and so on.
```

```
PS> diff .\poem.txt .\new_poem.txt

InputObject    SideIndicator
-----------    -------------
.\new_poem.txt =>
.\poem.txt     <=


```

UNIX 系ならつぎのような出力がえられるところ。

```
$ diff poem.txt new_poem.txt
2c2
<   upon their backs to bite 'em,
---
>   upon their backs to bite them,
4c4
<   and so ad infinitum.
---
>   and so on ad infinitum.
```

# PS の `diff` は `Compare-Object` の alias

- `Get-Alias -Name diff` とすると `diff -> Compare-Object` とでます。
- 逆に `Get-Alias -Definition Compare-Object` とするとデフォルトで `compare` と `diff` とがでます。

`Compare-Object` の構文は

```
PS> Get-Command -Name Compare-Object -Syntax

Compare-Object [-ReferenceObject] <psobject[]> [-DifferenceObject] <psobject[]> [-SyncWindow <int>] [-Property <Object[]>] [-ExcludeDifferent] [-IncludeEqual] [-PassThru] [-Culture <string>] [-CaseSensitive] [<CommonParameters>]

```

すなわち `.\poem.txt` と `.\new_poem.txt` をそれぞれ単に psobject (を継承する string) の要素数 1 の配列としてうけとった模様です。

というわけで、ファイルの内容を Get しないとなりません。いろいろ略さずにかくと[^1]

[^1]: `Get-Content` した結果が null の場合にそなえ array operator `@()` でかこむのがよいとおもいます。 

```
PS> Compare-Object -ReferenceObject @(Get-Content -Path .\poem.txt) -DifferenceObject @(Get-Content -Path .\new_poem.txt)

InputObject                      SideIndicator
-----------                      -------------
  upon their backs to bite them, =>
  and so on ad infinitum.        =>
  upon their backs to bite 'em,  <=
  and so ad infinitum.           <=


```

# 行番号をうるには

しかし、まだ不満です。

- 行番号が表示されない。
- 対応する行が隣接していない。

`Get-Content -Path .\poem.txt | Get-Member` してみると `ReadCount` という NoteProperty がありますが、これは `Compare-Object` の結果の `InputObject` に温存されています。このことを利用してようやくのぞましい結果をえられたのでした。

```
PS> Compare-Object -ReferenceObject @(Get-Content -Path .\poem.txt) -DifferenceObject @(Get-Content -Path .\new_poem.txt) | Select-Object -Property @{Name = 'ReadCount'; Expression = {$_.InputObject.ReadCount}}, * | Sort-Object -Property ReadCount

ReadCount InputObject                      SideIndicator
--------- -----------                      -------------
        2   upon their backs to bite 'em,  <=
        2   upon their backs to bite them, =>
        4   and so ad infinitum.           <=
        4   and so on ad infinitum.        =>


```

2020/04/17 追記

上記 `Select-Object` ではあたらしいオブジェクトを生成しています[^2]。

[^2]: “When you select properties, Select-Object returns new objects that have only the specified properties.” (`Get-Help -Name Select-Object`)

[Sorting Objects | Using hash tables](https://docs.microsoft.com/en-us/powershell/scripting/samples/sorting-objects?view=powershell-7#using-hash-tables "Sorting Objects - PowerShell | Microsoft Docs") をよみかえしていてきづいたのですが、ハッシュ テーブルをつかえばふかいところにあるプロパティでもソートできますと。あとは表示のときに `ReadCount` をおもてにもってくるのでよければ `Format-Table` にまかせて

```powershell:PowerShell
Compare-Object -ReferenceObject @(Get-Content -Path .\poem.txt) -DifferenceObject @(Get-Content -Path .\new_poem.txt) |
    Sort-Object -Property @{Expression = {$_.InputObject.ReadCount}; Ascending = $true} |
    Format-Table -Property @{Name = 'ReadCount'; Expression = {$_.InputObject.ReadCount}}, *
```

ただ、ながい。常用するようなら `function` でくるむところでしょうか。