サーバーから落としてきたログが数百MBあって、エディタで開いた瞬間に固まった。
そんなときは、まるごと扱おうとせず、先に小さく割ってしまうほうが早いです。
split は、1本の大きなファイルを決まった行数やサイズで切り分けてくれるコマンド。
分けたあとは cat でつなげば元どおりになるので、往復のコストもほとんどかかりません。
行数で割る・サイズで割る・つなぎ直すの3つに加えて、私がつまずいた落とし穴も、実際に走らせた結果で確かめてあります。
split は「大きい1本」を「小さい束」に変える
split は入力を一定の単位で切り出し、連番のついた複数のファイルへ書き出すコマンドです。
基本形はこれだけ。
split [オプション] 対象ファイル 出力の接頭辞
接頭辞を省くと x で始まる名前になるので、part_ のように自分で決めておくと後片付けが楽になります。
大事なのは、元のファイルには一切手を触れないという点。
断片を書き出すだけなので、失敗しても生成されたファイルを消せば元の状態に戻せます。
出番が多いのは、エディタが音を上げる大きさのログを切り出すときや、転送サイズに上限のある経路へファイルを渡すとき。
全体をひとつの塊として扱うのをやめるだけで、後続の作業がぐっと軽くなるのです。
行数で割る -l、サイズで割る -b
テキストなら行数、バイナリならサイズで割るのが基本の使い分けになります。
1,000行のファイルを300行ずつに割るなら、書き方は次のとおりです。
# 1,000行のファイルを300行ずつに分割
split -l 300 numbers.txt part_
# 500KB のバイナリを 200KB ずつに分割
split -b 200K big.bin chunk_
実際に走らせると part_aa から part_ad までの4本になり、最後の part_ad だけが100行の端数として残りました。
サイズ指定のほうは K・M・G の接尾辞 がそのまま使えます。
500KB のバイナリに -b 200K を指定したところ、204800 バイトの断片が2本と、余りの 102400 バイトが1本という内訳。
ただし バイナリを -l で割ってはいけません。
改行コードと同じ並びのバイトを行の区切りと解釈してしまい、意味のない位置で切られてしまうからです。
結合は cat だけで足りる
分割したファイルを元に戻すのに、専用のコマンドは要りません。
cat でまとめて連結し、ハッシュ値を比べれば往復できたかどうかまで確認できます。
# 断片を連結して元に戻す
cat chunk_* > restored.bin
# 分割前と結合後のハッシュを比べる
sha256sum big.bin restored.bin
手元で試したときは、分割前と結合後の sha256 が完全に一致しました。
ハッシュ値で中身の一致を確かめる手順は、sha256sum の記事にまとめています。
グロブ展開で順番が狂わないのは、split が付ける連番が 辞書順=分割順になるように設計されている から。
数字の連番にしたければ -d、拡張子を足したければ --additional-suffix=.txt を添えます。
分割しながら圧縮したいときに効いてくるのが --filter です。
# 分割しながら gzip 圧縮する
split -l 400 --filter='gzip > $FILE.gz' numbers.txt g_
断片が g_aa.gz のような形で直接書き出されるので、中間ファイルを置かずに済むのが気持ちいいところ。
つまずきやすい3つの落とし穴
ここからは、実際に手を動かして踏んだところを3つ挙げておきます。
1つ目は -n の挙動。
「3等分」のつもりで split -n 3 を使うと、行の途中で平気で切られます。
3,893バイトのテキストを3等分したら 1297 / 1297 / 1299 バイトに割れ、352 という行が 3 と 52 に分断されていました。
行を壊したくないときは -n l/3 と書きます(小文字のエルとスラッシュ)。
こちらで割り直したら 352 / 324 / 324 行になり、境界がきちんと行末にそろいました。
2つ目はサフィックスの枯渇。
-a 2 を明示すると 26×26=676 ファイルで split: output file suffixes exhausted と止まってしまいます。
逆に -a を書かなければ、GNU split が桁を自動で伸ばしてくれる。
3,000行を3行ずつ割ったら1,000ファイルができ、名前は s_zaaz のように伸びていました。
3つ目は CSV のヘッダです。
split は中身の意味を見ないので、見出し行は先頭のファイルにしか残りません。
本文だけを渡してから、あとでヘッダを付け直すのが確実な進め方。
# ヘッダを除いた本文だけを分割する
tail -n +2 data.csv | split -l 1000 - part_
# 各ファイルの先頭にヘッダを付け直す
for f in part_*; do
{ head -1 data.csv; cat "$f"; } > "$f.csv"
rm "$f"
done
ここまでの実行例は GNU coreutils 8.32 で確認したものです。
macOS 標準の split は BSD 版でオプションが異なるため、手元では man split を一度のぞいてみてください。
ディレクトリごと分けたいときは、先に tar でひとつにまとめてから split に渡すと扱いやすくなります。
最後に
行数なら -l、サイズなら -b、戻すのは cat。
この3つを押さえておけば、巨大なログもデータも扱える大きさまで落とせます。
迷ったら -n ではなく -l か -b を選び、行を壊したくないときだけ -n l/N に切り替える。
分けて、確かめて、また戻す。
この往復ができると分かっているだけで、大きなファイルへの身構えかたが変わります。
以上です。











コメントを残す