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

Linux 如何从另一个文件A中删除文件B上显示的行?,linux,shell,sed,diff,grep,Linux,Shell,Sed,Diff,Grep,我有一个很大的文件a(由电子邮件组成),每封邮件一行。我还有另一个文件B,其中包含另一组邮件 我将使用哪个命令从文件A中删除文件B中显示的所有地址 因此,如果文件A包含: A B C B D E 文件B包括: A B C B D E 然后,文件A应保留以下内容: A C 现在我知道这是一个可能会被问得更频繁的问题,但我只发现它给了我一个错误的分隔符 任何帮助都将不胜感激!肯定有人会想出一个聪明的单行程序,但我不是shell专家。如果文件已排序(它们在您的示例中): -2

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

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

因此,如果文件A包含:

A
B
C
B    
D
E
文件B包括:

A
B
C
B    
D
E
然后,文件A应保留以下内容:

A
C
现在我知道这是一个可能会被问得更频繁的问题,但我只发现它给了我一个错误的分隔符

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

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

-23
取消显示两个文件中的行,或仅显示文件2中的行。如果文件未排序,请首先通过管道将其排序。
sort


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

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

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

join-v1除非对文件进行了排序,否则您可以执行此操作

diff file-a file-b --new-line-format="" --old-line-format="%L" --unchanged-line-format="" > file-a
--新行格式
适用于文件b中但不在a中的行
--old-..
用于文件a中但不在b中的行
--unchanged-..
用于两种格式的行。
%L
使其能够精确打印行

man diff
有关更多详细信息

grep-Fvxf

例如:

cat <<EOF > A
b
1
a
0
01
b
1
EOF

cat <<EOF > B
0
1
EOF

grep -Fvxf B A
说明:

  • -F
    :使用文字字符串而不是默认的BRE
  • 代码> -x/代码>:只考虑与整个行匹配的匹配项
  • -v
    :打印不匹配
  • -f文件
    :从给定文件中获取模式
与其他方法相比,此方法在预排序文件上的速度较慢,因为它更通用。如果速度也很重要,请参见:

下面是一个用于在线操作的快速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"
)

用法:

remove-lines lines-to-remove remove-from-this-file
另请参见:

awk to the rescue

此解决方案不需要排序输入。您必须首先提供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]
将当前行作为键添加到关联数组中,请注意,其行为类似于集合,其中不会有任何重复值(键)

!(a中的$0)
我们现在在下一个文件中,
中的
是一个包含测试,这里它检查当前行是否在第一个文件
的第一步中填充的集合中否定条件。这里缺少的是操作,默认情况下它是
{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}'
#在输入中打印行,除非列$N中的值
#出现在查找文件中,$lookup;
#如果$N为0,则整行用于比较。
awk-v N=$N-v lookup=“$lookup”'
开始{while(getline
(这种方法的另一个优点是很容易修改比较标准,例如修剪前导和尾随空格。)

您可以使用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、comm或join命令。

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

这将显示文件1中与文件2中的任何行都不匹配的行

comm是一个实用命令,用于按词汇排序的文件。信息技术 将两个文件作为输入,并生成三个文本列作为输出: 仅第一个文件中的行;仅第二个文件中的行;和线路 在这两个文件中。您可以使用-1,-2抑制任何列的打印 或相应的-3选项

这将显示文件1中与文件2中的任何行都不匹配的行

最后,还有join,一个执行等式的实用程序命令 在指定的文件上加入。它的-v选项还允许删除 两个文件之间的公用行


为了向上面的用户添加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
comm-23 file1 file2>file3
将把file1中而不是file2中的内容输出到file3。然后,
mv file3 file1
将最终清除file1中的冗余内容。或者,使用
comm-23 file1 file2 |海绵file1
。无需清理。没有为我加载手册页链接–可选:@Socowi什么是海绵?我的系统上没有这个。(macos 10.13)@FelixRabe,好吧,那太累人了。替换为您的链接。如果这里的答案是针对已排序的文件的,并且最明显的一个丢失了,那当然不是你的错,但这使另一个更普遍有用。你说这将起作用,除非文件已排序。如果对它们进行排序,会出现什么问题?如果它们是部分排序的呢?这是对上述sug解决方案的响应
grep -vf file2 file1 
comm -1 -3 file2 file1
join -v1 -v2 file1 file2
    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")
    '