none
batファイルでcsvファイルの比較について RRS feed

  • 質問

  • お世話になります。

    仕事仲間より、TechNetのフォーラムで質問してみてはどうだろうか。お聞きしましたので、

    初めて利用させていただいております。

    内容はタイトル通りで、batファイルを使ってcsvファイルを比較するというものになります。



    【ファイルA】
    A列(文字列)、B列(文字列)、C列(文字列)、D列(日付)


    【ファイルB】
    A列(文字列)、B列(文字列/正規表現)、C列(文字列/正規表現)、D列(日付)、E列(日付)、F列(文字列)

    ファイルAはシステムから出力されるもの、ファイルBは予め準備しているものになります。

    この状態でシステムからファイルAが出力されるたびにOS上のbatファイルで

    ファイルAとBを比較したいと思っていますが、今まで簡易なbatしか作成したことがなく、

    苦戦をしております。



    要件としては、双方のファイルの各列比較を行いたく、B列やC列には正規表現が入ります。

    ファイルAのD列の値が、ファイルBのD列以上、E列以下でDE以外の列の値が同じ場合、

    F列の値をechoで画面に出力させるというものになります。

    また、比較については1行目で合致したものが合っても処理は終わらせず、最終行まで走査させてから

    F列の値をechoで画面に出力という流れにしたいと思っています。

    このような比較がbatファイルで行えるのかと苦戦しておりますので、質問させていただきました。



    以上、よろしくお願いします。

    2019年8月25日 3:04

回答

  • バッチでもできるのかもしれませんが、私ならPowershellにします。

    バッチ以外となれば選択肢は複数あるので、Powershellはあくまで選択肢の一つです。

    Powershellでスクリプトを書くとこんな感じです。

    $CsvA = Import-CSV "FileA.csv" -Header "A","B","C","D"
    $CsvB = Import-CSV "FileB.csv" -Header "A","B","C","D","E","F"
    
    ForEach ($B in $CsvB)
    {
        $B.D = [DateTime]$B.D
        $B.E = [DateTime]$B.E
    }
    
    ForEach ($A in $CsvA)
    {
        $A.D = [DateTime]$A.D
        ForEach ($B in $CsvB)
        {
            If (($A.D -ge $B.D) -and ($A.D -le $B.E))
            {
                If (($A.A -eq $B.A) -and ($A.B -match $B.B) -and ($A.C -match $B.C))
                {
                    Echo $B.F
                }
            }
        }
    }

    これをtest.ps1という名前でCSVと同じフォルダに保存し、コマンドプロンプトでここにcd し、

    Powershell ~ExecutionPolicy RemoteSigned .\test.ps1

    で実行します。

    詳しい説明は省きますが、Powershell をネットで調べれば色々分かると思いますし、TechnetにもPowershellのカテゴリがありますので、続きが必要ならそちらで質問いただくのが良いかと思います。

    個人的にはPowershellは便利に使わせてもらっていますが、私の勤め先のIT専門部隊はは専らVBスクリプトです。

    Powershellを使える人でさえ、私に向かって「可能な限りバッチで書くべき」と言ってくるので、Powershellを勧めるのは罪悪なのか?と悩んでしまいます。

    あくまでバッチで、ということでしたらごめんなさい。

    • 回答としてマーク Fujioka1004 2019年9月1日 4:38
    2019年8月26日 13:38
  • チャブーンです。

    この件ですが、答えるかどうか多少迷いました。他の方からのご指摘通り、PowerShellのほうが全然簡単に、かつ合理的なコード記述が可能だからです。バッチファイルで「複雑怪奇なコード」から難易度の高い動作を実現しても、何の技術的メリットもない認識です。ほとんど社内政治的な要件でしか行われないので、技術者としては「ガラパゴスエンジニア」と呼ばれてしまう可能性すらあるでしょう。

    どうしてもそうしたいという場合、CSVの各要素を「2次元の配列要素」にいれたうえ、そのデータを比較する方法を採るしかありません。そのためには環境変数に「数字を付けて区別する」必要から、Setlocal enabledelayedexpansionを使う必要があります。

    サンプルは以下の通りですが、最下段にあるようなURLを参考にすれば、「なぜこれが使われているのか」理解できるかと思います。

    REM *以下のcsvファイルを事前に定義しておく*
    REM 
    REM [FileA.csv]
    REM a,b,c,2019/08/26
    REM d,e,f,2019/08/27
    REM g,h,i,2019/08/28
    REM  
    REM [FileB.csv]
    REM a,b,c,2019/08/25,2019/08/27,aaa
    REM d,e,f,2019/08/26,2019/08/28,bbb
    REM g,h,i,2019/08/27,2019/08/29,ccc
    REM 
    
    @echo off
    
    Setlocal enabledelayedexpansion
    
    REM FileA.csv要素を疑似的な配列変数に入れる
    Set n=0
    For /F "tokens=1,2,3,4 delims=," %%a in (FileA.csv) Do (
    	Set FileA[!n!][0]=%%a
    	Set FileA[!n!][1]=%%b
    	Set FileA[!n!][2]=%%c
    	Set FileA[!n!][3]=%%d
    	Set /A n=n+1
    )
    
    REM FileB.csv要素を疑似的な配列変数に入れる
    Set n=0
    For /F "tokens=1,2,3,4,5,6 delims=," %%a in (FileB.csv) Do (
    	Set FileB[!n!][0]=%%a
    	Set FileB[!n!][1]=%%b
    	Set FileB[!n!][2]=%%c
    	Set FileB[!n!][3]=%%d
    	Set FileB[!n!][4]=%%e
    	Set FileB[!n!][5]=%%f
    	Set /A n=n+1
    )
    
    REM 日付データを数値化したうえ、日付の比較→CSVファイルの要素の順で、比較を行う
    Set /A n=n-1
    For /L %%m in (0,1,%n%) Do (
    	Set "Sdate1=!FileA[%%m][3]:~0,4!!FileA[%%m][3]:~5,2!!FileA[%%m][3]:~-2!"
    	Set "Sdate2=!FileB[%%m][3]:~0,4!!FileB[%%m][3]:~5,2!!FileB[%%m][3]:~-2!" 
    	Set "Sdate3=!FileB[%%m][4]:~0,4!!FileB[%%m][4]:~5,2!!FileB[%%m][4]:~-2!"
    	IF !Sdate1! GEQ !Sdate2! (
    		IF !Sdate1! LEQ !Sdate3! (
    			IF !FileA[%%m][0]! EQU !FileB[%%m][0]! (
    				IF !FileA[%%m][1]! EQU !FileB[%%m][1]! (
    					IF !FileA[%%m][2]! EQU !FileB[%%m][2]! (
    						@echo !FileB[%%m][5]!
    					)
    				)
    			)
    		)
    	)
    )

    Endlocal

    https://jj-blues.com/cms/wantto-usearray/
    https://stackoverflow.com/questions/17649235/compare-2-dates-in-a-windows-batch-file
    https://stackoverflow.com/questions/12150065/dos-batch-nested-variable-name-using-enabledelayedexpansion


    フォーラムは有償サポートとは異なる「コミュニティ」です。フォーラムでご質問頂くにあたっての注意点 をご一読のうえ、お楽しみください。




    2019年8月28日 5:51
  • Fujioka1004さん、こんにちは。フォーラムオペレーターのFarenaです。

    TechNetフォーラムにご投稿くださいましてありがとうございます。

     

    さんから寄せられた投稿はお役に立ちましたか。

     

    参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、ご協力くださいますようお願いいたします。

     

    ご不明な点がございましたら、お気軽にお問い合わせください。

    今後ともTechNet フォーラムをよろしくお願いします。


    Please remember to mark the replies as answers if they help.
    If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    • 回答としてマーク Fujioka1004 2019年9月1日 4:44
    2019年8月29日 2:18
    モデレータ

