Как удалить строки, которые появляются в файле B, из другого файла A?
У меня есть большой файл A (состоящий из электронных писем), одна строка для каждого письма. У меня также есть другой файл B, который содержит другой набор писем.
Какую команду я бы использовал, чтобы удалить все адреса, которые появляются в файле B, из файла A.
Итак, если файл A содержал:
A
B
C
и файл B содержал:
B
D
E
Тогда файл A должен остаться с:
A
C
Теперь я знаю, что это вопрос, который, возможно, задавался чаще, но я нашел только одну команду в сети, которая выдала мне ошибку с неправильным разделителем.
Любая помощь приветствуется! Кто-то наверняка придумает умную однострочку, но я не эксперт по оболочкам.
12 ответов
comm -23 file1 file2
-23 подавляет строки, которые есть в обоих файлах или только в файле 2. Файлы должны быть отсортированы (они есть в вашем примере), но если нет, направьте их через sort
первый...
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 ...
Другой способ сделать то же самое (также требует отсортированного ввода):
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
для строк, которые находятся в файле б, но не в--old-..
для строк, которые находятся в файле а, но не в б--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[$0]=$0 } }
!($N in dictionary) {print}'
(Другое преимущество этого подхода заключается в том, что он легко модифицирует критерий сравнения, например, обрезая начальные и конечные пробелы.)
Чтобы добавить к ответу Python пользователю выше, вот более быстрое решение:
python -c '
lines_to_remove = None
with open("partial file") as f:
lines_to_remove = {line.rstrip() for line in f.readlines()}
remaining_lines = None
with open("full file") as f:
remaining_lines = {line.rstrip() for line in f.readlines()} - lines_to_remove
with open("output file", "w") as f:
for line in remaining_lines:
f.write(line + "\n")
'
Повышение мощности вычитания множеств.
Вы можете использовать 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
Это будет работать для файлов, которые также не отсортированы.
Вот один лайнер, который передает вывод веб-сайта и удаляет элементы навигации с помощью grep и lynx! вы можете заменить lynx на cat FileA и нежелательные элементы.txt на FileB.
lynx -dump -accept_all_cookies -nolist -width 1000 https://stackoverflow.com/ | grep -Fxvf unwanted-elements.txt
Чтобы получить файл после удаления строк, которые появляются в другом файле
comm -23 <(sort bigFile.txt) <(sort smallfile.txt) > diff.txt
Чтобы удалить общие строки между двумя файлами, вы можете использовать команду grep, comm или join.
grep работает только с небольшими файлами. Используйте -v вместе с -f.
grep -vf file2 file1
Это отображает строки из файла1, которые не соответствуют ни одной строке в файле2.
comm - служебная команда, работающая с лексически отсортированными файлами. Он принимает два файла в качестве входных и создает три текстовых столбца в качестве выходных: строки только в первом файле; строки только во втором файле; и строки в обоих файлах. Вы можете подавить печать любого столбца, используя соответственно параметр -1, -2 или -3.
comm -1 -3 file2 file1
Это отображает строки из файла1, которые не соответствуют ни одной строке в файле2.
Наконец, есть join - служебная команда, которая выполняет соединение с равенством для указанных файлов. Его опция -v также позволяет удалить общие строки между двумя файлами.
join -v1 -v2 file1 file2