[Powershell] 2つのファイルを比較し差分データ(一致する行含む)を取得する

2020-09-15


はじめに

CSVファイルに限定したものではありませんが、WinMergeのようなソフトのように、2つのファイルの差分の行を取得する方法についての備忘録となります。

今回のサンプルの処理フローは下記となります。

実行環境

本スクリプトを作成した時の環境は下記の通り

項目 バージョン
PS Version 5.1.18362.752
OS Windows 10 1909 Pro 64bit
.Net Framework 4.7.2

差分比較し、比較結果を取得

2つのファイルの差分を比較するだけだと簡単です。

まずは下記の2つのファイルを用意します。

  • 比較元ファイル・・・BaseFile.csv
  • 比較先ファイル・・・CompFile.csv

BaseFile.csv

"Name","Age","Email"
"SampleName1","20","sample1@text.com"
"SampleName2","21","sample2@text.com"
"SampleName3","22","sample3@text.com"
"SampleName4","23","sample4@text.com"
"SampleName6","25","sample6@text.com"

CompFile.csv

"Name","Age","Email"
"SampleName1","20","sample1@text.com"
"SampleName2","21","sample2@text.com"
"SampleName3","22","sample3@text.com"
"SampleName4","23","sample4@text.com"
"SampleName5","24","sample5@text.com"
"SampleName7","26","sample7@text.com"

上記ファイルに対して以下のCmdletを実行してみます。

$baseFile = Get-Content .\BaseFile.csv
$compFile = Get-COntent .\CompFile.csv
Compare-Object $baseFile $compFile

実行した結果下記の結果が得られます。

InputObject                           SideIndicator
-----------                           -------------
"SampleName5","24","sample5@text.com" =>           
"SampleName7","26","sample7@text.com" =>           
"SampleName6","25","sample6@text.com" <=           

BaseFile.csvには存在せず、CompFile.csvには存在している行については「=>」と出力されます。

逆に、BaseFile.csvファイルには存在しているが、CompFile.csvには存在していない行については、「<=」が出力されます。

ただ、これだと、変更点のみの結果しか出力されません。

一致している行も出力する場合は、「-IncludeEqual」を引数に指定します。

$baseFile = Get-Content .\BaseFile.csv
$compFile = Get-COntent .\CompFile.csv
Compare-Object $baseFile $compFile -IncludeEqual

上記結果は下記の通りです。

InputObject                           SideIndicator
-----------                           -------------
"Name","Age","Email"                  ==           
"SampleName1","20","sample1@text.com" ==           
"SampleName2","21","sample2@text.com" ==           
"SampleName3","22","sample3@text.com" ==           
"SampleName4","23","sample4@text.com" ==           
"SampleName5","24","sample5@text.com" =>           
"SampleName7","26","sample7@text.com" =>           
"SampleName6","25","sample6@text.com" <= 

一致している行の結果は「==」と出力されています。

これで、比較結果が得られました。

差分結果毎にファイルを出力する

実際に比較した結果を、一致、差分(元/先)毎に出力していきます。

もともとcsvファイルなので、出力結果もcsvファイルとして出力します。

$baseFile = Get-Content .\BaseFile.csv
$compFile = Get-COntent .\CompFile.csv
$diffResult = Compare-Object $baseFile $compFile -IncludeEqual

$diffBaseData = @()
$diffCompData = @()
$matchData = @()
if(($diffResult -ne $null) -and ($diffResult.Count -gt 0)) {
    foreach($data in $diffResult) {
        $indicator = $data.SideIndicator
        $inputObject = $data.InputObject

        if($indicator -eq "<=") {
            # BaseFile.csvに存在し、CompFile.csvには存在しないデータ
            $diffBaseData += $inputObject
        } elseif ($indicator -eq "=>") {
            # CompFile.csvに存在し、BaseFile.csvには存在しないデータ
            $diffCompData += $inputObject
        } elseif ($indicator -eq "==") {
            # CompFile.csv、BaseFile.csv両方に存在するデータ
            $matchData += $inputObject
        } else {
            Write-Host "Invalid Line"
        }
    }

    # それぞれのデータをCSVデータ化する
    $diffBaseCsv = $diffBaseData | ConvertFrom-Csv -Header "Name","Age","Email"
    $diffCompCsv = $diffCompData | ConvertFrom-Csv -Header "Name","Age","Email"

    # ヘッダーはすでに一致しているデータとして持っているので、そのまま変換
    $matchCsv    = $matchData    | ConvertFrom-Csv

    # ファイルにエクスポート
    $diffBaseCsv | Export-Csv -NoTypeInformation .\DiffBaseFile.csv -Encoding UTF8
    $diffCompCsv | Export-Csv -NoTypeInformation .\DiffCompFile.csv -Encoding UTF8
    $matchCsv    | Export-Csv -NoTypeInformation .\MatchFile.csv -Encoding UTF8
}

