Ответ 1
Если файлы отсортированы (они есть в вашем примере):
comm -23 file1 file2
-23
подавляет строки, которые есть в обоих файлах или только в файле 2. Если файлы не отсортированы, сначала -23
их через sort
...
У меня есть большой файл A (состоящий из сообщений электронной почты), по одной строке для каждой почты. У меня также есть еще один файл B, содержащий другой набор писем.
Какую команду я бы использовал, чтобы удалить все адреса, которые отображаются в файле B из файла A.
Итак, если файл A содержит:
A
B
C
и файл B содержит:
B
D
E
Затем файл A следует оставить следующим образом:
A
C
Теперь я знаю, что это вопрос, который мог быть задан чаще, но я нашел одну команду онлайн, которая дала мне ошибку с плохой разделитель.
Любая помощь будет очень признательна! Кто-то наверняка придумает умный однострочный, но я не эксперт по оболочке.
Если файлы отсортированы (они есть в вашем примере):
comm -23 file1 file2
-23
подавляет строки, которые есть в обоих файлах или только в файле 2. Если файлы не отсортированы, сначала -23
их через sort
...
grep -Fvxf <lines-to-remove> <all-lines>
Пример:
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 на помощь!
Это решение не требует сортировки входов. Сначала вы должны предоставить файлB.
awk 'NR==FNR{a[$0];next} !($0 in a)' fileB fileA
возвращает
A
C
Как это работает?
NR==FNR{a[$0];next}
idiom предназначен для хранения первого файла в ассоциативном массиве в качестве ключей для последующего теста "содержит".
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
для строк, которые находятся в файле b, но не в
--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[$0]=$0 } }
!($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
diff fileA fileB | grep "^>" | cut -c3- > fileA
Это будет работать для файлов, которые также не отсортированы.