sed 代替使用が期待どおりに機能しません。

sed 代替使用が期待どおりに機能しません。

大容量ASCIIテキストファイルで検索/置換機能を実行する必要があります。入力ファイルからの短い抜粋:

gene_id "MSTRG.1";
gene_id "MSTRG.1";
gene_id "MSTRG.2";
gene_id "MSTRG.3";

このMSTRG文字列は、テンプレートファイル内の他のIDに置き換えられます。

MSTRG.1 AT1G01030
MSTRG.2 AT1G01010
MSTRG.3 AT1G01035

単純なwhileループは、テンプレートの各行を繰り返して置き換えます。

while read bef aft
do
  echo "Searching for $bef"
  echo "Replacing with $aft"
  sed "s/$bef/$aft/g" input > output
done < template

MSTRG.2その後、アイテムは正しく置き換えられますが変更されていませんMSTRG.1。出力は次のとおりです。

gene_id "MSTRG.1";
gene_id "MSTRG.1";
gene_id "AT1G01010";
gene_id "AT1G01035"; 

修正する

これが私がしたことです。

while read bef aft
do
  sed -i "s/$bef/$aft/g" input
done < template

答え1

問題は、ループを繰り返すたびに出力ファイルを削除して最新の変更のみを残し、output以前の変更は残らないことです。

代わりに、templateファイルを一連のsedコマンドに簡単に変換できます。

$ awk '{ printf("s/%s/%s/g\n", $1, $2) }' template
s/MSTRG.1/AT1G01030/g
s/MSTRG.2/AT1G01010/g
s/MSTRG.3/AT1G01035/g

...その後、ファイルに適用します。

$ awk '{ printf("s/%s/%s/g\n", $1, $2) }' template | sed -f - input
gene_id "AT1G01030";
gene_id "AT1G01030";
gene_id "AT1G01010";
gene_id "AT1G01035";

一部の実装では、標準入力の意味をsed認識しません。-このタイプのこの方法を使用するには、にsed置き換えます。-f --f /dev/stdin

または、以下からすべての操作を実行できますawk

$ awk 'FNR == NR { pat[$1] = $2; next } { for (p in pat) gsub(p, pat[p]); print }' template input
gene_id "AT1G01030";
gene_id "AT1G01030";
gene_id "AT1G01010";
gene_id "AT1G01035";

上記のすべてのバリアントは、最初の列の内容を次のtemplateように使用します。正規表現、意味.(点)が一致どの特徴。

答え2

各ループ反復で出力ファイルを上書きするのではなく、入力ファイルを出力ファイルにコピーしてこの出力ファイルで作業できます。

sedオプションの変更は-i同じファイルに書き込まれるため、以前の代替エントリは失われません。

cp input output
while read bef aft
do
  echo "Searching for $bef"
  echo "Replacing with $aft"
  sed -i "s/$bef/$aft/g" output
done < template

答え3

#!/usr/bin/perl -i

use strict;

# The %re hash holds the regexp searches and replacement strings.
my %re = ();

my $tfile = shift;
open(TEMPLATE, "<", $tfile) || die "couldn't open $tfile for read: $!\n";
while(<TEMPLATE>) {
   chomp;
   my ($search,$replace) = split;
   $re{qr/$search/} = $replace;
};
close(TEMPLATE);

while (<>) {
  foreach my $s (keys %re) {
    s/$s/$re{$s}/g;
  };
  print;
}

これはtemplate、ファイルを読み取り、%re正規表現の検索と置換という連想配列(別名「ハッシュ」)を構築します。

次に、コマンドライン(たとえばinput)に残っている各ファイル名を繰り返し、各入力行で検索と置換の両方を実行します。正規表現をプリコンパイルするために使用されますqr//。行が多いと、これはマイナーな最適化にすぎませんが、行が多いとtemplate速度が大幅に向上する可能性があります。

この-i#!/usr/bin/perl -iは、変更を標準出力に印刷するのではなく、Perlに入力ファイルの内部編集を実行させます。たとえば、-i.bakファイルが変更される前にファイルのバックアップコピーをアーカイブするには、に変更します。

たとえば、別の名前で保存してcryptic0.pl実行可能にし、chmod +x cryptic0.pl次のように実行します。

$ ./cryptic0.pl template input

スクリプトは端末に出力を生成しません。代わりに入力ファイルを編集します。

たとえば、inputファイルは次のように変更されます。

$ cat input
gene_id "AT1G01030";
gene_id "AT1G01030";
gene_id "AT1G01010";
gene_id "AT1G01035";

ちなみに、このスクリプトはすべての行のすべての項目を適切な代替文字列に変更します。確信していれば、ただ持てる一つ指定された行と一致する場合は、次の行を変更して作業を高速化できます。

s/$s/$re{$s}/g;

これに関して:

s/$s/$re{$s}/ && last;

これにより、スクリプトは foreach ループからそのステートメントに移動し、検索printと置換が成功した直後に次の入力行に移動します。


ところで、参照してくださいシェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?テキスト処理にshループを使用するのはなぜ悪い考えですか?または代わりに、または、または他のawkものをperl使用してください。sedshbash

関連情報