awkを使用して2つの文字列のいずれかを含むテーブルのすべてのフィールドを印刷する方法

awkを使用して2つの文字列のいずれかを含むテーブルのすべてのフィールドを印刷する方法

行が多く、行ごとに列数が可変なテーブルがあります。

各行で最初のフィールドと2つの文字列のうちの1つを含むすべてのフィールドのみを印刷したい(この場合は、dogとcowという単語を含むすべてのフィールドが必要です)。

たとえば、

A   dog999   dog284   cow284   pig383   pig234   cow432   chicken432
B   cow394   cow432   cow345   dog983   pig345   chicken532 
C   dog847   pig357   pig236   cow395   dog496
D   dog392   cow237   cow749

希望の出力:

A   dog999   dog284   cow284   cow432   
B   cow394   cow432   cow345   dog983   
C   dog847   cow395   dog496
D   dog392   cow237   cow749

これまで私はawkを使用していました。

awk -v OFS='\t' '{for (i = 1; i <= NF; i++) {if ($i ~ /dog/) print $1,$i; else if ($i ~ /cow/) print $1,$i} }' file.txt

ただし、これにより各フィールドに2つの文字列のうちの1つが1行を占有します。

答え1

perl回避策が大丈夫な場合:

$ cat ip.txt 
A   dog999   dog284   cow284   pig383   pig234   cow432   chicken432
B   cow394   cow432   cow345   dog983   pig345   chicken532 
C   dog847   pig357   pig236   cow395   dog496
D   dog392   cow237   cow749

$ perl -lane 'print join("\t",$F[0],grep {/cow|dog/} @F[1..$#F])' ip.txt 
A   dog999  dog284  cow284  cow432
B   cow394  cow432  cow345  dog983
C   dog847  cow395  dog496
D   dog392  cow237  cow749
  • -a入力行をスペースに分割して@F配列に保存
  • -l入力から改行文字を削除して印刷するときにもう一度追加してください。
  • join\t印刷するとき、
  • $F[0],grep {/cow|dog/} @F[1..$#F]配列の最初の要素とcow一致するすべての要素、またはdog
  • また利用可能ですperl -lape'$_=join"\t",shift(@F),grep/cow|dog/,@F'。ここでは配列の最初の要素をshift削除して返し、結果を に割り当てると最後に無料オプションが印刷されます(ヒント)。@F$_-pスティーブン・チャジェラス)


以下を含まないかcow無視されるdog行:

perl -lane 'print join("\t",$F[0],grep {//} @F[1..$#F]) if /cow|dog/' ip.txt 

答え2

ほとんど似ていますが、一致するすべての単語について印刷したくないので、最初の値を抽出する必要があります。これを使用してprintf改行を避けることができます。

awk '{printf "%s",$1
      for (i=1;i<=NF;i++)
      {
        if ($i ~ /dog|cow/) { printf " %s",$i; }
      }
      print ""
     }'

出力は次のとおりです。

A dog999 dog284 cow284 cow432
B cow394 cow432 cow345 dog983
C dog847 cow395 dog496
D dog392 cow237 cow749

これは1行に縮小できます。

awk '{printf "%s",$1; for (i=1;i<=NF;i++) { if ($i ~ /dog|cow/) { printf " %s",$i; }  } print ""  }'

これにより、どの単語にも一致しない行が印刷されます。

E pig sheep

出力されます

E

答え3

TxR マクロ:

$ txr -e '(awk (:let tmp)
               (:begin (set ofs "\t"))                     
               (f (set tmp (pop f))
                  (ff (keep-if #/cow|dog/))
                  (push tmp f) (prn)))' data
A   dog999  dog284  cow284  cow432
B   cow394  cow432  cow345  dog983
C   dog847  cow395  dog496
D   dog392  cow237  cow749

分解:

  1. :letマクロの句はローカル変数を指定します。このマクロは「Awk Paradigm」を実装していますが、使用する前に変数を定義する必要がある型安全言語で実装されています。したがって、(POSIX Awkに似ている)など:beginの句に加えて、このAwkは語彙範囲マクロで定義された変数も提供します。:endBEGINEND:let

  2. (f (set tmp (pop f)) ...)条件がある条件付き作業句ですf。レコードの区切りフィールドリストの場合、空でない場合(と等しくないnil)、ブール値trueのように機能します。したがって、何もない場合は、ジョブフォームが実行されますf

  3. (set tmp (pop f))リストから最初のフィールドをポップして一時変数に保存しますtmp。 2番目のフィールドが1番目のフィールドになり、3番目のフィールドが2番目のフィールドになる式です。fレコードは、フィールドを使用してレコードが再編成されるPOSIX Awkと同様に、作業時にrec自動的に再編成されます。ofs$0OFS

  4. (ff ...)この場合、タスクごとにフィールドをフィルタリングします(keep-if #/regex/)。デフォルトでは、f正規表現と一致しないすべてのフィールドを削除します。ffマクロ内に表示される演算子ですawkkeep-if一般的な関数です。ここでは暗黙的にカレーなので、リスト引数は表示されません。述語関数が必要ですが、正規表現は関数呼び出し可能なので、述語として適しています。

  5. 次に、以前に保存した最初のフィールドをフィールドfリストに戻します(push tmp f)

  6. (prn)と同じですprint。引数がない場合は、レコードとors改行文字で初期化される出力レコード区切り文字()を印刷します。recのすべての操作が再構成されたため、フィルタリングされたf出力を取得します。

ご覧のとおり、Awkパラダイムは基本的に完全です。他の言語の文脈で異なる種類のことが可能であるということです。フィールドが実際に存在することを確認せずに操作できる便利さは$2 > $1ありませんが、フィールドをデータ構造として扱うためにループを作成する必要はありません。フィールドは関数を介してマッピングすることも、スタックとして表示することもできます。

SundeepのPerlソリューションは、おおよそawk次のマクロに変換されます。

$ txr -e '(awk (t (prn `@[f 0]\t@{(keep-if #/cow|dog/ [f 1..:]) "\t"}`)))' data

関連情報