シェルスクリプトでテキストをN行ずつに分割したいが、効率的な方法が思いつかない。
効率的な方法を教えて欲しい。
こんなお悩みを解決します。
今回は、シェルスクリプトによる自動化において、任意の行ずつに分割する方法を解説します。
前提条件を取り上げて解説をするので、シェルスクリプトを用いて開発する際の参考になれば幸いです。
前提条件
例えば、以下のように実行したいコマンドをテキストに書き出した状況を想定します。
/opt/commands/job pattern1
/opt/commands/job pattern2
/opt/commands/job pattern3
/opt/commands/job pattern4
/opt/commands/job pattern5
今回は、split
コマンドを用いて、これらを行ずつに分割する方法を解説します。
また、説明の都合上、以下の2点を前提とします。
- 上記のリストをlists.txtに保存している
- splitコマンドのバージョンは「8.32」とする。
実行例
上記のlists.txtを2行ずつに分割した場合の例を示します。
この場合、3つに分割されることになり、それぞれの内容は以下のようになります。
# 1つ目のファイルの内容
/opt/commands/job pattern1
/opt/commands/job pattern2
# 2つ目のファイルの内容
/opt/commands/job pattern3
/opt/commands/job pattern4
# 3つ目のファイルの内容
/opt/commands/job pattern5
実装例
今回のケースでは、splitコマンドを利用します。※splitコマンドのオプション(一部)はコチラを参照のこと。
ここでは、以下のオプションを利用してファイル分割を行います。
オプション名 | 用途 |
---|---|
-a | 700行を超えるファイルを分割できるようにするため |
-l | 分割する行数を指定するため |
--verbose | splitコマンドの実行により得られたファイルをリネームするため |
実装例は以下のようになります。
#!/bin/bash
count=0
prefix=out
split -a 5 -l 2 --verbose lists.txt | while read dummy target others; do
# 「ファイル 'xaaaaa' を作成しています」という文字列から「xaaaaa」を抽出
filename=${target//\'/}
# mvコマンドでリネーム
mv ${filename} ${prefix}${count}.txt
count=$(expr ${count} + 1)
done
上記を理解する上で、splitコマンドを実行した場合の結果を知っておく必要があります。
splitコマンドの出力
splitコマンドのみの出力結果は以下のようになります。
split -a 5 -l 2 --verbose lists.txt
ファイル 'xaaaaa' を作成しています
ファイル 'xaaaab' を作成しています
ファイル 'xaaaac' を作成しています
ここで、-a
オプションで5を指定しているため、suffix部分(aaaaa
の部分)が5文字となっています。
また、アルファベットは26文字あるため、\(26^{5} = 11,881,376\)行まで対応でき、この程度あれば実用上問題ないと考えます。
動的なファイル分割
上記のシェルスクリプトの場合、決まった長さにしか分割できないため、指定した行数で分割できるようにカスタマイズします。
また、出力ファイル名のprefix(out
の部分)と入力ファイル名も指定できるようにします。
上記を加味した実装例は以下のようになります。
#!/bin/bash
prefix=out
line=2
input=
while [ -n "$1" ]; do
case "$1" in
-p | --prefix )
prefix="$2"
shift
shift
;;
-l | --line )
line="$2"
shift
shift
;;
-i | --input )
input="$2"
shift
shift
;;
* )
shift
;;
esac
done
if [ -z "${input}" ] || [ ! -e ${input} ]; then
echo Error: Input file does not exist. "(${input})"
exit 1
fi
count=0
split -a 5 -l ${line} --verbose ${input} | while read dummy target others; do
filename=${target//\'/}
mv ${filename} ${prefix}${count}.txt
count=$(expr ${count} + 1)
done
上記のシェルスクリプトのオプションは以下のようになります。
オプション | 内容 |
---|---|
-p, --prefix | 出力ファイルのprefix |
-l, --line | 分割時の行数 |
-i, --input | 入力ファイル名 |
上記のシェルスクリプトをlinesplit.sh
として保存した場合、以下のように利用します。
# ======
# 利用例
# ======
# 2行ずつに分割する場合
./linesplit.sh -l 2 -i lists.txt
out0.txt
out1.txt
out2.txt
# 3行ずつに分割&prefixをtmpにする場合
./linesplit.sh -l 3 -p tmp -i lists.txt
tmp0.txt
tmp1.txt
まとめ
今回は、split
コマンドを利用したテキストファイルの分割方法について解説しました。
他にもsplit
コマンドにはオプションが用意されているため、用途に合わせて使い分けてみてください。
この記事がテキストファイルの分割時において、少しでも参考になれば幸いです。