非Linux Unices(でももっと文書)

非Linux Unices(でももっと文書)

約30,000個のファイルがあります。各ファイルには約100,000行が含まれています。行にはスペースは含まれません。単一ファイル内の行はソートされ、重複しません。

私の目標:すべてを探したいみんな複数のファイルに重複した行があり、重複したエントリを含むファイル名。

簡単な解決策は次のとおりです。

cat *.words | sort | uniq -c | grep -v -F '1 '

次に、次を実行します。

grep 'duplicated entry' *.words

より効率的なアプローチが見えますか?

ベストアンサー1

すべての入力ファイルがすでにソートされているため、実際のソートステップをスキップして次のものをsort -m使用できます。マージファイルを一緒に入れてください。

一部のUnixシステムでは(私が知る限りただLinux)、これで十分だと思います

sort -m *.words | uniq -d >dupes.txt

fileに重複した行を書き込みますdupes.txt

この行がどのファイルから来たかを見つけるには:

grep -Fx -f dupes.txt *.words

grepこれは、()内の行が次のように処理されるように指示します。dupes.txt-f dupes.txt固定文字列パターン-F)。grepまた、行全体が最初から最後まで完全に一致する必要があります(-x)。ファイル名と行を端末に印刷します。

非Linux Unices(でももっと文書)

一部のUnixシステムでは、30000個のファイル名が単一のユーティリティに渡されるには長すぎる文字列に拡張されています(これは私のOpenBSDシステムが実行するsort -m *.words印刷出力が失敗することを意味します)。Argument list too longファイルの数がはるかに多い場合は、Linuxでもこれについて不平を言うでしょう。

詐欺師を探しています

これは一般的なケース(これは次にも適用されます)を意味します。たくさん(30000を超えるファイル)ソートは「チャンク」する必要があります。

rm -f tmpfile
find . -type f -name '*.words' -print0 |
xargs -0 sh -c '
    if [ -f tmpfile ]; then
        sort -o tmpfile -m tmpfile "$@"
    else
        sort -o tmpfile -m "$@"
    fi' sh 

または、tmpfile次のように生成しませんxargs

rm -f tmpfile
find . -type f -name '*.words' -exec sh -c '
    if [ -f tmpfile ]; then
        sort -o tmpfile -m tmpfile "$@"
    else
        sort -o tmpfile -m "$@"
    fi' sh {} +

現在のディレクトリ(またはそれ以下)で名前が一致するすべてのファイルを見つけます*.words。これらの名前の適切なサイズのチャンク(サイズはxargs/によって決まります)に対してfindソートされたファイルにマージされますtmpfile。ファイルがすでに存在する場合tmpfile(最初のブロックを除くすべてのブロックについて)、そのファイルは現在のブロックの他のファイルともマージされます。ファイル名の長さとコマンドラインで許可されている最大長によっては、内部スクリプトを10回以上別々に実行する必要があるかもしれません(find/はxargs自動的にこれを行います)。

「内部」shスクリプト、

if [ -f tmpfile ]; then
    sort -o tmpfile -m tmpfile "$@"
else
    sort -o tmpfile -m "$@"
fi

出力用sort -o tmpfile(これも入力でも上書きtmpfileされません)とマージします。両方とも、スクリプトから渡されるか、スクリプトに渡された個別に参照されたファイル名のリストに展開されます。tmpfilesort-m"$@"findxargs

その後、実行を続けてuniq -dすべてのtmpfile重複行を取得します。

uniq -d tmpfile >dupes.txt

「DRY」の原則(「繰り返しないでください」)が好きな場合は、内部スクリプトを次のように書くことができます。

if [ -f tmpfile ]; then
    t=tmpfile
else
    t=/dev/null
fi

sort -o tmpfile -m "$t" "$@"

または

t=tmpfile
[ ! -f "$t" ] && t=/dev/null
sort -o tmpfile -m "$t" "$@"

どこから来たの?

上記と同じ理由で をgrep -Fx -f dupes.txt *.words使用してこれらの重複項目のソースを見つけることができないため、次のように再度find使用します。

find . -type f -name '*.words' \
    -exec grep -Fx -f dupes.txt {} +

「複雑な」処理は必要ないので、grepから直接呼び出すことができます-exec。この-execオプションはユーティリティコマンドを取り、見つかった名前をここに入れます{}。最後に、+現在のシェルがサポートするだけの引数がfindユーティリティの各呼び出しに配置されます。{}

~になる完全そうですね。次のいずれかを使用したい場合があります。

find . -type f -name '*.words' \
    -exec grep -H -Fx -f dupes.txt {} +

または

find . -type f -name '*.words' \
    -exec grep -Fx -f dupes.txt /dev/null {} +

ファイル名は常にgrep

最初のバリアントは、grep -H常に一致するファイル名を出力するために使用されます。最後のバリエーションは次の事実を使用しますgrep複数のファイルコマンドラインで提供されます。

grepfromに送信された最後のファイル名ブロックには、find実際にはファイル名のみを含めることができます。この場合、結果にgrep記載されていないため、これは重要です。


報酬データ:

プロファイリングfind++xargsコマンドsh:

find . -type f -name '*.words' -print0 |
xargs -0 sh -c '
    if [ -f tmpfile ]; then
        sort -o tmpfile -m tmpfile "$@"
    else
        sort -o tmpfile -m "$@"
    fi' sh 

find . -type f -name '*.words'単に現在のディレクトリ(またはその下)からパス名のリストを生成します。ここで、各パス名は一般ファイル-type f)そして最後に一致するファイル名の部分があります*.words。をする現在の検索するディレクトリは-maxdepth 1後ろに追加.するか、前に追加できます-type f

-print0\0見つかったすべてのパス名が(nul)文字を区切り文字として出力していることを確認してください。これはUnixパスで無効な文字であり、改行文字(またはその他の奇妙な内容)が含まれていてもパス名を処理できます。

find出力をxargs

xargs -0A - 区切りパス名のリストを読み、\0そのユーティリティはその中のチャンクを使用して繰り返し実行されます。したがって、引数リストが長すぎるとシェルが文句を言わないように、十分な引数を使用してユーティリティを実行します。もう入力がありませんfind

呼び出されるユーティリティは、そのフラグを使用してコマンドラインに文字列として提供されるスクリプトxargsです。sh-c

後続のパラメータと一緒に呼び出されると、sh -c '...some script...'これらのパラメータをスクリプトで使用できます$@最初のパラメータを除いて、 に配置されます(たとえば、十分に高速な場合に見つけることが$0できる「コマンド名」です)。topこれがsh実際のスクリプトの最後に最初のパラメータとして文字列を挿入する理由です。文字sh仮想論争任意の単一の単語にすることができます(一部の人はまたはを_好むようですsh-find)。

おすすめ記事