如何从另一个文件A中删除文件B上显示的行?


160

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

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

因此,如果文件A包含:

A
B
C

文件B包含:

B    
D
E

然后,将文件A保留为:

A
C

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

任何帮助将非常感激!肯定有人会想出一个聪明的一线客,但我不是shell专家。



1
如果这里的答案大多数是针对排序的文件的,而最显而易见的答案是缺失的,那当然不是您的错,但这使另一个更有用。
2014年

Answers:


202

如果文件已排序(在您的示例中):

comm -23 file1 file2

-23禁止显示两个文件中或仅文件2中的行。如果未对文件进行排序,sort请先通过管道...

此处查看手册页


8
comm -23 file1 file2 > file3会将文件1中而不是文件2中的内容输出到文件3中。然后mv file3 file1最终将清除file1中的多余内容。
Spectral

2
或者,使用comm -23 file1 file2 | sponge file1。无需清理。
Socowi

手册页链接未为我加载–替代:linux.die.net/man/1/comm
Felix Rabe,

@Socowi什么是海绵?我的系统上没有这个。(macos 10.13)
Felix Rabe

@FelixRabe,好吧,那太累了。替换为您的链接。谢谢
原型保罗

84

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

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

这是用于在线操作的快速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


55

急救!

此解决方案不需要排序的输入。您必须先提供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 ...

满分。要在Windows的GnuWin32中的命令行上使用此命令,请用双引号替换单字节。工作请客。非常感谢。
twobob

这可行,但是我将如何将输出重定向为A形式的FileA(用新行)B
Anand Builders

我想你的意思是A\nC,先写一个临时文件,然后覆盖原始文件... > tmp && mv tmp fileA
karakfa

我也从中得到满分。这个awk需要1秒钟的全部时间来处理包含104,000个条目的文件:+1:
MitchellK

在脚本中使用this时,请确保首先检查它fileB是否为非空(长0个字节),因为如果是,则将得到空结果,而不是预期的内容fileA。(原因:FNR==NR适用于fileA当时。)
彼得·诺威

18

执行相同操作的另一种方式(也需要排序的输入):

join -v 1 fileA fileB

在Bash中,如果文件未预先排序:

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

7

您可以执行此操作,除非文件已排序

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

更多细节


1
您说除非文件被排序,否则这将起作用。如果对它们进行排序会出现什么问题?如果将它们部分分类怎么办?
卡洛斯·马卡萨特

1
那是对上面建议使用comm命令的解决方案的回应。comm需要对文件进行排序,因此,如果对文件进行了排序,您也可以使用该解决方案。无论文件是否经过排序,都可以使用此解决方案
aec

7

对于大型文件,@ 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}'

(此方法的另一个优点是很容易修改比较标准,例如修剪前和后空白。)


在角落情况下的跨平台方案中,使用这种方法比使用另一种方法更难。然而,对表演的努力
却不屑一顾

2

您可以使用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)
'

2

您可以使用 - diff fileA fileB | grep "^>" | cut -c3- > fileA

这将适用于未排序的文件。


-1

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

grep仅适用于小文件。与-f一起使用-v。

grep -vf file2 file1 

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

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

comm -1 -3 file2 file1

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

最后,有一个联接,一个实用程序命令,它对指定的文件执行相等联接。其-v选项还允许删除两个文件之间的公共行。

join -v1 -v2 file1 file2

所有这些都已经在其他答案中给出了。您的grep需要一个-F,否则当行看起来像正则表达式时,您将得到奇怪的结果
The Archetypal Paul
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.