根据另一个文件中的模式从文件中删除行,该模式可能部分匹配第一个文件中的特定列

根据另一个文件中的模式从文件中删除行,该模式可能部分匹配第一个文件中的特定列

我搜索过类似的答案,但没有解决部分匹配问题。模式文件是 file2,要删除的行位于 file1.csv 中,这是一个相当大的文件,其列数比此处显示的列多得多。

我在 file1.csv 中有以下字段:

更新:

Linking page,Last crawled
https://start.me/discover/be/entertainment/betting?locale=ro,"Nov 17, 2018"
https://imgcop.com/img/Bwin-Mobile-App-77898390/,"Nov 17, 2018"
https://start.me/site/unibet.be?locale=fr,"Nov 17, 2018"
https://poker.partypoker402.com/en/blog/matt-savage-talks-wpt500.html,"Nov 17, 2018"

文件2包含:

https://roulette2.tk
paradisebingo.t
paradisebingo.tm
free-bwin.ro
sb288.co

OUTPUT
Linking page,Last crawled
Linking page,Last crawled
Linking page,Last crawled
Linking page,Last crawled
Linking page,Last crawled
https://start.me/discover/be/entertainment/betting?locale=ro,"Nov 17, 2018"
https://start.me/discover/be/entertainment/betting?locale=ro,"Nov 17, 2018"
https://start.me/discover/be/entertainment/betting?locale=ro,"Nov 17, 2018"
https://start.me/discover/be/entertainment/betting?locale=ro,"Nov 17, 2018"
https://start.me/discover/be/entertainment/betting?locale=ro,"Nov 17, 2018"
etc....

输出正在重复。我不确定出了什么问题。

awk 'FNR == NR{ neg[$1]; next } { for ( i in neg ) if ( $1 !~ i) print }' file2.txt FPAT='([^,]*)|("[^"]+")' file1.csv > out.csv

但无法让它发挥作用。由于某些奇怪的原因 grep 失败:

grep -vwF -f file2 file1.csv > output.csv

答案1

您所做的看起来是一个不错的尝试,但是正则表达式匹配的子句并没有按照您想要的方式工作。在$2 !~ neg[$1]file1,您尝试查找 的值,neg['156398439']因为$1将从第二个文件中检索 ,并且不是首先。所以你的条件永远不会匹配。

您可以执行如下操作,file1通过循环在操作部分内进行正则表达式比较

awk 'FNR == NR { neg[$1]; next }{ for ( i in neg ) if ( $2 !~ i) print  }' file2 FS="," file1

另外,我认为FS不能采用如此复杂的正则表达式来对 CSV 文件进行去限制,请记住FS定义要拆分的去限制符,而不是定义如何定义字段。您似乎有一个表达式可以解释该字段应该是什么样子。 GNUawk允许另一个变量FPAT来定义这样的正则表达式。

您可以使用

awk 'FNR == NR { neg[$1]; next }{ for ( i in neg ) if ( $2 !~ i) print  }' file2 FPAT='([^,]*)|("[^"]+")' file1

答案2

伊尼安的回答file2当只有一行长时,效果很好,并且是一个更通用的答案的良好开端。但我相信

awk 'FNR == NR { neg[$1]; next } { ok=1; for (i in neg) if ($2 ~ i) ok=0; if (ok) print }' file2 FS="," file1

一般来说会做你想做的事。就像您的答案一样,它首先读取file2其内容(您想要从中删除的模式file)并将其存储在数组中。就像 Inian 的答案一样,它是这样的file1。对于 中的每一行file1,它循环遍历 中的模式file2。我们假设线路没问题;如果它与任何模式匹配,那么就不行。如果检查完所有图案后仍然没问题,我们就打印它。


但我把它作为和FS=","之间的一个论点, 只是因为 Inian 就是这样做的。什么都没关系file2file1F产量s我们在阅读时使用的分隔符file2,只要它不出现在其中 - 并且file2不包含逗号。因此,我们可以通过以“正常”方式指定字段分隔符来稍微简化上述内容 --F在命令开头添加一个选项:

