ファイルを二分割する
レギュレーション
- ファイルを受け取り、それを二分割して二つのファイルを生成せよ。
- 分割する条件は行数とする(32768行目で分割、等)。
- 入力はメモリに乗りきらないことも考慮せよ。
-
awk(1)
,perl(1)
等のワンライナーを使ってはならない。それで実現可能なのは自明であり面白くない。 -
bash(1)
,zsh(1)
等の拡張機能を使ってはならない。シェルはdash(1)
とする。
テストファイル
$ yes yes | head -c 40000 > input.txt
$ wc input.txt
10000 10000 40000 input.txt
$
不合格な回答
split
split(1)
といういかにもな名前のプログラムはこの用途には使えない。
$ split -l 4096 input.txt output
$ wc output*
4096 4096 16384 outputaa
4096 4096 16384 outputab
1808 1808 7232 outputac
10000 10000 40000 total
望まないファイル outputac
が生成されており要求仕様を満たさない。(ただし下記を見よ)
head; cat
(head -n ${x} > output1.txt; cat > output2.txt) < input.txt
この問題をググると出て来る回答 https://superuser.com/a/624240 これはNG。なんでかというとhead(1)
がバッファリングするからデータが欠ける。
$ (head -n 7 > output1.txt; cat > output2.txt) < input.txt
$ wc output*
7 7 28 output1.txt
8976 8976 35904 output2.txt
8983 8983 35932 total
7行出力するのに4096バイト読んじゃったから、足しても元に戻らない。
なお、head(1)
の実装によっては頑張って無駄な読み込みを避けようとするものもあるようだ。が、絶対保証できるかというとパイプがseekで読み戻せないと厳しい気がするのでkernel依存の気がする。
間違いではないが難のある回答
head -n 負の数
head -n ${x} input.txt > output1.txt
head -n -${x} input.txt > output2.txt
これはcoreutilsの拡張機能だ。POSIXを逸脱しており、移植性に問題がある。
wc; head; tail
x=1024
y=$(wc -l input.txt)
z=$((${x} - ${y}))
head -n ${x} input.txt > output1.txt
tail -n ${z} input.txt > output2.txt
これは入力ファイルを3回スキャンしている。入力がめちゃくちゃ大きい時にペナルティが大きい。
split その2
split -l ${x} input.txt output
mv outputaa output1.txt
cat output?? > output2.txt
rm output??
上の方で split(1)
は要求仕様を満たさないとしたが、切りすぎてるだけなので、あとからまた混ぜればいいじゃんという発想はあり得る。
上記のようにcatで後半を混ぜればレギュレーション通りの結果は得られる。ただし入力を二回スキャンすることになり、無駄ではある。
q
q -k "SELECT * FROM input.txt LIMIT ${x}" > output1.txt
q -k "SELECT * FROM input.txt WHERE ROWID > ${x}" > output2.txt
q(1)
は、処理中にインメモリDBを作っているので、メモリに乗りきらない入力に対して不適切である。
あとawkがNGってレギュレーションなのにSQLがOKとするというのはちょっとずるい。
文句のない回答
csplit
csplit -f output input.txt $((${x} + 1))
csplit(1)
はPOSIXに古くからあるコマンドであり移植性が高い。普通はファイルを正規表現で切るという使い方で解説されていることが多いが、行番号で切ることもできる。ただ+1する必要があるのがポイントか。
シェルのreadで
n=0
x=1024
while IFS= read -r line || [ -n "${line}" ]; do
if [ ${n} -lt ${x} ]; then
output=output1.txt
else
output=output2.txt
fi
n=$((${n} + 1))
echo ${line} >> ${output}
done < input.txt
いくらdashの機能が貧弱とはいえこのていどのシェルスクリプトは実現可能である。
Author And Source
この問題について(ファイルを二分割する), 我々は、より多くの情報をここで見つけました https://qiita.com/shyouhei/items/464e0c4e11728f3e9c65著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .