CUBE SUGAR CONTAINER

技術系のこと書きます。

split コマンドでファイルを分割する

巨大なファイルを扱おうとすると、環境によってはクォータなどの影響を受けて取り回しが悪いことがある。 今回は、そんなときに split コマンドで一つのファイルを複数にばらして扱う方法について。 ここでは macOS を使ったけど GNU/Linux でも同じやり方ができる。

使った環境は次の通り。

$ sw_vers 
ProductName:    Mac OS X
ProductVersion: 10.12.6
BuildVersion:   16G1212
$ python --version
Python 3.6.4

バイナリファイルを分割する

まずは分割するために大きなバイナリファイルを用意する。 ゼロフィルされたものだと連結したとき壊れていないか確かめにくいのでランダムな値で構成した。 具体的には dd コマンドで入力を /dev/random デバイスにする。

$ dd if=/dev/random of=largefile bs=1m count=1000
1000+0 records in
1000+0 records out
1048576000 bytes transferred in 100.697699 secs (10413108 bytes/sec)

これでランダムな値の詰まった 1GB のバイナリファイルができた。 連結したときに内容が変わっていないか確かめるため MD5 のハッシュ値を確認しておこう。

$ ls -alF largefile 
-rw-r--r--  1 amedama  staff  1048576000  2 10 19:08 largefile
$ md5 largefile
MD5 (largefile) = 98fc2a9a7a18d9c8907662a88fb238c8

続いて分割したファイルを入れるディレクトリを用意する。

$ mkdir -p splits

そして split コマンドを使って先ほど作ったバイナリファイルを 100MB ずつに分割する。 バイナリファイルでは -b オプションを指定することで分割サイズが指定できる。 第一引数には分割するファイルを、第二引数には分割したファイルの保存先を指定する。 分割したファイルには後ろにアルファベットがつくので、ドットで終わるようにしておくと後から扱いやすくなる。

$ split -b 100m largefile splits/largefile.

分割したファイルはこんな感じ。

$ ls -alF splits
total 2048000
drwxr-xr-x  12 amedama  staff        408  2 10 19:09 ./
drwxr-xr-x   6 amedama  staff        204  2 10 19:09 ../
-rw-r--r--   1 amedama  staff  104857600  2 10 19:09 largefile.aa
-rw-r--r--   1 amedama  staff  104857600  2 10 19:09 largefile.ab
-rw-r--r--   1 amedama  staff  104857600  2 10 19:09 largefile.ac
-rw-r--r--   1 amedama  staff  104857600  2 10 19:09 largefile.ad
-rw-r--r--   1 amedama  staff  104857600  2 10 19:09 largefile.ae
-rw-r--r--   1 amedama  staff  104857600  2 10 19:09 largefile.af
-rw-r--r--   1 amedama  staff  104857600  2 10 19:09 largefile.ag
-rw-r--r--   1 amedama  staff  104857600  2 10 19:09 largefile.ah
-rw-r--r--   1 amedama  staff  104857600  2 10 19:09 largefile.ai
-rw-r--r--   1 amedama  staff  104857600  2 10 19:09 largefile.aj

分割したら、後はもちろん連結して元に戻すことになる。 split コマンドで分割したファイルは cat コマンドで連結できる。 ようするに順番に内容を出力して、それをファイルにリダイレクトするだけ。

$ cat splits/largefile.* > largefile.restored

分割したファイルを一つに連結したファイルができた。

$ ls -alF largefile.restored 
-rw-r--r--  1 amedama  staff  1048576000  2 10 19:09 largefile.restored

連結したファイルのハッシュ値を確認しておこう。 この値が分割する前のファイルと一致していれば、内容は変わっていないことが分かる。

$ md5 largefile.restored 
MD5 (largefile.restored) = 98fc2a9a7a18d9c8907662a88fb238c8

ハッシュ値は一致するので分割前と分割・連結後で内容は変わっていないことが分かった。

テキストファイルを分割する

先ほどはファイルをバイト単位で分割したけど、テキストファイルとして扱って行数で分割することもできる。 今度はそれを試してみよう。

先ほどと同様、ランダムな値の入ったテキストファイルを用意したい。 今回は Python を使って作ってみることにしよう。

$ python

以下のスニペットで、ランダムな 10 桁の数値が 10000 行連続するテキストファイルを作ってみた。

>>> import random
>>> with open('largefile.txt', 'w') as f:
...     for _ in range(10000):
...         line = '{:010d}\n'.format(random.randint(0, 9999999999))
...         while True:
...           wrote = f.write(line)
...           if wrote == len(line):
...               break
...           line = line[wrote:]
... 

こんな感じのファイルができる。

$ ls -alF largefile.txt 
-rw-r--r--  1 amedama  staff  110000  2 10 19:47 largefile.txt
$ head largefile.txt 
2959925144
8203396354
9888228717
0217007087
6089181232
0800076687
8748631182
4433119315
5389404148
7941794927

先ほどと同じように、連結後のファイルと比較するためのハッシュ値を確認しておこう。

$ md5 largefile.txt 
MD5 (largefile.txt) = 6b98e9d1ec56869609c5cfa29c996f5a

一旦、先ほど分割したファイルはお掃除しておく。

$ rm -rf splits/*

その上でファイルを分割する。 テキストファイルの場合は -l オプションで分割する行数を指定する。

$ split -l 1000 largefile.txt splits/largefile.txt.

このようにファイルが分割された。

$ ls -alF splits 
total 240
drwxr-xr-x  12 amedama  staff    408  2 10 19:49 ./
drwxr-xr-x   5 amedama  staff    170  2 10 19:49 ../
-rw-r--r--   1 amedama  staff  11000  2 10 19:49 largefile.txt.aa
-rw-r--r--   1 amedama  staff  11000  2 10 19:49 largefile.txt.ab
-rw-r--r--   1 amedama  staff  11000  2 10 19:49 largefile.txt.ac
-rw-r--r--   1 amedama  staff  11000  2 10 19:49 largefile.txt.ad
-rw-r--r--   1 amedama  staff  11000  2 10 19:49 largefile.txt.ae
-rw-r--r--   1 amedama  staff  11000  2 10 19:49 largefile.txt.af
-rw-r--r--   1 amedama  staff  11000  2 10 19:49 largefile.txt.ag
-rw-r--r--   1 amedama  staff  11000  2 10 19:49 largefile.txt.ah
-rw-r--r--   1 amedama  staff  11000  2 10 19:49 largefile.txt.ai
-rw-r--r--   1 amedama  staff  11000  2 10 19:49 largefile.txt.aj

wc コマンドで確認すると、ちゃんとそれぞれ 1000 行ずつに分割されているようだ。

$ wc -l splits/largefile.txt.aa
    1000 splits/largefile.txt.aa

続いては分割したファイルを連結する。 やり方はバイト単位で分割したときと同じ。

$ cat splits/largefile.txt.* > largefile.restored.txt

連結できた。

$ ls -alF largefile.restored.txt 
-rw-r--r--  1 amedama  staff  110000  2 10 19:50 largefile.restored.txt

ハッシュ値を確認しよう。 分割前のハッシュ値と一致しているため、分割・連結後も内容が変わっていないことが分かる。

$ md5 largefile.restored.txt
MD5 (largefile.restored.txt) = 6b98e9d1ec56869609c5cfa29c996f5a

めでたしめでたし。