awk-F,'FNR == NR { 负[$1];下一个} { 确定=1; for (i in neg) if ($2 ~ i) ok=0;如果(确定)打印}' file2 file1

-F","如果您愿意,可以使用;它们是等价的。


该测试FNR == NR是如此流行和普遍,以至于我们不假思索地使用它。  FNR是行号(又名记录号)在当前文件中,NR行号跨所有输入。  所以,举例来说,

$ cat cats
Felix
Garfield
Heathcliff

$ cat dogs
Lassie
Marmaduke
Snoopy

$ awk '{ print FNR, NR, $0 }' cats dogs
1 1 Felix
2 2 Garfield
3 3 Heathcliff
1 4 Lassie
2 5 Marmaduke
3 6 Snoopy

… 因此 和FNR对于NR要处理的第一个文件的每一行都是相等的,而不是在后续文件中。因此我们用来FNR == NR测试是否正在处理第一个文件。

但这实际上是一种不好的做法。如果第一个文件为空怎么办?

$ cat unicorns

$ wc unicorns
      0       0       0 unicorns

$ awk '{ print FNR, NR, $0 }' unicorns dogs
1 1 Lassie
2 2 Marmaduke
3 3 Snoopy

FNR == NR是真的对于第一个文件确实有数据  如果你的file2意志永远不会是空的,你也许可以忽略这个问题。但是,根据问题的定义,如果file2为空,则输出应该全部为file1,因为我们没有删除任何内容。但是,如果你用空运行上面的命令file2,你会得到 输出,因为当它实际读取第二个文件 ( ) 时,awk它认为它正在读取第一个文件 ( )。file2file1

更安全的方法是在文件参数之间进行赋值:

awk -F, '文件!= 2{ 负[$1];下一个} { 确定=1; for (i in neg) if ($2 ~ i) ok=0;如果(确定)打印}'文件2文件=2文件1

这个问题有点模棱两可。 “部分匹配”是什么意思,确切地?伊尼安选择按照问题所暗示的意义来解释它——比如 grep。如果任何值file2 与第二列的值匹配 file1 作为正则表达式, 然后删除该行 file1。但这有两个问题。

  1. 令人惊讶的因素。我获取了问题中的文件并添加了

    154376352,"http://sb288eco.tm","example4"
    

    线路到file1,并运行我的第一个命令。该"example4"行未输出,因为sb288.co(from  file2),被视为正则表达式(其中.表示“匹配任何字符”),matched sb288eco

    如果这就是您想要并期望发生的事情,那么您最好现在就停止阅读本文。

  2. 正则表达式处理的计算成本很高。必须解析和处理正则表达式。这可能比简单的字符串比较花费更多的时间。

我们可以通过测试是否可以解决上述两个问题细绳from存在于 awk 函数的file2 值 from 中:file1index

awk -F, 'FILE != 2 { neg[$1];下一个} { 确定=1;对于(i 为负数)如果(索引($2,i)> 0)好的=0; if (ok) print }' file2 FILE=2 file1

综上所述,一个.file2比赛中只有一个.file1,而不是任何其他字符。我邀请您在您的数据上测试上述内容,看看它是否更快。


PS 我刚刚注意到自从我发布答案以来您更改了文件格式。最初您想要测试来自的值与 file2 来自的值第二的专栏 file1。现在您似乎想要测试来自第一的的专栏 file1。为了适应这一更改,您应该采用$2与进行比较的任何上述答案的部分i,并将其更改为使用$1。或者,如果您确实想测试 的整行 file1,请使用$0.


所以,底线,你可能想使用

awk -F, 'FILE != 2 { neg[$1]; next } { ok=1; for (i in neg) if (index($1,i) > 0) ok=0; if (ok) print }' file2 FILE=2 file1

作为你的命令。为了便于阅读,换行符是

awk -F, 'FILE != 2 { neg[$1]; next }
                   {
                     ok=1
                     for (i in neg)
                             if (index($1,i) > 0) ok=0
                     if (ok) print
                   }' \
        file2 FILE=2 file1

相关内容