Как удалить строки, которые появляются в файле B из другого файла A?


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

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

Так, если файл содержал:

A
B
C

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

B    
D
E

затем файл A должен быть оставлен с:

A
C

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

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

8 107

8 ответов:

comm -23 file1 file2

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

посмотреть man page здесь

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: использовать строковые литералы, а не по умолчанию Бре
  • -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[];next} !( in a)' fileB fileA

возвращает

A
C

как это работает?

NR==FNR{a[];next} идиома предназначена для хранения первого файла в ассоциативном массиве в качестве ключей для последующего теста "содержит".

NR==FNR проверяет, сканируем ли мы первый файл, где глобальный счетчик строк (NR) равен счетчик текущей строки файла (FNR).

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

!( in a) теперь мы находимся в следующем файле(ах), in содержит тест, здесь он проверяет, находится ли текущая строка в наборе, который мы заполнили на первом шаге из первого файла,! перечеркивает состоянии. Здесь отсутствует действие, которое по умолчанию является {print} и обычно не написано явно.

обратите внимание, что теперь это можно использовать для удаления слов из черного списка.

$ awk '...' badwords allwords > goodwords

С небольшим изменением он может очистить несколько списков и создать очищенные версии.

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

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

join -v 1 fileA fileB

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

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

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

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

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

это уточнение хорошего ответа @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[]= } }
  !($N in dictionary) {print}'

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

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

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

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