Как удалить строки, которые появляются в файле B, из другого файла A? - PullRequest
132 голосов
/ 06 декабря 2010

У меня есть большой файл A (состоящий из электронных писем), по одной строке для каждого письма. У меня также есть еще один файл B , содержащий другой набор писем.

Какую команду использовать для удаления всех адресов, которые отображаются в файле B, из файла A.

Итак, если файл A содержал:

A
B
C

и файл B содержал:

B    
D
E

Тогда файл A должен остаться с:

A
C

Теперь я знаю, что это вопрос, который, возможно, задавался чаще, но я нашел только одну команду онлайн , которая выдала мне ошибку с неверным разделителем.

Любая помощь будет высоко ценится! Кто-то наверняка придумает умную однострочку, но я не эксперт по оболочкам.

Ответы [ 8 ]

179 голосов
/ 06 декабря 2010
comm -23 file1 file2

-23 подавляет строки, которые находятся в обоих файлах или только в файле 2. Файлы должны быть отсортированы (они в вашем примере), но если нет, сначала направьте их через sort ...

См. Справочную страницу здесь

63 голосов

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

44 голосов
/ 23 сентября 2015

в помощь!

Это решение не требует сортированных входных данных. Сначала вы должны предоставить файл B.

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 ...
17 голосов
/ 06 декабря 2010

Другой способ сделать то же самое (также требуется отсортированный ввод):

join -v 1 fileA fileB

В Bash, если файлы предварительно не отсортированы:

join -v 1 <(sort fileA) <(sort fileB)
6 голосов
/ 29 декабря 2015

Это уточнение хорошего ответа @ 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}'

(Другое преимущество этого подхода заключается в том, что критерий сравнения легко изменить, например, обрезать начальные и конечные пробелы.)

6 голосов
/ 22 августа 2012

Вы можете сделать это, если ваши файлы не отсортированы

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

для более подробной информации

2 голосов
/ 30 марта 2018

Вы можете использовать - diff fileA fileB | grep "^>" | cut -c3- > fileA

Это будет работать для файлов, которые также не отсортированы.

2 голосов
/ 10 августа 2017

Вы можете использовать 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)
'
...