Linuxでsedとsplitを使って大容量ファイルを自動でチャンク分割する方法

概要

今回はLinuxで大容量ファイルをsedとsplitコマンドで分割する方法について紹介していきます。

自分は数百万行あるCSVやログファイルをテキストエディタで開こうとして、フリーズさせてしまったことが何度もあります(- -;

自分も先日、1000万行超えのCSVを扱うことになり、まずはsedで行範囲を指定しながら分割しようとしたのですが、範囲の指定を少しずつずらすつもりが計算を間違え、同じ行が2つのファイルに重複して出力されてしまったことがありました。

そこでsedで行範囲を手動指定して分割する方法の注意点と、splitコマンドで自動的に一定行数ごとに分割する方法をあわせて整理してみました。

それではやっていきましょう!

目次

sedで行範囲を指定してファイルを分割する

1つ目はsedコマンドで行範囲を指定し、複数のファイルに切り出していく方法です。

大まかな行数の区切りが決まっている場合や、特定の範囲だけをピンポイントで抜き出したいときに使います。

基本構文

コマンド

1
sed -n '[開始行],[終了行]p' input.csv >> output.csv
  • -n
    • 通常の出力を抑制し、pで指定した行だけを出力
  • '[開始行],[終了行]p'
    • 出力したい行の範囲
  • >>
    • 追記モード。既存ファイルの末尾に追加する

実行例(100万行ずつ3ファイルに分割)

100万行ずつ区切って、3つのファイルに分割する場合は下記のようになります。

コマンド

1
2
3
sed -n '1,1000000p' HOGE.csv >> HOGE_1.csv
sed -n '1000001,2000000p' HOGE.csv >> HOGE_2.csv
sed -n '2000001,3000000p' HOGE.csv >> HOGE_3.csv
範囲の終わりと次の範囲の始まりを1行ずらさないと、境界の行が2つのファイルに重複して出力されてしまいます。

自分も一度、終了行と次の開始行を同じ数字にしてしまい、同じ行が2ファイルに入ってしまったことがあるので気をつけてください(- -;

手動指定を繰り返すのは大変なのでループにする

分割したい件数が多いと、範囲を毎回手で計算するのは現実的ではありません。

そういったときは、シェルのループで開始行・終了行を自動計算しながらsedを繰り返し実行するとラクになります。

コマンド

1
2
3
4
5
6
7
8
9
lines_per_file=1000000
total_lines=$(wc -l < HOGE.csv)
file_count=$(( (total_lines + lines_per_file - 1) / lines_per_file ))

for i in $(seq 1 "$file_count"); do
start=$(( (i - 1) * lines_per_file + 1 ))
end=$(( i * lines_per_file ))
sed -n "${start},${end}p" HOGE.csv > "HOGE_${i}.csv"
done

出力を>(上書き)にしているのもポイントで、ループのたびに新しいファイルとして作り直すので追記の>>にする必要はありません。

sedでの範囲指定は、途中の特定範囲だけをピンポイントで抜き出したいときにとても便利ですが、ファイル全体を機械的に分割したいだけなら次に紹介するsplitの方がシンプルです。

splitコマンドで行数ごとに自動分割する

2つ目はsplitコマンドを使って、指定した行数ごとに自動でファイルを分割する方法です。

範囲を自分で計算する必要がないので、ファイル全体を均等に割りたいときはこちらが手軽です。

基本構文

コマンド

1
split -l [分割行数] -d --additional-suffix="[任意の文字列]" input.txt output/prefix
  • -l [数字]
    • 1ファイルあたりの行数
  • -d
    • 出力ファイル名の連番を数字にする(デフォルトはアルファベット)
  • --additional-suffix
    • 出力ファイル名の末尾に付与する文字列
  • output/prefix
    • 出力先ディレクトリと、出力ファイル名の先頭に付ける文字列

実行例(1万行ごとに分割)

1万行ごとに分割し、末尾に__を付けたファイル名で出力したい場合は下記のようになります。

コマンド

1
split -l 10000 -d --additional-suffix="__" aaa.txt output/aaa.txt

実行するとoutput/aaa.txt00__output/aaa.txt01__のように連番付きで出力されます。

splitは出力先のディレクトリを自動で作成してくれません。 output/ディレクトリが存在しないとエラーになるので、実行前に`mkdir -p output`しておいてください。

オプション名さえ覚えていればsplitの方が圧倒的に楽ですが、途中の特定範囲だけ抜き出したいといったピンポイントの用途には向かないので、そこは使い分けが必要です。

sedとsplit、どちらを使うべきか

  • 特定の範囲だけ抜き出したい
    • sed -nで開始行・終了行を指定する方法が向いている
  • ファイル全体を均等に分割したい
    • split -lで自動分割する方法が向いている
  • 分割数が多い・毎回範囲を変えたい
    • sedを使う場合はループで開始行・終了行を自動計算すると手間が減る

迷ったら、まずはsplitで機械的に割ってみて、範囲を細かく調整したい部分だけsedで対応する、という使い分けがおすすめです。

分割後にファイルを結合したい場合は、Linuxでテキストファイルを分割する方法【サンプル有】(split と sed)🔗 shinpinoshi.comで紹介したcatコマンドでの結合方法もあわせて参考にしてみてください。

まとめ

Linuxで大容量ファイルをsedsplitで分割する方法を紹介しました。

範囲を手動でずらす方法は自由度が高い反面、境界のずれによる重複行が起きやすいので、機械的に割れる場面ではsplitに任せてしまうのがおすすめです。

デカいファイルを開くときの待ち時間は無駄でしかないので、分割してある程度めぼしい行数のファイルを確認すると効率が良いですよね(^^

以上となります。
本番のログファイルを開くときは注意!(^^
それではお疲れさまでした。