はじめに
Linuxでファイルを分割したり結合する場合に、意外と知られていない便利な方法があるので紹介します。
各コマンドの詳細な説明は割愛しますが、ファイルの分割・結合の参考になれば幸いです。
ファイルを分割する方法
・split
コマンド(行/バイト単位で分割)
代表的なファイル分割のコマンドとしてsplit
コマンドがあります。指定した行数やバイト数毎にファイルを分割したり、分割するファイル数を指定できたりと、柔軟な分割を行う事ができます。
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/01 210
4 3 2022/11/13 150
5 2 2022/10/16 30
6 4 2022/11/15 100
7 1 2022/10/16 200
8 1 2022/08/21 70
9 3 2022/09/08 105
10 2 2022/11/11 250
- 行数を指定して分割 (
-l
オプション)
$ ls
hoge.txt
$ split -l 3 hoge.txt
$ ls
hoge.txt xaa xab xac xad
$ cat xaa
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/01 210
$
- 分割するファイル数を指定して分割 (
-n
オプション)
$ ls
hoge.txt
$ split -n l/3 hoge.txt
$ ls
hoge.txt xaa xab xac
$ cat xaa
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/01 210
4 3 2022/11/13 150
$
-n
オプションを指定すると、指定したファイル数で均等に分割されます。ただ、行の途中で分割されてしまうので、それを回避する場合はl/(ファイル数)
でファイル数を指定する必要があります。
- バイト数を指定して分割 (
-b
オプション)
$ ls
hoge.txt
$ split -b 50 hoge.txt
$ ls
hoge.txt xaa xab xac xad xae
$ cat xaa
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/
$
通常split
コマンドを使用すると分割したデータはファイルに出力されます。ただ、--filter
オプションを使用する事と、分割したデータをひとまとまりとしてコマンドに渡して処理させる事ができます。
$ split -l 2 hoge.txt --filter="cat;echo --"
1 4 2022/11/16 100
2 2 2022/10/22 101
--
3 1 2022/09/01 210
4 3 2022/11/13 150
--
5 2 2022/10/16 30
6 4 2022/11/15 100
--
7 1 2022/10/16 200
8 1 2022/08/21 70
--
9 3 2022/09/08 105
10 2 2022/11/11 250
--
また、分割したデータ毎にコマンドの処理が実行される特性を利用すると、下記のように集計として利用する事も可能です。
$ split -l 2 hoge.txt --filter="awk '{s+=\$4}END{print s}'"
201
360
130
270
355
$
split
コマンドは単純にファイルを分割してファイルに出力するだけでなく、分割されたデータをコマンドで処理できるので幅広く応用する事ができます。
・csplit
コマンド(文字列パターンで分割)
意外と知られていませんが、文字列パターン毎にファイルを分割する事ができるcsplit
というコマンドがあります。
行数やサイズではなく特定の文字列でファイルを分割する事ができるので、ログファイルやDB等のdumpファイルの調査・解析・作成する際などに有用です。
1 4 2022/11/16 100
2 2 2022/10/22 101
--
3 1 2022/09/01 210
4 3 2022/11/13 150
--
5 2 2022/10/16 30
6 4 2022/11/15 100
--
7 1 2022/10/16 200
8 1 2022/08/21 70
--
9 3 2022/09/08 105
10 2 2022/11/11 250
- パターンを指定して分割 (引数
//
)
$ ls
hoge.txt
$ csplit hoge.txt '/--/' '{*}'
38
41
40
40
42
$ ls
hoge.txt xx00 xx01 xx02 xx03 xx04
$ cat xx00
1 4 2022/11/16 100
2 2 2022/10/22 101
$
- 行数を指定して分割(引数 数値)
$ ls
hoge.txt
$ csplit hoge.txt 3 '{3}'
38
41
40
40
42
$ ls
hoge.txt xx00 xx01 xx02 xx03 xx04
$ cat xx00
1 4 2022/11/16 100
2 2 2022/10/22 101
$
単純に行数で分割したい場合はsplit
コマンドをお勧めします。
csplit
コマンドでは行数を指定したファイルの分割を行えますが、分割するファイル数を繰り返し({*}
)として指定した場合、最後の分割でエラーが発生します。
-k
オプションを付けると分割したファイル自体は出力されますが、あくまで指定した行数をマッチしたとして扱うため1ファイル目は指定した行数 -1しか出力しないので注意してください。
$ ls
hoge.txt
$ csplit -k hoge.txt 3 '{*}'
38
41
40
40
42
csplit: '3': 範囲外の行番号 繰り返し 4 回目
$ ls
hoge.txt xx00 xx01 xx02 xx03 xx04
$ cat xx00
1 4 2022/11/16 100
2 2 2022/10/22 101
$ cat xx01
--
3 1 2022/09/01 210
4 3 2022/11/13 150
$
ただ、指定した行数をマッチしたとして扱うので、パターンにマッチした行を出力しない--suppress-matched
オプションなども使用可能です。
$ csplit -k hoge.txt 3 '{*}' --suppress-matched
38
38
37
37
39
csplit: `3': 範囲外の行番号 繰り返し 5 回目
0
$ ls
hoge.txt xx00 xx01 xx02 xx03 xx04 xx05
$ cat xx00
1 4 2022/11/16 100
2 2 2022/10/22 101
$ cat xx01
3 1 2022/09/01 210
4 3 2022/11/13 150
$
・awk
コマンド(グループ単位で分割)
awk
コマンドはいわゆるフィルタコマンドになりますが、意外と高速にファイル分割も行えます。
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/01 210
4 3 2022/11/13 150
5 2 2022/10/16 30
6 4 2022/11/15 100
7 1 2022/10/16 200
8 1 2022/08/21 70
9 3 2022/09/08 105
10 2 2022/11/11 250
- 特定列の値単位でファイルを分割
$ ls
hoge.txt
$ awk '{file="group"$2;print $0 >> file}' hoge.txt
$ ls
group1 group2 group3 group4 hoge.txt
$ cat group1
3 1 2022/09/01 210
7 1 2022/10/16 200
8 1 2022/08/21 70
$ cat group2
2 2 2022/10/22 101
5 2 2022/10/16 30
10 2 2022/11/11 250
$
- 偶数行・奇数行でファイルを分割
$ ls
hoge.txt
$ awk '{file="line"NR%2;print $0 >> file}' hoge.txt
$ ls
hoge.txt line0 line1
$ cat line0
2 2 2022/10/22 101
4 3 2022/11/13 150
6 4 2022/11/15 100
8 1 2022/08/21 70
10 2 2022/11/11 250
$
- 行数を指定してファイルを分割 (一応
split
コマンドと同様の分割も可能)
$ ls
hoge.txt
$ awk 'BEGIN{c=0}{file="xx"c;print $0 >> file;NR%3==0&&c++}' hoge.txt
$ ls
hoge.txt xx0 xx1 xx2 xx3
$ cat xx0
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/01 210
$
上記はコマンド実行を行なったディレクトリに分割ファイルが展開されますが、別のパスに分割ファイルを展開したい場合は下記の様にファイル名にパスを付与する事ができます。
awk '{file="tmp/group"$2";print $0 >> file}' hoge.txt
リダイレクトの箇所にパスの文字列を付与することは出来ないので注意してください。
・cut
コマンド(横に分割)
今までファイルを縦(行)に分割していましたが、横(列)に分割したい場合はcut
コマンドを使用します。
また、split
コマンドなどの一括分割できるコマンドがないので、地道に分割したい箇所を抽出していく必要があります。
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/01 210
4 3 2022/11/13 150
5 2 2022/10/16 30
6 4 2022/11/15 100
7 1 2022/10/16 200
8 1 2022/08/21 70
9 3 2022/09/08 105
10 2 2022/11/11 250
- 縦に2つにファイルを分割
$ ls
hoge.txt
$ cut -d " " -f 1-2 hoge.txt > col0
$ cut -d " " -f 3-4 hoge.txt > col1
$ ls
col0 col1 hoge.txt
$ cat col0
1 4
2 2
3 1
4 3
5 2
6 4
7 1
8 1
9 3
10 2
$ cat col1
2022/11/16 100
2022/10/22 101
2022/09/01 210
2022/11/13 150
2022/10/16 30
2022/11/15 100
2022/10/16 200
2022/08/21 70
2022/09/08 105
2022/11/11 250
$
ファイルを結合する方法
・cat
コマンド(分割ファイルを縦に結合)
代表的なファイル結合のコマンドとしてcat
コマンドがあり、指定した複数の分割ファイルを1つに結合する事ができます。
上記で紹介したsplit
、csplit
、awk
コマンドで分割したファイルはcat
コマンドで1つのファイルに結合できます。
$ ls
xx00 xx01 xx02 xx03 xx04 xx05
$ head xx*
==> xx00 <==
1 4 2022/11/16 100
2 2 2022/10/22 101
==> xx01 <==
3 1 2022/09/01 210
4 3 2022/11/13 150
==> xx02 <==
5 2 2022/10/16 30
6 4 2022/11/15 100
==> xx03 <==
7 1 2022/10/16 200
8 1 2022/08/21 70
==> xx04 <==
9 3 2022/09/08 105
10 2 2022/11/11 250
==> xx05 <==
$
- 複数の分割ファイルを結合
$ cat xx00 xx01 xx02 xx03 xx04 xx05 > hoge.txt
$ ls
hoge.txt xx00 xx01 xx02 xx03 xx04 xx05
$ cat hoge.txt
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/01 210
4 3 2022/11/13 150
5 2 2022/10/16 30
6 4 2022/11/15 100
7 1 2022/10/16 200
8 1 2022/08/21 70
9 3 2022/09/08 105
10 2 2022/11/11 250
$
cat
コマンドは引数で指定された順番にファイルを結合していくので、下記の通り順番を入れ替えて結合する事もできる。
$ cat xx05 xx04 xx03 xx02 xx01 xx00
9 3 2022/09/08 105
10 2 2022/11/11 250
7 1 2022/10/16 200
8 1 2022/08/21 70
5 2 2022/10/16 30
6 4 2022/11/15 100
3 1 2022/09/01 210
4 3 2022/11/13 150
1 4 2022/11/16 100
2 2 2022/10/22 101
$
また、分割ファイルが大量にある場合はワイルドカード(*
)を使用して結合する事もできます。
$ cat xx*
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/01 210
4 3 2022/11/13 150
5 2 2022/10/16 30
6 4 2022/11/15 100
7 1 2022/10/16 200
8 1 2022/08/21 70
9 3 2022/09/08 105
10 2 2022/11/11 250
$
ワイルドカード以外にもブレース展開({}
)を使用して結合する事もできます。
$ cat xx0{0..5}
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/01 210
4 3 2022/11/13 150
5 2 2022/10/16 30
6 4 2022/11/15 100
7 1 2022/10/16 200
8 1 2022/08/21 70
9 3 2022/09/08 105
10 2 2022/11/11 250
$
・paste
コマンド(分割ファイルを横に結合)
cut
コマンドなどで分割されているファイルを横(列)に結合したい場合はpaste
コマンドを使用します。
また、split
コマンドなどの一括分割できるコマンドがないので、地道に分割したい箇所を抽出していく必要があります。
$ ls
col0 col1
$ head col*
==> col0 <==
1 4
2 2
3 1
4 3
5 2
6 4
7 1
8 1
9 3
10 2
==> col1 <==
2022/11/16 100
2022/10/22 101
2022/09/01 210
2022/11/13 150
2022/10/16 30
2022/11/15 100
2022/10/16 200
2022/08/21 70
2022/09/08 105
2022/11/11 250
$
- 複数の分割ファイルを横に結合
$ ls
col0 col1
$ paste -d " " col0 col1 > hoge.txt
$ ls
col0 col1 hoge.txt
$ cat hoge.txt
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/01 210
4 3 2022/11/13 150
5 2 2022/10/16 30
6 4 2022/11/15 100
7 1 2022/10/16 200
8 1 2022/08/21 70
9 3 2022/09/08 105
10 2 2022/11/11 250
$
・join
コマンド(RDBのように横に結合)
こちらも意外と知られていませんが、分割されたファイル同士をRDBのテーブルのようにリレーション(共通項目)を使用して横に結合する事ができます。
3 1 2022/09/01 210
7 1 2022/10/16 200
8 1 2022/08/21 70
10 2 2022/11/11 250
2 2 2022/10/22 101
5 2 2022/10/16 30
4 3 2022/11/13 150
9 3 2022/09/08 105
1 4 2022/11/16 100
6 4 2022/11/15 100
1 Group1
2 Group2
3 Group3
4 Group4
- 二つのファイルを共通項目で結合
$ join -1 2 -2 1 hoge.txt master.txt
1 3 2022/09/01 210 Group1
1 7 2022/10/16 200 Group1
1 8 2022/08/21 70 Group1
2 10 2022/11/11 250 Group2
2 2 2022/10/22 101 Group2
2 5 2022/10/16 30 Group2
3 4 2022/11/13 150 Group3
3 9 2022/09/08 105 Group3
4 1 2022/11/16 100 Group4
4 6 2022/11/15 100 Group4
$
join
コマンドは結合に使用する共通項目がファイル内でソートされている必要があります。
1 4 2022/11/16 100
2 2 2022/10/22 101
3 1 2022/09/01 210
4 3 2022/11/13 150
5 2 2022/10/16 30
6 4 2022/11/15 100
7 1 2022/10/16 200
8 1 2022/08/21 70
9 3 2022/09/08 105
10 2 2022/11/11 250
2 Group2
1 Group1
4 Group4
例えば上記のように共通項目がソートされていないファイルの場合は一度ソートしたファイルに修正するか、下記のようにプロセス置換(<()
)を使用してファイルをソートしたデータを渡す必要があります。
$ join -a 1 -1 2 -2 1 <(sort -k 2n hoge.txt) <(sort -k 1n master.txt) | sort -k 2n
4 1 2022/11/16 100 Group4
2 2 2022/10/22 101 Group2
1 3 2022/09/01 210 Group1
3 4 2022/11/13 150
2 5 2022/10/16 30 Group2
4 6 2022/11/15 100 Group4
1 7 2022/10/16 200 Group1
1 8 2022/08/21 70 Group1
3 9 2022/09/08 105
2 10 2022/11/11 250 Group2
$
また、join
コマンドでは出力する項目の順番や結合方法など細かな操作ができるので、興味がある方はコマンドを調べてみることをお勧めします。
$ join -a 1 -o 1.1,2.2,1.3,1.4 -1 2 -2 1 <(sort -k 2n hoge.txt) <(sort -k 1n master.txt) | sort -k 1n
1 Group4 2022/11/16 100
2 Group2 2022/10/22 101
3 Group1 2022/09/01 210
4 2022/11/13 150
5 Group2 2022/10/16 30
6 Group4 2022/11/15 100
7 Group1 2022/10/16 200
8 Group1 2022/08/21 70
9 2022/09/08 105
10 Group2 2022/11/11 250
$