上記で出力した結果をみてみます。

DiffBaseFile.csv・・・BaseFile.csvに存在し、CompFile.csvには存在しないデータ

Get-Content .\DiffBaseFile.csv

"Name","Age","Email"
"SampleName6","25","sample6@text.com"

DiffCompFile.csv・・・CompFile.csvに存在し、BaseFile.csvには存在しないデータ

Get-Content .\DiffCompFile.csv

"Name","Age","Email"
"SampleName5","24","sample5@text.com"
"SampleName7","26","sample7@text.com"

MatchFile.csv・・・CompFile.csv、BaseFile.csv両方に存在するデータ

Get-Content .\MatchFile.csv

"Name","Age","Email"
"SampleName1","20","sample1@text.com"
"SampleName2","21","sample2@text.com"
"SampleName3","22","sample3@text.com"
"SampleName4","23","sample4@text.com"

無事に、差分毎に結果を得ることができました。

最後に

今回は、CSVファイルでの比較を行いましたが、基本的にはどのテキストファイルでもできます。

上記を関数化し使いまわせるようなものを下記に用意しました、

コピペで活用ください。

function GetDiffData($baseFilePath, $compFilePath) {
    $diffResult = Compare-Object $baseFilePath $compFilePath -IncludeEqual

    $diffBaseData = @()
    $diffCompData = @()
    $matchData = @()
    if(($diffResult -ne $null) -and ($diffResult.Count -gt 0)) {
        foreach($data in $diffResult) {
            $indicator = $data.SideIndicator
            $inputObject = $data.InputObject

            if($indicator -eq "<=") {
                # BaseFile.csvに存在し、CompFile.csvには存在しないデータ
                $diffBaseData += $inputObject
            } elseif ($indicator -eq "=>") {
                # CompFile.csvに存在し、BaseFile.csvには存在しないデータ
                $diffCompData += $inputObject
            } elseif ($indicator -eq "==") {
                # CompFile.csv、BaseFile.csv両方に存在するデータ
                $matchData += $inputObject
            } else {
                Write-Host "Invalid Line"
            }
        }

        return $diffBaseData, $diffCompData, $matchData
    }
}

使い方は下記の通りです。

$baseFile = Get-Content .\BaseFile.csv
$compFile = Get-COntent .\CompFile.csv

$result = GetDiffData $baseFile $compFile

Write-Host
Write-Host "BaseFile.csvに存在し、CompFile.csvには存在しないデータ"
$result[0]

Write-Host
Write-Host "CompFile.csvに存在し、BaseFile.csvには存在しないデータ"
$result[1]

Write-Host
Write-Host "CompFile.csv、BaseFile.csv両方に存在するデータ"
$result[2]

上記を実行した結果は下記の通りです。

BaseFile.csvに存在し、CompFile.csvには存在しないデータ
"SampleName6","25","sample6@text.com"

CompFile.csvに存在し、BaseFile.csvには存在しないデータ
"SampleName5","24","sample5@text.com"
"SampleName7","26","sample7@text.com"

CompFile.csv、BaseFile.csv両方に存在するデータ
"Name","Age","Email"
"SampleName1","20","sample1@text.com"
"SampleName2","21","sample2@text.com"
"SampleName3","22","sample3@text.com"
"SampleName4","23","sample4@text.com"

以上です