我有一个大文件A(由电子邮件组成),每封邮件一行。我还有另一个文件B,其中包含另一组邮件。
我将使用哪个命令从文件A中删除文件B中出现的所有地址。
因此,如果文件A包含:
A
B
C
文件B包含:
B
D
E
然后,将文件A保留为:
A
C
现在,我知道这是一个可能会经常被问到的问题,但是我只在线找到了一条命令,该命令给我一个错误的分隔符错误。
任何帮助将非常感激!肯定有人会想出一个聪明的一线客,但我不是shell专家。
我有一个大文件A(由电子邮件组成),每封邮件一行。我还有另一个文件B,其中包含另一组邮件。
我将使用哪个命令从文件A中删除文件B中出现的所有地址。
因此,如果文件A包含:
A
B
C
文件B包含:
B
D
E
然后,将文件A保留为:
A
C
现在,我知道这是一个可能会经常被问到的问题,但是我只在线找到了一条命令,该命令给我一个错误的分隔符错误。
任何帮助将非常感激!肯定有人会想出一个聪明的一线客,但我不是shell专家。
Answers:
comm -23 file1 file2 > file3
会将文件1中而不是文件2中的内容输出到文件3中。然后mv file3 file1
最终将清除file1中的多余内容。
comm -23 file1 file2 | sponge file1
。无需清理。
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"
)
用法:
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
急救!
此解决方案不需要排序的输入。您必须先提供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 ...
A\nC
,先写一个临时文件,然后覆盖原始文件... > tmp && mv tmp fileA
fileB
是否为非空(长0个字节),因为如果是,则将得到空结果,而不是预期的内容fileA
。(原因:FNR==NR
适用于fileA
当时。)
执行相同操作的另一种方式(也需要排序的输入):
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
更多细节
comm
命令的解决方案的回应。comm
需要对文件进行排序,因此,如果对文件进行了排序,您也可以使用该解决方案。无论文件是否经过排序,都可以使用此解决方案
对于大型文件,@ 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}'
(此方法的另一个优点是很容易修改比较标准,例如修剪前和后空白。)
要删除两个文件之间的公共行,可以使用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