删除匹配行和连续一行(for循环)

删除匹配行和连续一行(for循环)

我在这个主题上发现了一些类似的问题和解决方案,但我无法解决该问题for 循环与之前建议的解决方案。

文件B:

88569.abcrat
44689.defhom
3702.ghigop

名为 234 的文件中的文本示例:

9606.jklpan
how is the weather
88569.abcrat
today is a sunny day
44689.defhom
tomorrow will be a rainy day
3702.ghigop
yesterday was a cloudy day
10116.zyxtak
i am happy to see rainbow

名为 234 的文件的所需输出:

9606.jklpan
how is the weather
10116.zyxtak
i am happy to see rainbow

然后,我需要对 fileA 中列出的其他文件重复搜索、匹配和删除的过程。

文件A:

234
123
456

我正在尝试:

for i in $(cat fileA); do for j in $(cat fileB); do awk "/$j/ {while (/$j/ && getline>0) ; next} 1" $i; done; done

for i in $(cat fileA); do for j in $(cat fileB); do sed -e "/**$i/$j**/ { N; d; }" $i; done; done

但到目前为止,它们都不起作用。一定有什么地方出了问题。希望在这里得到一些帮助。如果可能的话,也许有一些更好的指挥建议。

另外,我想知道我是否正确地写了第二个脚本中的粗体部分?

PS:我是脚本编写的初学者。我将不胜感激所提供的任何帮助。谢谢!

答案1

我的理解是,您有多个文件,它们的名称存储在一个名为 的文件中fileA,然后您想要打印每个文件中除存储在 中的文本之外的所有内容fileB,因此您可以执行以下操作:

while read -r file_name
do
grep -v -f <(grep -A1 -f fileB "$file_name") "$file_name"
done < file

它将打印内容stdout

答案2

fileA如果 中的文件名每行恰好列出一个并且文件名不包含任何换行符 ( \n) 字符,则以下内容有效:

$ xargs -d'\n' <fileA \
    perl -MFile::Slurp -e '
     my @patterns=read_file(shift, {chomp=>1});
     $re = join ("|",@patterns);

     while (<>) {
       if (m/$re/o) { readline; next };
       print
     }' fileB
9606.jklpan
how is the weather
10116.zyxtak
i am happy to see rainbow

xargs用于向 perl 脚本提供文件名参数列表,fileA一次读取一行。

Perl 脚本首先读取命令行 ( fileB) 上的第一个文件名参数,并构建组合每一行的正则表达式(在chomp结束每个输入行的换行符之后)。

之后,它循环遍历每个剩余的文件名参数,跳过任何匹配的行和下一行 - 打印剩余的行。

请注意,此脚本只是将所有输入文件的输出打印到 stdout,并且不会尝试区分不同输入文件的输出。

如果您希望每个输入文件的输出转到不同的输出文件(例如文件的输出234将转到234.new),您可以将整个while (<>) {...}循环替换为如下所示:

my $lastfn="";
while (<>) {
  if(eof) { close(OUTFILE) };

  if ($lastfn != $ARGV) {
    $lastfn=$ARGV;
    open(OUTFILE,">","$ARGV.new")
  };

  if (m/$re/o) { readline; next; };
  print OUTFILE
}

或者如果您只想在输出中显示文件名:

my $lastfn="";
my $nl="";   # we dont want to print a LF before the first output filename
while (<>) {
  if ($lastfn != $ARGV) {
    print "$nl", $ARGV,":\n";
    $nl="\n";
    $lastfn=$ARGV };
  };

  if (m/$re/o) { readline; next };
  print
}

或者将输入文件名作为每个输出行的前缀:

while (<>) {
  if (m/$re/o) { readline; next };
  print "$ARGV:$_"
}

最后,这可以完全在 perl 中完成,而不需要 xargs:

$ perl -MFile::Slurp -e '
   my @patterns=read_file(shift, {chomp=>1});
   $re = join ("|",@patterns);

   my @files=read_file(shift, {chomp=>1});
   @ARGV=@files;

   while (<>) {
     if (m/$re/o) { readline; next };
     print
   }' fileB fileA

答案3

我们将首先通过检查 fileB 构建 sed 命令文件,然后将该命令文件应用于 fileA 中列出的文件来解决此问题。

这里要注意的一点是我们引用 fileB 的内容,因为它们在稍后使用时应该是有效的 sed 语法。

$ sed -e '
   s:[][\/.^$*]:\\&:g
   s:.*:/&/{$q;N;d;}:
' < fileB > cmds

$ < fileA xargs -d'\n' -r -l sed -f cmds

这是解决问题的另一个视角,其中我们将 fileB 的行存储为散列的键,然后在读取 fileA 中列出的文件时检查是否找到任何键。

$ < fileA xargs -d'\n' -r \
   perl -ne 'BEGIN { $argc = @ARGV - 1; }
       @ARGV == $argc and $h{$_}++,next;
       print,close(ARGV) if eof;
       my $n = <>;
       print $_,$n if ! exists $h{$_};
' fileB

相关内容