从文件中删除行,具体取决于在另一个文件中找到的行


11

文件file1.txt包含以下行:

/api/purchase/<hash>/index.html

例如:

/api/purchase/12ab09f46/index.html

文件file2.csv包含以下行:

<hash>,timestamp,ip_address

例如:

12ab09f46,20150812235200,22.231.113.64 
a77b3ff22,20150812235959,194.66.82.11

我想过滤file2.csv,以删除在file1.txt中也存在哈希值的所有行。就是说:

cat file1.txt | extract <hash> | sed '/<hash>/d' file2.csv

或类似的东西。

它应该很简单,但是我似乎无法使其工作。

任何人都可以为该任务提供有效的管道吗?

Answers:


13

cut -d / -f 4 file1.txt | paste -sd '|' | xargs -I{} grep -v -E {} file2.csv

说明:

cut -d / -f 4 file1.txt 将从第一个文件中选择哈希

paste -sd '|' 将所有散列连接到一个正则表达式ex中。 H1|H2|H3

xargs -I{} grep -v -E {} file2.csv将使用先前模式作为参数调用grep,xargs将替换{}STDIN

如果没有,paste您可以将其替换为tr "\\n" "|" | sed 's/|$//'


3
+1,但没有必要cat,只是cut -d / -f 4 file1.txt。或者,如果您喜欢顺序外观,<file1.txt cut -d / -f 4
Sparhawk

@Sparhawk谢谢!我不知道;-)解决方案已更新:-)
Gabriele Lana

11

可能的awk解决方案:

awk 'NR == FNR { x[$4] = 1; next; } { if (!($1 in x)) print $0; }' FS="/" file1.txt FS="," file2.txt

首先,我们file1.txt使用FS(字段分隔符)“ /”进行读取,并使用$4您想要的哈希值来自字段的键值创建数组x 。接下来,我们将读取第二个文件file2.txt设置FS为be,,并检查field的值$1是否不存在作为数组中的键x,如果不存在,我们将其打印出来。
评论中提议的更多惯用语可能是:

awk 'NR == FNR { x[$4] = 1; next; } !($1 in x)' FS="/" file1.txt FS="," file2.txt

感谢您的努力,但我担心这会飞到头顶。我一直希望基于sed / grep / cat混合的解决方案是可能的。
Marco Faustinelli 2015年

1
我将添加一个解释,这很简单。也许有人会用您想要的工具提出解决方案。
taliezin

为什么不只是!($1 in x)代替{ if (!($1 in x)) print $0; }
iruvar

@ 1_CR这是我的坏习惯,我知道这可能更惯用,但我一直认为向OP解释会更简单。
taliezin

@Muzietto仍然,我认为开始学习其他工具(例如awk基于此的解决方案)并没有什么坏处……从长远来看,为了简单起见,您将学习倾向于使用较少管道实现的解决方案... :)
hjk

5

对于GNU sed

sed -z 's%.*/\([^/]*\)/index.html\n%\1\\|%g;s%^%/%;s%\\|$%/d%' file1.csv |
sed -f - file2.csv

其中第一个 sed以sed-command格式生成哈希列表,/12ab09f46\|a77b3ff22\|..../d并将其传输到下一个 sed -script,该脚本从输入中读取上述命令,因此为-f -option。
grep相同

grep -oP '[^/]*(?=/index.html$)' file1.csv | grep -Fvf - file2.csv

或不使用perl表达式:

grep -o '[^/]*/index.html$' file1.csv | 
grep -o '^[^/]*' | 
grep -Fvf - file2.csv

甚至用cut更好:

cut -d/ -f4 file1.csv | grep -Fvf - file2.csv

在我看来,这就是我要找的东西。你能说明一下吗?我看不到第二个命令如何从file2.csv中删除行。
Marco Faustinelli,2015年

@Muzietto查看更新
Costas

2
#!/bin/bash
cut -d, -f1 file2 | while read key ; do 
   #check for appearance in file1 with successful grep:
   #exit status is 0 if pattern is found, only search for at least 1
   #appearance -> to speed it up
   if [[ $(grep -m 1 "/$key/" file1) ]] ; then
      sed "/^$key,/d" -i file2
      #note that we are gradually overwriting file2 (-i option),
      #so make a backup!
   fi
done

请注意,搜索字符串是,/$key/并且^$key,将结果减小为两个斜线之间(文件1),或者将结果减少为一行的第一个条目,后跟逗号(文件2)。如果键看起来像这样,应该可以确保安全

a,values
a1,values

在文件2中,或类似

/api/../a1/../
/api/../a/../

在文件1中


2

我刚刚尝试了以下一种衬板,它似乎可以完成此工作:

 for i in `cat file1.txt  | awk -F"/" '{print $4}'`; do echo "\n $i" ; sed -ri "/^$i,/d" file2.csv ; done

请先将-ri替换为-re进行测试。-re进行空运行,如果一切正常,则可以使用-ri运行


嗯,我已经将您的代码输出重定向到一个临时文件,它包含约30k行,而file2.csv最初有240行,应该进行过滤。
Marco Faustinelli,2015年

好吧,我认为这是因为在进行替换(回显“ \ n” $ i部分)时,我在第一个文件中打印了每个哈希。无论如何,如果您使用-ri运行它,则无需重定向,因为它可以进行替换
Primo 15'Aug

另外,如果您使用-re运行并重定向,您将重复file2,以获得与第一个文件中一样多的哈希值。基本上,对于第一个文件中的每个哈希值,它将替换第二个文件中的哈希值并打印结果,因此这就是为什么您有这么多行的原因。
–primero

1

除了Gabriele Lana的答案外,请注意,需要指定BSD粘贴命令以破折号才能从标准输入读取内容。

粘贴命令手册

如果为一个或多个输入文件指定了“-”,则使用标准输入。对于“-”的每个实例,一次循环读取一次标准输入。

所以最终需要像下面这样改变

cut -d / -f 4 file1.txt | paste -sd '|' - | xargs -I{} grep -v -E {} file2.csv
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.