2つ以上のファイルから共通の行を出力する可能性のあるUNIXコマンドを見つけたことがあると思いますが、その名前を知っている人はいますか?それよりもはるかに簡単diff
でした。
pr-123-xy-45
が含まれ、file2にが含まれる場合に、2つのファイル間で共通にする方法がありますec11_orop_pr-123-xy-45.gz
。file3が必要ですec11_orop_pr-123-xy-45.gz
2つ以上のファイルから共通の行を出力する可能性のあるUNIXコマンドを見つけたことがあると思いますが、その名前を知っている人はいますか?それよりもはるかに簡単diff
でした。
pr-123-xy-45
が含まれ、file2にが含まれる場合に、2つのファイル間で共通にする方法がありますec11_orop_pr-123-xy-45.gz
。file3が必要ですec11_orop_pr-123-xy-45.gz
回答:
あなたが探しているコマンドはcomm
です。例えば:-
comm -12 1.sorted.txt 2.sorted.txt
ここに:
-1:列1を抑制(1.sorted.txtに固有の行)
-2:列2を抑制します(2.sorted.txtに固有の行)
grep
ないかもしれない奇妙なことをします。具体的には、すべてが1.txt
正規表現として解釈され、プレーンな文字列として解釈されません。また、の空白行はの1.txt
すべての行と一致し2.txt
ます。したがってgrep
、非常に特定の状況でのみ機能します。あなたは少なくとも使用したいと思うでしょうfgrep
(またはgrep -f
)が、空白行のことはおそらくこのプロセスに大混乱をもたらすでしょう。
commコマンドをソートされていないファイルに簡単に適用するには、Bashのプロセス置換を使用します。
$ bash --version
GNU bash, version 3.2.51(1)-release
Copyright (C) 2007 Free Software Foundation, Inc.
$ cat > abc
123
567
132
$ cat > def
132
777
321
したがって、ファイルabcとdefには、「132」を含む1行が共通しています。ソートされていないファイルでcommを使用する:
$ comm abc def
123
132
567
132
777
321
$ comm -12 abc def # No output! The common line is not found
$
最後の行は出力を生成しませんでした、共通の行は発見されませんでした。
次に、ソートされたファイルに対してcommを使用し、プロセス置換でファイルをソートします。
$ comm <( sort abc ) <( sort def )
123
132
321
567
777
$ comm -12 <( sort abc ) <( sort def )
132
今、私たちは132行を手に入れました!
sort abc > abc.sorted
、sort dev > def.sorted
その後、comm -12 abc.sorted def.sorted
?
Perlのワンライナーを補足するために、以下はawk
同等のものです。
awk 'NR==FNR{arr[$0];next} $0 in arr' file1 file2
これfile1
により、からすべての行が配列arr[]
に読み込まれ、各行file2
が配列内に既に存在するかどうか(つまり、file1
)がチェックされます。見つかった行は、に表示される順序で印刷されfile2
ます。比較でin arr
は、file2
インデックスから配列までの行全体が使用されるため、行全体の完全一致のみが報告されることに注意してください。
perl
ので)。おかげで百万、さん
もしかしてcomm
?
ソートされたファイルFILE1とFILE2を行ごとに比較します。
オプションなしで、3列の出力を生成します。1列目にはFILE1に固有の行が含まれ、2列目にはFILE2に固有の行が含まれ、3列目には両方のファイルに共通の行が含まれています。
これらの情報を見つける秘訣は、情報ページです。GNUプログラムの場合、それらはmanページよりもはるかに詳細です。試してみてinfo coreutils
ください。小さな便利なユーティリティがすべて一覧表示されます。
ながら
grep -v -f 1.txt 2.txt > 3.txt
2つのファイルの違い(2.txtにあり、1.txtにないもの)を提供するため、簡単に
grep -f 1.txt 2.txt > 3.txt
すべての一般的な行を収集します。これにより、問題を簡単に解決できます。ファイルを並べ替えている場合でも、comm
それを行う必要があります。よろしく!
grep
あなたが期待しないかもしれないいくつかの奇妙なことをします。具体的には、すべてが1.txt
正規表現として解釈され、プレーンな文字列として解釈されません。また、の空白行はの1.txt
すべての行と一致し2.txt
ます。したがって、これは非常に特定の状況でのみ機能します。
grep
サポートされているPOSIX 表記を使用して、この回答をアップグレードして作業を改善することができますgrep
。追加-F
(またはを使用fgrep
)して、正規表現を抑制します。-x
行全体にのみ一致するように(正確に)追加します。
comm
ソートされたファイルをとるべきなのですか?
comm
は、メモリに3行を保持するだけでよいので、並べ替えられている限り、任意の大きなファイルで機能します(comm
行が本当に長い場合、GNU はプレフィックスだけを保持することさえ知っていると思います)。grep
解決策は、メモリ内のすべての検索式を維持する必要があります。
2つのファイルがまだソートされていない場合は、以下を使用できます。
comm -12 <(sort a.txt) <(sort b.txt)
そしてそれは機能し、comm: file 2 is not in sorted order
実行時のエラーメッセージを回避しますcomm -12 a.txt b.txt
。
<(command)
はPOSIXシェルに移植できませんが、Bashやその他の一部では機能します。
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' file1 file2
comm
、それはそれぞれの行を検索すると、コマンドfile1
でfile2
どこcomm
行場合にのみ比較されますn
でfile1
、ISは、ラインに等しいですn
でfile2
。
comm
file1の行Nをfile2の行Nと比較するだけではありません。どちらかのファイルに挿入された一連の行を完全にうまく管理できます(もちろん、他のファイルから一連の行を削除することと同じです)。入力がソートされている必要があります。
comm
順序を維持したい場合は、回答よりも優れています。awk
重複を望まない場合は、答えるよりはましです。
Linuxの限定バージョン(私が取り組んでいたQNAP(nas)など):
grep -f file1 file2
@ChristopherSchultzで述べられているようにいくつかの問題を引き起こす可能性があり、使用grep -F -f file1 file2
が非常に遅かった(5分以上-終了しなかった-20MBを超えるファイルで以下の方法で2〜3秒以上)だからここに私がやったことがあります:
sort file1 > file1.sorted
sort file2 > file2.sorted
diff file1.sorted file2.sorted | grep "<" | sed 's/^< *//' > files.diff
diff file1.sorted files.diff | grep "<" | sed 's/^< *//' > files.same.sorted
files.same.sorted
元の順序と同じ順序である必要がある場合は、file1と同じ順序でこの行を追加します。
awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file1 > files.same
または、file2と同じ順序の場合:
awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file2 > files.same
参考までに、複数のファイルに対してこれを行う方法をまだ誰かが探している場合は、多くのファイルで一致する行を見つけることへのリンクされた回答を参照してください。
これらの2つの答え(ans1とans2)を組み合わせると、ファイルを並べ替えなくても必要な結果を得ることができると思います。
#!/bin/bash
ans="matching_lines"
for file1 in *
do
for file2 in *
do
if [ "$file1" != "$ans" ] && [ "$file2" != "$ans" ] && [ "$file1" != "$file2" ] ; then
echo "Comparing: $file1 $file2 ..." >> $ans
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' $file1 $file2 >> $ans
fi
done
done
保存して実行権限(chmod +x compareFiles.sh
)を与えて実行するだけです。現在の作業ディレクトリに存在するすべてのファイルを取得し、all-vs-all比較を実行して、「matching_lines」ファイルに結果を残します。
改善すべき点:
rm file3.txt
cat file1.out | while read line1
do
cat file2.out | while read line2
do
if [[ $line1 == $line2 ]]; then
echo $line1 >>file3.out
fi
done
done
これでうまくいくはずです。
rm -f file3.txt
ファイルを削除する場合は、おそらく使用する必要があります。ファイルが存在しない場合、エラーは報告されません。OTOH、スクリプトが標準出力にエコーするだけの場合、スクリプトのユーザーが出力先を選択できるようにする必要はありません。最終的には、固定ファイル名(および)の代わりに$1
and $2
(コマンドライン引数)を使用したいと思うでしょう。それはアルゴリズムを残します:それは遅くなるでしょう。の各行に1回読み込まれます。ファイルが大きい場合(たとえば、数キロバイト)は遅くなります。file1.out
file2.out
file2.out
file1.out
grep -F
1つのファイルをメモリに読み込んでから、もう1つのパスを1回実行するようなツールは、両方の入力ファイルで繰り返しループすることを回避します。
comm
ソートされた入力ファイルが必要なため、この質問への回答は必ずしも誰もが望むものではありません。行ごとの共通のみが必要な場合、それは素晴らしいことです。しかし、もしあなたが私が "anti-diff"と呼ぶものを望むcomm
なら、その仕事はしません。