web-dev-qa-db-ja.com

効率的にマージ/ソート/ユニークな多数のテキストファイル

私は素朴なことを試しています:

$ cat * | sort -u > /tmp/bla.txt

これは失敗します:

-bash: /bin/cat: Argument list too long

したがって、(巨大な一時ファイルを作成する)のようなばかげた解決策を避けるために:

$ find . -type f -exec cat {} >> /tmp/unsorted.txt \;
$ cat /tmp/unsorted.txt | sort -u > /tmp/bla.txt

私は私が使用してファイルを1つずつ処理することができましたが(これはメモリ消費を減らし、ストリーミングメカニズムに近いはずです):

$ cat proc.sh
#!/bin/sh
old=/tmp/old.txt
tmp=/tmp/tmp.txt
cat $old "$1" | sort -u > $tmp
mv $tmp $old

その後に続く:

$ touch /tmp/old.txt
$ find . -type f -exec /tmp/proc.sh {} \;

ファイルの数がcat * | sort -uに達したときに、MAX_ARGのより簡単なUNIXスタイルの置き換えはありますか?このような一般的なタスク用の小さなシェルスクリプトを作成するのは面倒です。

8
malat

GNU sort、およびprintfが組み込まれているシェル(最近のpdkshの一部のバリアントを除くすべてのPOSIXのようなもの)) :

_printf '%s\0' * | sort -u --files0-from=- > output
_

ここでの問題は、そのパイプラインの2つのコンポーネントが同時に独立して実行されるため、左側のコンポーネントが_*_グロブを展開するまでに、右側のコンポーネントがoutputファイルを作成した可能性があるということですoutputは入力ファイルと出力ファイルの両方になるため、すでに問題が発生している可能性があります(おそらく_-u_では発生しません)。そのため、出力を別のディレクトリ(_> ../output_たとえば)、またはグロブが出力ファイルと一致しないことを確認します。

このインスタンスでそれに対処する別の方法は、それを書くことです:

_printf '%s\0' * | sort -u --files0-from=- -o output
_

こうすることで、書き込みのためにsortを開くoutputとなり、(私のテストでは)ファイルの完全なリストを受け取るまでは(グロブが展開された後は)実行されません)。また、読み取り可能な入力ファイルがない場合は、outputの破壊を回避します。

zshまたはbashで書き込む別の方法

_sort -u --files0-from=<(printf '%s\0' *) -o output
_

これは、プロセス置換を使用しています(<(...)は、printfが書き込みを行うパイプの読み取り側を参照するファイルパスに置き換えられています)。その機能はkshに由来しますが、ksh<(...)の展開をコマンドへの個別の引数にすることを主張しているため、--option=<(...)構文。ただし、この構文で機能します。

_sort -u --files0-from <(printf '%s\0' *) -o output
_

改行文字で終わっていないファイルがある場合、ファイルにcatの出力をフィードするアプローチとは異なることに注意してください。

_$ printf a > a
$ printf b > b
$ printf '%s\0' a b | sort -u --files0-from=-
a
b
$ printf '%s\0' a b | xargs -r0 cat | sort -u
ab
_

また、sortはロケール(strcollate())の照合アルゴリズムを使用してソートし、_sort -u_はそのアルゴリズムで同じようにソートする各行セットの1つを報告し、一意ではないことに注意してください。バイトレベルの行。行がバイトレベルで一意であることにのみ関心があり、並べ替えの順序にはあまり関心がない場合は、並べ替えがバイト値に基づいているCにロケールを修正することができます(memcmp();それはおそらくかなりスピードアップするでしょう):

_printf '%s\0' * | LC_ALL=C sort -u --files0-from=- -o output
_
8

printfが組み込まれており、コマンドライン引数の制限が適用されないため、少なくともBashでは簡単な修正が機能します。

printf "%s\0" * | xargs -0 cat | sort -u > /tmp/bla.txt

echo * | xargsも機能しますが、空白などのファイル名の処理は例外です。)

11
ilkkachu
find . -maxdepth 1 -type f ! -name ".*" -exec cat {} + | sort -u -o /path/to/sorted.txt

これにより、現在のディレクトリ内の非表示でない通常のファイルがすべて連結され、それらの結合された内容が(重複する行を削除しながら)ファイル/path/to/sorted.txtにソートされます。

9
Kusalananda

効率は相対的な用語であるため、最小化する要素を実際に指定する必要があります。 CPU、メモリ、ディスク、時間など。議論のために、メモリ使用量を最小限に抑えたいと考えており、それを実現するためにCPUサイクルをより多く費やす用意があると仮定します。 StéphaneChazelasによって提供されるようなソリューションはうまく機能します

sort -u --files0-from <(printf '%s\0' *) > ../output

しかし、彼らは個々のテキストファイルが最初から高度な一意性を持っていると想定しています。そうでない場合、つまり後の場合

sort -u < sample.txt > sample.srt

sample.srtは、sample.txtよりも10%以上小さいため、マージする前にファイル内の重複を削除することにより、メモリを大幅に節約できます。また、コマンドをチェーンしないことで、さらに多くのメモリを節約できます。つまり、異なるプロセスの結果を同時にメモリに保存する必要はありません。

find /somedir -maxdepth 1 type f -exec sort -u -o {} {} \;
sort -u --files0-from <(printf '%s\0' *) > ../output
1
Paul Smith

@ilkkachuと同様ですが、cat(1)は不要です。

printf "%s\0" * | xargs -0 sort -u

また、データが非常に長い場合は、sort(1)オプション-parallel=[〜#〜] n [〜# 〜]

[〜#〜] n [〜#〜]がコンピュータのCPUの数である場合

0
Udi