すべての返信

  • バッチでもできるのかもしれませんが、私ならPowershellにします。

    バッチ以外となれば選択肢は複数あるので、Powershellはあくまで選択肢の一つです。

    Powershellでスクリプトを書くとこんな感じです。

    $CsvA = Import-CSV "FileA.csv" -Header "A","B","C","D"
    $CsvB = Import-CSV "FileB.csv" -Header "A","B","C","D","E","F"
    
    ForEach ($B in $CsvB)
    {
        $B.D = [DateTime]$B.D
        $B.E = [DateTime]$B.E
    }
    
    ForEach ($A in $CsvA)
    {
        $A.D = [DateTime]$A.D
        ForEach ($B in $CsvB)
        {
            If (($A.D -ge $B.D) -and ($A.D -le $B.E))
            {
                If (($A.A -eq $B.A) -and ($A.B -match $B.B) -and ($A.C -match $B.C))
                {
                    Echo $B.F
                }
            }
        }
    }

    これをtest.ps1という名前でCSVと同じフォルダに保存し、コマンドプロンプトでここにcd し、

    Powershell ~ExecutionPolicy RemoteSigned .\test.ps1

    で実行します。

    詳しい説明は省きますが、Powershell をネットで調べれば色々分かると思いますし、TechnetにもPowershellのカテゴリがありますので、続きが必要ならそちらで質問いただくのが良いかと思います。

    個人的にはPowershellは便利に使わせてもらっていますが、私の勤め先のIT専門部隊はは専らVBスクリプトです。

    Powershellを使える人でさえ、私に向かって「可能な限りバッチで書くべき」と言ってくるので、Powershellを勧めるのは罪悪なのか?と悩んでしまいます。

    あくまでバッチで、ということでしたらごめんなさい。

    • 回答としてマーク Fujioka1004 2019年9月1日 4:38
    2019年8月26日 13:38
  • チャブーンです。

    この件ですが、答えるかどうか多少迷いました。他の方からのご指摘通り、PowerShellのほうが全然簡単に、かつ合理的なコード記述が可能だからです。バッチファイルで「複雑怪奇なコード」から難易度の高い動作を実現しても、何の技術的メリットもない認識です。ほとんど社内政治的な要件でしか行われないので、技術者としては「ガラパゴスエンジニア」と呼ばれてしまう可能性すらあるでしょう。

    どうしてもそうしたいという場合、CSVの各要素を「2次元の配列要素」にいれたうえ、そのデータを比較する方法を採るしかありません。そのためには環境変数に「数字を付けて区別する」必要から、Setlocal enabledelayedexpansionを使う必要があります。

    サンプルは以下の通りですが、最下段にあるようなURLを参考にすれば、「なぜこれが使われているのか」理解できるかと思います。

    REM *以下のcsvファイルを事前に定義しておく*
    REM 
    REM [FileA.csv]
    REM a,b,c,2019/08/26
    REM d,e,f,2019/08/27
    REM g,h,i,2019/08/28
    REM  
    REM [FileB.csv]
    REM a,b,c,2019/08/25,2019/08/27,aaa
    REM d,e,f,2019/08/26,2019/08/28,bbb
    REM g,h,i,2019/08/27,2019/08/29,ccc
    REM 
    
    @echo off
    
    Setlocal enabledelayedexpansion
    
    REM FileA.csv要素を疑似的な配列変数に入れる
    Set n=0
    For /F "tokens=1,2,3,4 delims=," %%a in (FileA.csv) Do (
    	Set FileA[!n!][0]=%%a
    	Set FileA[!n!][1]=%%b
    	Set FileA[!n!][2]=%%c
    	Set FileA[!n!][3]=%%d
    	Set /A n=n+1
    )
    
    REM FileB.csv要素を疑似的な配列変数に入れる
    Set n=0
    For /F "tokens=1,2,3,4,5,6 delims=," %%a in (FileB.csv) Do (
    	Set FileB[!n!][0]=%%a
    	Set FileB[!n!][1]=%%b
    	Set FileB[!n!][2]=%%c
    	Set FileB[!n!][3]=%%d
    	Set FileB[!n!][4]=%%e
    	Set FileB[!n!][5]=%%f
    	Set /A n=n+1
    )
    
    REM 日付データを数値化したうえ、日付の比較→CSVファイルの要素の順で、比較を行う
    Set /A n=n-1
    For /L %%m in (0,1,%n%) Do (
    	Set "Sdate1=!FileA[%%m][3]:~0,4!!FileA[%%m][3]:~5,2!!FileA[%%m][3]:~-2!"
    	Set "Sdate2=!FileB[%%m][3]:~0,4!!FileB[%%m][3]:~5,2!!FileB[%%m][3]:~-2!" 
    	Set "Sdate3=!FileB[%%m][4]:~0,4!!FileB[%%m][4]:~5,2!!FileB[%%m][4]:~-2!"
    	IF !Sdate1! GEQ !Sdate2! (
    		IF !Sdate1! LEQ !Sdate3! (
    			IF !FileA[%%m][0]! EQU !FileB[%%m][0]! (
    				IF !FileA[%%m][1]! EQU !FileB[%%m][1]! (
    					IF !FileA[%%m][2]! EQU !FileB[%%m][2]! (
    						@echo !FileB[%%m][5]!
    					)
    				)
    			)
    		)
    	)
    )

    Endlocal

    https://jj-blues.com/cms/wantto-usearray/
    https://stackoverflow.com/questions/17649235/compare-2-dates-in-a-windows-batch-file
    https://stackoverflow.com/questions/12150065/dos-batch-nested-variable-name-using-enabledelayedexpansion


    フォーラムは有償サポートとは異なる「コミュニティ」です。フォーラムでご質問頂くにあたっての注意点 をご一読のうえ、お楽しみください。




    2019年8月28日 5:51
  • Fujioka1004さん、こんにちは。フォーラムオペレーターのFarenaです。

    TechNetフォーラムにご投稿くださいましてありがとうございます。

     

    さんから寄せられた投稿はお役に立ちましたか。

     

    参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、ご協力くださいますようお願いいたします。

     

    ご不明な点がございましたら、お気軽にお問い合わせください。

    今後ともTechNet フォーラムをよろしくお願いします。


    Please remember to mark the replies as answers if they help.
    If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    • 回答としてマーク Fujioka1004 2019年9月1日 4:44
    2019年8月29日 2:18
    モデレータ
  • ご返信ありがとうございます。

    今後の参考にさせていただきます。

    私の勤め先のIT専門部隊からPowershellやVBはNGが出ており、batでの処理でという

    社内的な問題があり、それで上記のような質問をさせていただきました。

    2019年9月1日 4:42
  • ご返信ありがとうございます。

    今後の参考にさせていただきます。

    私の勤め先のIT専門部隊からPowershellやVBはNGが出ており、batでの処理でという

    社内的な問題があり、それで上記のような質問をさせていただきました。

    また、注意点の方は一度読んでから、次回以降利用させていただきます。

    2019年9月1日 4:44