ファイルBに表示される行を別のファイルAから削除するにはどうすればよいですか?


160

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

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

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

A
B
C

およびファイルBに含まれるもの:

B    
D
E

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

A
C

これがより頻繁に尋ねられる可能性のある質問であることがわかりましたが、区切り文字が正しくないエラーが発生するコマンドをオンライン1つだけ見つけました。

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



1
ここでの回答がソートされたファイルに対するもので、最も明白なものが欠落している場合がほとんどですが、もちろんこれはあなたの責任ではありませんが、他の方がより一般的に役立ちます。
tripleee 2014年

回答:


202

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

comm -23 file1 file2

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

こちらのmanページをご覧ください


8
comm -23 file1 file2 > file3file2ではなくfile1の内容をfile3に出力します。そして、mv file3 file1最終的にfile1の冗長コンテンツをクリアします。
スペクトル

2
または、を使用しますcomm -23 file1 file2 | sponge file1。クリーンアップは必要ありません。
ソコウィ2018年

マニュアルページのリンクが読み込まれない–代替:linux.die.net/man/1/comm
Felix Rabe

@ソコウィスポンジとは?私のシステムにはありません。(macos 10.13)
Felix Rabe

@FelixRabe、まあ、それは面倒です。リンクに置き換えられます。ありがとう
典型的なポール

84

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:指定されたファイルからパターンを取得します

この方法は一般的なため、事前にソートされたファイルでは他の方法よりも時間がかかります。速度も重要な場合は、以下を参照してください。ファイル内の行を別のファイル内にないものにすばやく見つける方法」を。

インライン操作用のbash自動化を次に示します。

remove-lines() (
  remove_lines="$1"
  all_lines="$2"
  tmp_file="$(mktemp)"
  grep -Fvxf "$remove_lines" "$all_lines" > "$tmp_file"
  mv "$tmp_file" "$all_lines"
)

GitHubアップストリーム

使用法:

remove-lines lines-to-remove remove-from-this-file

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


55

助けにawk!

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

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

戻り値

A
C

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

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

NR==FNR 最初のファイルをスキャンしているかどうかを確認しています。この場合、グローバル行カウンター(NR)は現在のファイル行カウンター(FNR)と同じです。

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

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

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

$ awk '...' badwords allwords > goodwords

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

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

これに関する満点。これをWindowsのGnuWin32のコマンドラインで使用するには、単一ニブルを二重引用符で置き換えます。御馳走を働かせます。どうもありがとう。
twobob 2016

これは機能しますが、出力をAの形式でfileAにリダイレクトするにはどうすればよいですか(新しい行を使用)B
Anand Builders

私はあなたが意味を推測A\nCまず、一時ファイルへの書き込みをし、元のファイルの上書き... > tmp && mv tmp fileA
karakfa

私もこれで満点です。このawkは、104,000エントリのファイルを処理するのに1秒かかります:+1:
MitchellK

これをスクリプトで使用する場合fileBは、空でないこと(0バイト長)を最初に確認してください。空である場合、の期待される内容ではなく空の結果が返されますfileA。(原因:そのFNR==NRfileA
時点で

18

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

join -v 1 fileA fileB

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

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

7

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

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

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

man diff

詳細については


1
これは、ファイルがソートされていない限り機能すると言います。並べ替えるとどうなりますか?それらが部分的にソートされている場合はどうなりますか?
Carlos Macasaet

1
これは、commコマンドの使用を提案した上記のソリューションへの応答です。commファイルを並べ替える必要があるため、ファイルが並べ替えられている場合は、そのソリューションも使用できます。ファイルがソートされているかどうかに関係なく、このソリューションを使用できます
aec

7

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

この定式化により、入力ファイル内の特定のフィールド($ N)のみを比較に使用できるようになります。

# 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つの利点は、比較基準を変更しやすいことです。たとえば、先頭と末尾の空白を削除できます。)


これは、他の1つのライナーよりも、コーナーケースのクロスプラットフォームシナリオでの使用が困難です。しかし、パフォーマンスへの取り組みは
嫌い

2

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

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

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


-1

2つのファイル間の共通の行を削除するには、grep、comm、またはjoinコマンドを使用できます。

grepは小さなファイルに対してのみ機能します。-fとともに-vを使用します。

grep -vf file2 file1 

これにより、file2のどの行とも一致しないfile1の行が表示されます。

commは、レキシカルにソートされたファイルで機能するユーティリティコマンドです。2つのファイルを入力として受け取り、3つのテキスト列を出力として生成します。最初のファイルの行のみ。2番目のファイルのみの行。と両方のファイルの行。-1、-2、または-3オプションを適宜使用することにより、列の印刷を抑制することができます。

comm -1 -3 file2 file1

これにより、file2のどの行とも一致しないfile1の行が表示されます。

最後に、指定したファイルに対して等価結合を実行するユーティリティコマンドである結合があります。-vオプションを使用すると、2つのファイル間の共通の行を削除することもできます。

join -v1 -v2 file1 file2

これらのすべてはすでに他の答えで与えられました。あなたのgrepには-Fが必要です。そうしないと、行が正規表現のように見えると奇妙な結果になります
ポール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.