一致する行と連続した行を削除する(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

しかし、これまでのところ、それらのどれも動作しません。何か問題があるようです。ここで助けを願っています。可能であれば、より良いコマンド提案があるかもしれません。

そして、2番目のスクリプトで太字の部分を正しく書いたかどうかを知りたいです。

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のファイル名が正確に1行に1つずつリストされ、ファイル名に改行(\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

xargsPerlスクリプトにファイル名パラメータのリストを提供するために使用され、fileA一度に1行ずつ読みます。

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:$_"
}

最後に、xargsなしでperlで完全に実行できます。

$ 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にリストされているファイルに適用してこの問題を解決します。

ここで注目すべき点は、後で使用するときに有効なsed構文でなければならないため、fileBの内容を引用していることです。

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

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

トラブルシューティングのもう1つの観点は、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

関連情報