awkコマンドが列2で機能するようにどのように指示できますか?

awkコマンドが列2で機能するようにどのように指示できますか?

awkこのコマンドを使用して、列2の最後の下線をタブに置き換えたいと思います。各行の最後の下線がタブ文字に置き換えられます。各行列の下線の数が異なる可能性があることに注意してください。私はコマンドが列2でのみ動作するように指示するためにいくつかの方法を試しました。かなり近づいていますが、最終調整をしてくれる人がいますか?

タブ区切りのサンプルファイル:

OTU1 this_is_the_second_column 100 0 450 this_is_the_sixth_column 1 5 3.2
OTU2 this_is_another_column_to_parse 103 4 650 this_is_another_test_string_too 4 7 4.6

次のようにする必要があります。

OTU1 this_is_the_second column 100 0 450 this_is_the_sixth_column 1 5 3.2
OTU2 this_is_another_column_to parse 103 4 650 this_is_another_test_string_too 4 7 4.6

これは私の現在のコードです。

gawk -F'\t' -v OFS='\t' 'BEGIN{FS=OFS="_"}{last=$NF;NF--;print $0"\t"last}' test1.tab > test1_reformat.tab

どんな助けでも大変感謝します。

ありがとう

答え1

GNU awkがあるようですので、これを使うことができます。この関数は、アンダースコアの後のアンダースコアではなく、末尾のシーケンスをキャプチャし、タブの後ろで再び置き換えます。

gawk 'BEGIN {OFS=FS="\t"} {$2 = gensub(/_([^_]*)$/, "\t\\1", "1", $2)} 1' test1.tab

または(私の考えでは移植可能)、このmatch関数を使用して文字列分割を実行できます。

awk 'BEGIN{OFS=FS="\t"} match($2,/_[^_]*$/) {$2 = substr($2,1,RSTART-1) "\t" substr($2,RSTART+1)} 1' test1.tab

答え2

ここに

gawk '
    BEGIN { OFS = FS = "\t" }              # Output as input as tabs
    {
        n = split($2, a, "_");             # Split $2 by "_" into array
        for(i = 1; i<n; i++) {
            s = (i>1 ? s "_" : "") a[i]    # Rejoin fields with "_"
        }
        $2 = s OFS a[n];                   # Join last with OFS
        print
    }
' file

コメントを削除すると、1行で実行できますが、本番コードではこれを行わないことをお勧めします。

入力例への出力

OTU1    this_is_the_second      column  100     0       450     this_is_the_sixth_column        1       5       3.2
OTU2    this_is_another_column_to       parse   103     4       650     this_is_another_test_string_too 4       7       4.6

答え3

解決策を過度に複雑にしないでください。必要なものを達成する1つの方法は次のとおりです。

入力内容が次のように保存されているとしますinfile(デモ目的で別の行を追加します)。

OTU1 this_is_the_second_column 100 0 450 this_is_the_sixth_column 1 5 3.2
OTU2 this_is_another_column_to_parse 103 4 650 this_is_another_test_string_too 4 7 4.6
OTU2 this_is_another_column_to_parse_and_parse 103 4 650 this_is_another_test_string_too 4 7 4.6

これにより、次のことができます。

awk -vOFS="\t" '{p = match($2, /_[^_]*$/); if (p) $2 = substr($2, 1, p-1) "\t" substr($2, p+1)}1' infile

出力:

OTU1    this_is_the_second  column  100 0   450 this_is_the_sixth_column    1   5   3.2
OTU2    this_is_another_column_to   parse   103 4   650 this_is_another_test_string_too 4   7   4.6
OTU2    this_is_another_column_to_parse_and parse   103 4   650 this_is_another_test_string_too 4   7   4.6

答え4

パールの使用:

$ perl -F"\t" -le 'BEGIN{ $, = "\t" };
                   $F[1] =~ s/^(.*)_(.*)/$1$,$2/;
                   print @F' test1.tab  
