如何从另一个文件A中删除文件B中出现的行?

我有一个大文件一个(由电子邮件组成),每封邮件一行。我还有另一个文件B,它包含另一组邮件。

我将使用哪个命令从文件A中删除文件B中出现的所有地址。

因此,如果文件A包含:

A
B
C

文件B包含:

B
D
E

那么文件A应该剩下:

A
C

现在我知道这是一个可能经常被问到的问题,但我只找到了联机一条命令,它给了我一个错误的分隔符。

任何帮助都将不胜感激!肯定有人会想出一个聪明的俏皮话,但我不是shell专家。

119609 次浏览

如果文件已经排序(在你的例子中):

comm -23 file1 file2

-23抑制两个文件中的行,或仅在文件2中。如果文件没有排序,则先通过sort管道将它们导入…

参见这里是手册页

另一种方法来做同样的事情(也需要排序输入):

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

欲知详情

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:从给定的文件中获取模式

这种方法在预排序文件上比其他方法慢,因为它更通用。如果速度也很重要,请参见:在一个文件中找到不在另一个文件中的行的快速方法?

下面是一个用于内联操作的快速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

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 ...

对于非常大的文件,@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 < / p >

这也适用于没有排序的文件。

要删除两个文件之间的公共行,可以使用grep、comm或join命令。

Grep只适用于小文件。使用-v和-f。

grep -vf file2 file1

这将显示file1中与file2中任何行不匹配的行。

comm是一个实用命令,用于按词法排序的文件。它 以两个文件作为输入,并产生三个文本列作为输出: 只在第一个文件中的行;只在第二个文件中的行;和线 在两个文件中。可以使用-1、-2来抑制任何列的打印 或相应的-3选项

comm -1 -3 file2 file1

这将显示file1中与file2中任何行不匹配的行。

最后是join,这是一个执行相等操作的实用命令 在指定文件上连接。它的-v选项也允许删除

.文件之间的公共行
join -v1 -v2 file1 file2

为了给上面的用户添加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")
'

提高集合减法的幂。

删除出现在另一个文件上的行后获取该文件

comm -23 <(sort bigFile.txt) <(sort smallfile.txt) > diff.txt