web-dev-qa-db-ja.com

ファイルBに表示される行を別のファイルAから削除する方法は?

大きなファイルA(電子メールで構成)、各メールに1行あります。また、別のメールセットを含む別のfile Bがあります。

ファイルAからファイルBにあるすべてのアドレスを削除するには、どのコマンドを使用しますか。

したがって、ファイルAに含まれている場合:

A
B
C

含まれるファイルB:

B    
D
E

次に、ファイルAを次のように残します。

A
C

これはもっと頻繁に尋ねられるかもしれない質問ですが、私は オンラインで1つのコマンド が見つかりました。

どんな助けでも大歓迎です!誰かが確かに賢いワンライナーを思い付くでしょうが、私はシェルの専門家ではありません。

135
slhck

ファイルがソートされている場合(例にあります):

comm -23 file1 file2

-23は、両方のファイル、またはファイル2のみにある行を抑制します。ファイルがソートされていない場合は、最初にsortにパイプしてください...

manページはこちら を参照してください

181

grep -Fvxf <lines-to-remove> <all-lines>

  • ソートされていないファイルで動作します
  • 順序を維持する
  • POSIX

例:

cat <<EOF > A
b
1
a
0
01
b
1
EOF

cat <<EOF > B
0
1
EOF

grep -Fvxf B A

出力:

b
a
01
b

説明:

  • -F:デフォルトのBREの代わりにリテラル文字列を使用します
  • -x:行全体に一致する一致のみを考慮する
  • -v:一致しないものを出力
  • -f file:指定されたファイルからパターンを取得します

このメソッドは、より一般的であるため、事前にソートされたファイルでは他のメソッドよりも遅くなります。速度も同様に重要な場合は、以下を参照してください。 あるファイルで別のファイルにない行を見つけるための高速な方法?

参照: https://unix.stackexchange.com/questions/28158/is-there-a-tool-to-get-the-lines-in-one-file-that-are-not-in -another

救助にawk!

このソリューションは、ソートされた入力を必要としません。まずfileBを提供する必要があります。

awk 'NR==FNR{a[$0];next} !($0 in a)' fileB fileA

返却値

A
C

それはどのように機能しますか?

NR==FNR{a[$0];next}イディオムは、後の「含む」テストのキーとして連想配列に最初のファイルを保存するためのものです。

NR==FNRは、グローバルラインカウンター(NR)が現在のファイルラインカウンター(FNR)に等しい最初のファイルをスキャンしているかどうかを確認しています。

a[$0]はキーとして連想配列に現在の行を追加します。これはセットのように動作し、重複する値(キー)はないことに注意してください

!($0 in a)次のファイルになりました。inは包含テストです。ここでは、現在の行が最初のファイルの最初のステップで設定したセットにあるかどうかを確認します!は条件を否定します。ここで不足しているのはアクションで、デフォルトでは{print}であり、通常は明示的に記述されていません。

これを使用して、ブラックリストに登録された単語を削除できることに注意してください。

$ awk '...' badwords allwords > goodwords

わずかな変更を加えるだけで、複数のリストをクリーンアップし、クリーンなバージョンを作成できます。

$ awk 'NR==FNR{a[$0];next} !($0 in a){print > FILENAME".clean"}' bad file1 file2 file3 ...
46
karakfa

同じことを行う別の方法(ソートされた入力も必要):

join -v 1 fileA fileB

Bashで、ファイルが事前にソートされていない場合:

join -v 1 <(sort fileA) <(sort fileB)
17

ファイルがソートされていない限り、これを行うことができます

diff file-a file-b --new-line-format="" --old-line-format="%L" --unchanged-line-format="" > file-a

--new-line-formatは、ファイルbにあるが、aにはない--old-..は、ファイルaにあるが、bにはない行です--unchanged-..は、両方にある行です。 %Lは、行が正確に印刷されるようにします。

man diff

詳細については

6
aec

@karakfaのニースの回答のこの改良は、非常に大きなファイルの場合、著しく高速になる可能性があります。その答えと同様に、どちらのファイルもソートする必要はありませんが、awkの連想配列によって速度が保証されます。ルックアップファイルのみがメモリに保持されます。

この定式化により、入力ファイル内の特定のフィールド($ N)が1つだけ比較に使用される可能性も考慮されます。

# Print lines in the input unless the value in column $N
# appears in a lookup file, $LOOKUP;
# if $N is 0, then the entire line is used for comparison.

awk -v N=$N -v lookup="$LOOKUP" '
  BEGIN { while ( getline < lookup ) { dictionary[$0]=$0 } }
  !($N in dictionary) {print}'

(このアプローチのもう1つの利点は、比較基準を簡単に変更できることです(たとえば、先頭と末尾の空白を削除するなど)。)

6
peak

Pythonを使用できます:

python -c '
lines_to_remove = set()
with open("file B", "r") as f:
    for line in f.readlines():
        lines_to_remove.add(line.strip())

with open("file A", "r") as f:
    for line in [line.strip() for line in f.readlines()]:
        if line not in lines_to_remove:
            print(line)
'
2
HelloGoodbye

使用できます-diff fileA fileB | grep "^>" | cut -c3- > fileA

これは、ソートされていないファイルでも機能します。

2
Darpan