【问题标题】:Remove from one file what is in another file从一个文件中删除另一个文件中的内容
【发布时间】:2013-01-12 13:03:42
【问题描述】:

我有两个文本文件,file1.txt 和 file2.txt。

file1.txt 包含一个数字列表。 file2.txt 还包含一个数字列表,但数量更多(很大一部分是来自 file1.txt 的数字)。这就是我想要做的:

我想从 file2.txt 中删除 file1.txt 中的所有数字,并将输出保存到 file3.txt。所以在 file3.txt 中,它将不包含 file1.txt 中的数字。我怎样才能做到这一点?

【问题讨论】:

标签: linux shell unix command-line grep


【解决方案1】:

您只想打印 file2.txt 的唯一元素。这就是 comm 实用程序的设计目的:

comm -13 <(sort file1.txt) <(sort file2.txt)

测试

$ cat file1.txt
5
4
6
2
10

$ cat file2.txt
3
7
8
2
4
1
9
10
5
6

$ comm -13 <(sort file1.txt) <(sort file2.txt)
1
3
7
8
9

【讨论】:

  • 时间:10k 的 file1.txt 和 1M 的 file2.txt 需要 2.170 秒。虽然 comm 是为这种练习而设计的,但它比 awk 慢吗?
  • 命令是否应该是comm -13 ...,这样如果file1.txt 中有没有出现在file2.txt 中的数字,它们就不会出现在输出中?如果file1.txt1 中的每个数字确实出现在file2.txt 中的某个位置,那么您所拥有的一切都很好。
  • @erik:这是预先排序的输入吗?
  • @JonathanLeffler:你说得对,-13 更正确,我会修正它。
【解决方案2】:

使用 GNU grep,您可以使用“fgrep”模式:

grep -F -v -f file1.txt -w file2.txt > file3.txt

演示:

seq 1 30 > file2.txt
for i in 1 2 3 4 5; do echo $RANDOM; done | sed 's/\(..\).*/\1/' > file1.txt
grep -F -v -f file1.txt -w file2.txt > file3.txt

file2.txt 的内容是数字 1 到 30 的行。file1.txt 的内容是 5 个半随机的 2 位数字。 file3.txt 中的输出是文件 2 中不在文件 1 中的行。请注意,循环生成的随机数不是很好,也没有限制为 1..30(另请参见下面的 cmets)。

GNU grep 特有的功能是-w 标志,它匹配整个单词。有趣的是,POSIX 2008 指定 -x 应该与精确的行匹配,并且 -x 选项对我来说可以正常工作(在 Mac OS X 10.7.5 上,但 /usr/bin/grep 是 GNU grep 2.5.1)。理论上,-x 更便携。由于它也在POSIX 1997 标准中,因此应该可以广泛使用。如果一行中有多个数字,-w 选项会更合适(但grep 会消除整行)。

【讨论】:

  • +1。根据OP的问题 file1.txt 和 file2.txt 应该交换。顺便提一句。您可以使用 shuf 获得更可控的随机化:seq 1 30 | shuf | head -n5.
  • 我不会遇到shuf(尽管我在我的机器上的/usr/gnu/bin 目录中找到了它)。我有自己的random 来生成数字(random -n 5 1 30),但我必须解释一下。是的,与问题相比,看起来我确实拥有file1.txtfile2.txt 的角色;问题中的措辞有些奇怪,我读错了,所以我会修复它。谢谢!
  • 我同意应该改进这个问题。 shuf 于 2006 年 8 月添加到 GNU coreutils,因此它不像使用 $RANDOM 那样可移植。
  • 您也可以使用 $(($RANDOM % 30 +1)) 将数字保持在 1 和 30 的范围内。
  • 时间:10k 的 file1.txt 和 1M 的 file2.txt 需要 1.177 秒。
【解决方案3】:

这是使用awk的一种方式:

awk 'FNR==NR { a[$0]; next } !($0 in a)' file1.txt file2.txt > file3.txt

这会将file1读入一个数组,然后在遍历file2时,它将打印file2中不在数组中的行并将它们写入输出文件。如果您有任何问题,请不要犹豫。干杯。

【讨论】:

  • 时间:10k 的 file1.txt 和 1M 的 file2.txt 需要 1.010 秒。
【解决方案4】:

您能否提供更多关于这些数字的格式的信息?他们每个人都在新线上吗?它们的位数都一样吗?

编辑:收到评论后:

while read line
do
    bool="false"
    while read secLine
    do
        if [ "$line" == "$secLine" ]
        then
            bool="true"
        fi
    done <file1
    if [ "$bool" == "false" ]
    then
         echo $line >> file3.txt
    fi
done <file2

这会起作用,尽管通过蛮力(或者它应该起作用。检查语法错误。我没有看到任何错误,但可能有一些。)这可能需要一段时间,具体取决于你有多少数字。

【讨论】:

  • 它们是一个新行,它们的位数不同
  • 我建议使用脚本为您执行此操作。比如说,一个检查每一行 file2 并将其与 file1 中的每一行进行比较的脚本。如果从来没有匹配,则将该特定行放在 file3 中。
  • 那里,有一个适合你的脚本,我确定(只要我没有犯任何语法错误)请注意,这可能需要一段时间,具体取决于文件大小。
  • 感谢您的回复,我认为文件可能太大,因为我收到“第 6 行:[:参数太多”
  • 啊,实际上不,这是我害怕的那些难以捉摸的语法错误之一 >
【解决方案5】:

您可以使用 unix 的“diff”命令来获取差异并过滤掉不需要的行。您可以使用 --changed-group-format--unchanged-group-format 选项过滤所需的数据。

以下三个选项可用于为每个选项选择相关组:

  • '%

  • '%>' 从 FILE2 获取行

  • ''(空字符串)用于从两个文件中删除行。

例如:

diff --changed-group-format="%>" --unchanged-group-format="" file1.txt file2.txt > file3.txt

【讨论】:

  • 好像有错误。在对使用 comm 插入的非标签进行排序和删除之后,我得到了一个不同的 file3.txt 以及该问题的所有其他答案(请参阅 Thor)。而你的答案是最慢的:10k 的 file1.txt 和 1M 数字的 file2.txt 需要 7.381 秒。
  • @erik 很高兴了解性能。谢谢。你在说什么错误?您是否尝试比较两个小文件并检查这是否给出了预期的结果?
【解决方案6】:
sort file1.txt file2.txt|uniq -u > file3.txt

【讨论】:

  • 谢谢,我试过了,得到“排序:字符串比较失败:非法字节序列”
  • 也许尝试导出 LC_ALL=C?所以:LC_ALL=C sort file1.txt file2.txt|uniq -u > file3.txt
  • 如果 file1.txt 是 file2.txt 的子集,并且 file2.txt 不包含 file1.txt 中没有的重复项,则此操作会产生差异。
  • 例如 "echo -e 'foo\nbaz' > file1.txt; echo -e 'foo\nbar\nbaz' > file2.txt; 排序file1.txt file2.txt|uniq - u > file3.txt" 产生 "bar"。
  • 我错过了-u 选项对uniq 的意义。我们生活,我们学习。
猜你喜欢
  • 2013-04-05
  • 2011-06-14
  • 2014-07-19
  • 2014-02-15
  • 1970-01-01
  • 2021-09-18
  • 2012-02-22
  • 2013-02-10
相关资源
最近更新 更多