OTU1    this_is_the_second      column  100     0       450     this_is_the_sixth_column        1       5       3.2
OTU2    this_is_another_column_to       parse   103     4       650     this_is_another_test_string_too 4       7       4.6
  • -F"\t"Perlの自動分割モードをオンにし(awkに似ていますが、@F$ 1、$ 2、$ 3などの代わりに名前付き配列を使用して)、Perlにタブに分割するように指示します。また、およびを表示および検索できる-FPerlモードが開きます。-nsed -nman perlrun-F-a-n

    Perlの配列インデックスは0から始まるので、$F[0]最初の要素は0から始まり、$F[1]2番目の要素も0から始まる式です。

  • $,Perlの出力フィールド区切り変数(perlvarmanページで説明されています)。 BEGINブロック(BEGIN{ $, = "\t" })に設定すると、各入力行に対して1回ではなく、スクリプトの起動時に1回だけ実行されます。

  • $F[1] =~ s/^(.*)_(.*)/$1$,$2/_2番目のフィールドの最後の項目をに変更します$,。 Perlの正規表現のマッチングは基本的に貪欲であるため、^(.*)_以前のすべてのアイテムが一致してキャプチャされます。最後 _

  • @F次に配列を印刷します。

これは、Perlバージョンがインストールされている場合は機能するという点で移植可能です(つまり、GNU awk forなどの非標準バージョンは必要ありませんgensub())。


または、設定の代わりにPerlのjoin()機能(参照)を使用してください。perldoc -f join$,

perl -F"\t" -le '$F[1] =~ s/^(.*)_(.*)/$1\t$2/;
                 print join "\t", @F' test1.tab

別の方法は、このsplice()関数(参考文献を参照perldoc -f splice)を使用して、フィールド2と3の間の配列に実際に新しい要素を挿入することです@F(フィールド区切り文字を含む2番目の要素とは反対)。新しい要素は3番目の要素($F[2])になり、その後のすべての要素のインデックスは1ずつ増加します。

これは、新しいフィールドを挿入した後に配列で追加の処理を実行する必要がある場合に便利です(awkとは異なり、Perlには配列操作機能が組み込まれているため、配列から要素を挿入または削除するのは簡単です)。

perl -F"\t" -le '$F[1] =~ s/^(.*)_(.*)/$1/;
                 splice @F, 2, 0, $2;
                 print join "\t", @F' test1.tab

注目すべき点:代替演算子からグループをキャプチャすることは、そのグループがs///範囲外になるか、他の正規表現が一致するか、置換が成功するまで続きます。これがすぐに$2使用できる理由ですsplice

$F[1]これはまた、このバージョンが入力ラインで正しく機能しないことを意味します。いいえ文字を含めます_(最後に成功した一致に $2 を含む 3 番目のフィールドを挿入するか、最初の置換が成功するまで空のフィールドを挿入します)。これを処理するには、交換が成功したかどうかをテストする必要があります。たとえば、次のようになります。

perl  -F"\t" -le 'if ($F[1] =~ s/^(.*)_(.*)/$1/) {
                    splice @F, 2, 0, $2;
                  } else {
                    splice @F, 2, 0, ""; # insert empty field 3
                  };
                  print join "\t", @F' test1.tab

他のバージョンも実際には正しく動作しません。タブで区切られたさまざまなフィールドの数(2番目のフィールドにアンダースコアが含まれている場合は10個、それ以外の場合は9個)を含む行を出力します。

1. 2 番目のフィールドに常に 1 つ以上の下線が含まれることが保証されている場合、または 2. 出力にフィールドが 9 つか 10 つかは関係ありません。

置換が失敗した場合は、2 番目のフィールドにフィールド区切り文字を追加してこれらのバージョンを変更できます。たとえば、次のようになります。

$F[1] .= $, unless $F[1] =~ s/^(.*)_(.*)/$1$,$2/;

またはこれ:

$F[1] .= "\t" unless $F[1] =~ s/^(.*)_(.*)/$1\t$2/;

しかし、これまでの他の回答のawkバージョンにはすべて同じ基本的な問題があります。つまり、2番目のフィールドの内容に応じて可変数のフィールドも出力するということです。問題を解決することは難しくありません。

関連情報