ファイルの各行について、その値が他のフィールドの値より小さい場合、特定の列のフィールドをNFとして印刷します。

ファイルの各行について、その値が他のフィールドの値より小さい場合、特定の列のフィールドをNFとして印刷します。

1行あたりのフィールド数が可変である次の形式のファイルがあります。

NC_000001.11_NM_001005484.2 69270   234 69037   65565   69037
NC_000001.11_NM_001005484.2 69511   475 69037   65565   69037
NC_000001.11_NM_001005484.2 69761   725 69037   65565   69037
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772 939040   939272  941144  942136  942410  942559  943253  943698  943908  

各行に対して最初の4つのフィールドを印刷したいと思います。残りのフィールド($ 5〜NF)の場合、このフィールドの値が$ 4の値より小さい場合は、そのフィールドを印刷したいと思います。

出力例:

NC_000001.11_NM_001005484.2 69270   234 69037   65565   
NC_000001.11_NM_001005484.2 69511   475 69037   65565   
NC_000001.11_NM_001005484.2 69761   725 69037   65565   
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772 939040   939272  941144  

いくつかのawkオプションを試しましたが、すべて失敗しました。 awkに初めて触れてくれて助けてくれてありがとう。

答え1

出力でスペースを気にしない場合は、必要なものは次のとおりです。

$ cat tst.awk
{
    out = $1 OFS $2 OFS $3 OFS $4
    for (i=5; i<=NF; i++) {
        if ( $i < $4 ) {
            out = out OFS $i
        }
    }
    print out
}

$ awk -f tst.awk file
NC_000001.11_NM_001005484.2 69270 234 69037 65565
NC_000001.11_NM_001005484.2 69511 475 69037 65565
NC_000001.11_NM_001005484.2 69761 725 69037 65565
NC_000001.11_NM_001385640.1 942155 20 942136 924432 925922 930155 931039 935772 939040 939272 941144

column必要に応じて、パイプを介して視覚的な位置合わせを実行できます。

$ awk -f tst.awk file | column -t
NC_000001.11_NM_001005484.2  69270   234  69037   65565
NC_000001.11_NM_001005484.2  69511   475  69037   65565
NC_000001.11_NM_001005484.2  69761   725  69037   65565
NC_000001.11_NM_001385640.1  942155  20   942136  924432  925922  930155  931039  935772  939040  939272  941144

そうでなければ、出力の間隔が入力の間隔のように見えるようにしたい場合(つまり、最初の4つのフィールドは1つ以上の空白として見え、残りは2つ以上の空白として表されます)、一部の行には4つ以下の空白しかありません。 。フィールドを選択してPOSIX awkを使用します(文字クラスと正規表現スペース用)。

$ cat tst.awk
BEGIN { OFS="\t" }
match($0,/([^[:space:]]+[[:space:]]+){3}[^[:space:]]+/) {
    out = substr($0,RSTART,RLENGTH)
    for (i=5; i<=NF; i++) {
        if ( $i < $4 ) {
            out = out OFS $i
        }
    }
    $0 = out
}
{ print }

$ 4以降のフィールドをタブで区切る必要がある場合:

$ awk -f tst.awk file
NC_000001.11_NM_001005484.2 69270   234 69037   65565
NC_000001.11_NM_001005484.2 69511   475 69037   65565
NC_000001.11_NM_001005484.2 69761   725 69037   65565
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772  939040  939272  941144

またはスペースで区切る必要がある場合:

$ awk -f tst.awk file | column -s$'\t' -t
NC_000001.11_NM_001005484.2 69270   234 69037   65565
NC_000001.11_NM_001005484.2 69511   475 69037   65565
NC_000001.11_NM_001005484.2 69761   725 69037   65565
NC_000001.11_NM_001385640.1 942155  20  942136  924432  925922  930155  931039  935772  939040  939272  941144

上記は、入力のタブおよび/またはスペースの組み合わせに対して最初の4つのフィールド間のスペースを保持し、次に5番目以降のフィールドの前にタブを印刷します。これを使用して、同等のcolumnアイテムが必要な場合は空白のままにできます。どちらも質問の入力と出力のように見えます。

out上記のループで名前付きの新しい文字列を作成し、ループを変更するか、ループ内で変更するのではなく、ループの後に$0一度割り当てます。これは、awkを変更するたびにそのフィールドに再構築する必要があり、awkを変更するたびに再分割する必要があるためです。フィールドに含まれるため、両方とも非効率的であり、フィールドの内容によって予期しないエラーが発生する可能性があるため、非常に特定の目的がない限り、ループ内で修正または修正しないでください。$0$i$i$0$0$0$0$i

答え2

これはGNU Awk 5.1.0、API:3.0を使用してテストされました。splitこれは、このソリューションの4番目のパラメータを使用すると、ここで使用されている構文と互換性のない他のバージョンでは機能しない可能性があるためです。

awk '{n=split($0, a, " ", b); line=""; for (i = 1; i <= n; i++) { if (i < 5 || a[i] < $4) line=(line a[i] b[i])}; print line; }' file.txt

説明する:

  • n=split($0, a, " ", b);- 行全体($0)を値(に保存されているa)とスペース(に保存されているb)に分割するので、元のファイルの形式を保存できます。保存された値は、n各行に対して処理されるフィールドの数を提供します。split配列abインデックスは両方とも1から始まります。
  • line=""- 空の文字列で始まる
  • for (i = 1; i <= n; i++)- 各フィールドを繰り返し、インデックス1から分割してループを作成します。<=最後の(n番目)フィールドも処理されることを部分的に保証します。
  • if (i < 5 || a[i] < $4)- 最初の4つのフィールドまたはフィールド値が4番目のフィールド(必要な条件)より小さい場合、条件はtrueです。
  • line=(line a[i] b[i])- 「if」条件要件を満たす古いフィールドとスペースと実際のフィールドとスペースを関連付けます。
  • print line-line目的の出力を含む変数を印刷します。

答え3

これは、行の終わりから行の先頭まで(つまり、逆の順序で)フィールドを繰り返し、NFフィールド番号()が4より大きい場合はフィールドを削除します。そしてこのフィールドの値は、$4フィールド4()の値よりも大きいです。

$ awk '{
    for (i=NF; i>=1; i--) {
      if ((i > 4) && ($i >= $4)) {
        $i=""
      }
    };
    print
    }' input.txt
NC_000001.11_NM_001005484.2 69270 234 69037 65565 
NC_000001.11_NM_001005484.2 69511 475 69037 65565 
NC_000001.11_NM_001005484.2 69761 725 69037 65565 
NC_000001.11_NM_001385640.1 942155 20 942136 924432 925922 930155 931039 935772 939040 939272 941144 

ところで、入力内容がスペースかタブで区切られているかはわかりません。各フィールドの間にスペースの代わりにタブ区切りの出力が必要な場合は、-v OFS='\t'スクリプトを起動する単一引用符の前にawkコマンドを追加してください。例えば

awk -v OFS='\t' '...awk script here...' input.txt

ところで、awk は、フィールドが削除される前の位置に関係なく、出力行に多くの追加フィールド区切り文字を残します。これを削除するには、ステートメントの前に次の行を追加しますprint

    $0=$0; $1=$1;

これにより、awkは入力行を再評価し、それをフィールドに再分割するように強制し、空のフィールドを効果的に削除します(FSから分割、フィールド区切り文字、デフォルトはスペースの数に制限なし)。 awkは実際に行のフィールドを削除する方法がないので、行を修正した後に強制的に削除する必要があるため、これは少しハッキングです。

関連情報