コマンドプロンプト/PowerShellを使った大容量ファイル操作

## はじめに
<!--
何についての記事なのか、誰に向けた記事なのかを簡単に説明しましょう。
-->
本番環境の運用保守に携わって1年ほど経過しましてこれまでの経験で得られた知識を共有するため、また同様の課題に直面している方々の参考になれば幸いです。

この記事でお伝えしたいことは**コマンドプロンプト、PowerShell**を使った大容量のファイル操作のコマンドについてです。



### この記事でわかる・できること
<!--
読んだら何がわかるようになるのか、何ができるようになるのかを書きましょう。
-->

- ファイルの行数カウント
- ファイル分割
- 指定行数のデータ抽出

### この記事の対象者
<!--
誰に向けた記事なのかを明確にすることで読者に選んでもらいましょう。
-->

- システム運用におけるログファイルやデータファイルの処理に課題を感じている方
- なるべく手作業を減らして楽したい方

### 下準備
実行結果を明確に示すために5行と6行のサンプルデータを含むテキストファイルを用意してみました。
これをもとにそれぞれの手順と実行結果を見ていこうと思います。
```test.txt
test1
test2
test3
test4
test5
```
```test2.txt
test1
test2
test3
test4
test5
test6
```

またコマンドプロンプトとPowerShellの使い方に関してはこちらの投稿者様の記事をご参考ください。
[コマンドプロンプトの使い方](https://qiita.com/im1990/items/d653e7ac91b9d235e27a)
[PowerShellの使い方](https://oldnote.net/windows-powershell-learn/)

## ファイルの行数カウント
<!--
章全体の要約や、何について書くのかをここに書きます。
記事が長い時や資料として作るときは番号を振ることをおすすめします。
章を増やすときは章から項までをコピーしてください。
-->
こちらはコマンドプロンプトを使用した手順です。
ファイルが大きすぎて直接開くことが難しい場合に、データ件数を知りたい。
調査対象のファイルが大量にあり、一つずつ開いて確認するのが煩雑だ。
そんな時に便利なものです。

こちらをコマンドプロンプトに入力するとカウント結果が下記のように表示されます。
`find /v /c "" *`

```言語:実行結果
D:\test>find /v /c "" *

---------- TEST.TXT: 5

---------- TEST2.TXT: 6
```

### 解説

まず、`find ファイル名`で指定されたファイル名をフォルダ内から検索します。
`/v`オプションは指定した文字列を含まない行（この場合は空行）を検索対象として、
`/c`オプションは、検索対象の行数を表示します。
`*` の部分を特定のファイル名に置き換えることで、そのファイルのみをカウントすることも可能です。

またPowerShellでも同様に行数カウントが可能です。
`(Get-Content ファイル名).Length`
```言語:実行結果
PS D:\test> (Get-Content test.txt).Length
5
```

### 解説
`Get-Content ファイル名`でファイルの内容を読み込みます。
`.Length`プロパティで行数をカウントします。

## ファイル分割
続いてはファイル分割についてです。こちらはPowerShellを使います。
Excelで処理するためにファイルを貼り付けたいが、ファイルサイズが大きすぎてメモリ不足になる。
Excelで扱える行数を超えるため、ファイルを貼り付けることができない。
そんな時にファイルを指定行数で分割して扱いやすくすることができます。

そんな時にPowerShellにて使うコマンドレットです。
`$i=1; Get-Content -Encoding UTF8 ファイル名 -ReadCount 分割したい行数 | % { Write-Output $_ | Out-File -Encoding UTF8 ファイル名_$i ;$i++}`

コマンドが複雑に見えますが、とりあえず実行画面と結果を確認しましょう。

```言語:実行画面
PS D:\test> $i=1; Get-Content -Encoding UTF8 test.txt -ReadCount 2 | % { Write-Output $_ | Out-File -Encoding UTF8 test_$i.txt ;$i++}
```
実行結果を見ると元ファイルと同じフォルダ内に2行ずつに分割されたテキストが生成されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4044527/714d4d17-8894-4ce6-b36f-c55094c67f32.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4044527/fbf3423e-84b8-4af9-9232-0527e9b558bf.png)

### 解説
`$i = 1:`
* 変数$iを初期化し、値1を代入します。この変数は、分割後のファイル名を生成するために使用されます

`Get-Content -Encoding UTF8 ファイル名 -ReadCount 数字:`
* Get-Contentコマンドレットは、ファイルの内容を読み込み、ストリームとしてデータを処理します
* -Encoding UTF8オプションは、ファイルをUTF-8エンコーディングで読み込むように指定します
* ファイル名は、読み込むファイルのパスを指定します
* -ReadCount 分割したい行数は、一度に読み込む行数を指定します。数字には、分割したい行数を指定します

`| % { ... }:`
* |（パイプライン）は、Get-Contentコマンドレットの出力を次のコマンドレットに渡します
* %は、ForEach-Objectコマンドレットのエイリアスであり、パイプラインで渡された各オブジェクト（ここでは、指定された行数ごとのファイルの塊）に対して、{ ... }で囲まれたスクリプトブロックを実行します

`Write-Output $_:`
* Write-Outputコマンドレットは、オブジェクトをパイプラインに出力します
* $_は、パイプラインで渡された現在のオブジェクト（ファイルの塊）を表します

`Out-File -Encoding UTF8 ファイル名 ; $i++:`
* Out-Fileコマンドレットは、オブジェクトをファイルに書き込みます
* ファイル名は、書き込むファイルのパスを指定します。ここでは、元のファイル名に連番を付加するなどして、分割後のファイル名を生成する必要があります。例："ファイル名_$i.txt"
* ;は、複数のコマンドを同じ行に記述するための区切り文字です
* \\$i++は、変数$iの値を1増やします。これにより、分割後のファイル名に連番が付加されます

### 注意点
 * Out-Fileコマンドレットで生成するファイル名は、元のファイル名と重複しないように注意してください
 * ファイルの行数が指定された行数で割り切れない場合、最後のファイルは指定された行数よりも少なくなることがあります
 * ファイルのサイズが大きい場合は、処理に時間がかかり、メモリを圧迫する可能性があります






## 行数を指定してファイル内のデータを抽出する
最後に行数を指定してファイル内のデータを抽出する方法についてです。
ジョブが異常終了し、ログに「○○ファイルの××行目でエラーが発生しました」と出力された場合を想定します。
ファイルサイズが大きすぎて直接開けない場合、ファイル分割などの手段で目的のデータにたどり着く必要があり、非常に煩雑です。
そんな時にPowerShellにて使うコマンドレットです。


`(Get-Content -Encoding UTF8 ファイル名)[取得したい行数-1]`
```言語:実行結果
PS D:\test> (Get-Content -Encoding UTF8 test.txt)[4]
test5
```

### 解説
`[取得したい行数-1]`
配列のインデックスは0から始まるため、5行目のデータを取得する場合は、[4]を指定します。
# おわりに
ここまで読んでいただきありがとうございます。
この記事がお役に立てれば幸いです。