web-dev-qa-db-ja.com

特定のフィールドでファイルをグレップする

2つのファイルがあるとしましょう

ファイル1:

Locus_1
Locus_2
Locus_3

ファイル2:

3  3  Locus_1  Locus_40  etc_849    
3  2  Locus_2  Locus_94  *    
2  2  Locus_6  Locus_1  *    
2  3  Locus_3,Locus_4  Locus_50  *    
3  3  Locus_9  Locus_3  etc_667

出力など、最初のファイルに対してgrep -Fを実行したいのみ 2番目のファイルの3列目(元のFile2フィールドはタブで区切られています)でなければなりません:

出力:

3  3  Locus_1  Locus_40  etc_849    
3  2  Locus_2  Locus_94  *    
2  3  Locus_3,Locus_4  Locus_50  *

どうすればできますか?

カオスに編集:いいえ、カンマは間違いではありません。 1つの列に複数のLocus_ *を含めることができます。2番目のLocus_ *(コンマの後の1つ)がFile1の行の1つと一致する場合も、取得します。

6
LinuxBlanket

grepが必要ない場合、1つの簡単な解決策はjoinを使用することです。

_$ join -1 1 -2 3 <(sort file1) <(sort -k3 file2)
Locus_1 3 3 Locus_40 etc_849
Locus_2 3 2 Locus_94 *
Locus_3 2 3 Locus_4 Locus_50 *
_

説明

  • _join -1 1 -2 3_:2つのファイルを結合します。最初のファイルでは最初の(そして唯一の)フィールドが使用され、2番目のファイルでは3番目のフィールドが使用されます。それらが等しいときに印刷されます。
  • <(sort file1)joinはソートされた入力が必要です
  • <(sort -k3 file2):入力は結合フィールド(ここでは3番目のフィールド)でソートする必要があります
5
chaos

https://stackoverflow.com/a/9937241/1673337 からソリューションを適応させると、(g)awkを使用して取得できます:

awk 'NR==FNR{a[$0]=1;next} {for(i in a){if($3~i){print;break}}}' File1 File2

指定された出力を提供します。

RegExを作成してgrepにフィードし、3列目のマッチングのみを満たすようにすることもできますが、この時点でawkを使用する方が理解しやすいと思います。

if($3~i){print;break}部分は、3番目の列がFile1の行(配列aに格納されている)と一致する場合にのみ印刷を行います。残りの説明については、リンクされた投稿を参照してください。

これによりFile1の内容全体がメモリに読み込まれることに注意してください。ただし、比較の乗法的な性質のため、これが問題になるのは、ファイルが大きい場合のみです。

5
IsoLinearCHiP

grep -Fオプションを使用すると、現在の行でliteral文字列anywhereが検索されます。定義により、literalは、正規表現を使用して検索をフィールド3 (TAB区切り)内のみに絞り込むことができないことを意味します。

ただし、grep -fを使用してパターン入力を読み取ることができますfile1-ただし、正規表現のリストに変更する必要があります。以下は、bashプロセスの置換を使用してsedを使用し、grep -fが処理できる標準正規表現のリストを生成する方法の1つです。

Basic Regular Expressionsでgrepを使用する:

  grep -f <(sed 's/.*/^\\([^\t]\\+\t\\)\\{2\\}\\([^\t]\\+,\\)*&[,\t]/'  file1) file2

GrepのBasic regexの場合、file1は動的に次のように変換されます。

^\([^   ]\+ \)\{2\}\([^ ]\+,\)*Locus_1[,    ]
^\([^   ]\+ \)\{2\}\([^ ]\+,\)*Locus_2[,    ]
^\([^   ]\+ \)\{2\}\([^ ]\+,\)*Locus_3[,    ]

[〜#〜] or [〜#〜]grep -E拡張正規表現とともに使用すると、コードを視覚的に簡略化して、 grepsedの両方にほとんどのバックスラッシュが必要

grep -Ef <(sed 's/.*/^([^\t]+\t){2}([^\t]+,)*&[,\t]/' file1) file2

Grepの拡張正規表現の場合、file1は動的に次のように変換されます。

^([^    ]+  ){2}([^ ]+,)*Locus_1[,  ]
^([^    ]+  ){2}([^ ]+,)*Locus_2[,  ]
^([^    ]+  ){2}([^ ]+,)*Locus_3[,  ]

出力(両方の場合)

3   3   Locus_1 Locus_40    etc_849     
3   2   Locus_2 Locus_94    *       
2   3   Locus_3,Locus_4 Locus_50    *       

-fおよび-Fは、file1が大きい間、物事を劇的に遅くする可能性があることに注意してください。

3
Peter.O

_grep -P_ソリューション:

_regexp=$( echo -n '('; < File1 tr '\n' '|' | sed 's/|$//'; echo ')' )
grep -P "^[^\s]+\s+[^s]+\s+([^\s]*,)*$regexp" File2
_

出力:

_3  3  Locus_1  Locus_40  etc_849    
3  2  Locus_2  Locus_94  *    
2  3  Locus_3,Locus_4  Locus_50  *    
_

_File1_に特殊な正規表現文字が含まれている可能性がある場合は、それらをエスケープする必要があります。

_regexp_escape() { Ruby -pe '$_ = Regexp.escape($_.chomp("\n")) + "\n"'; }

regexp=$( echo -n '('; < File1 regexp_escape  |  tr '\n' '|' | sed 's/|$//'; echo ')' )
grep -P "^[^\s]+\s+[^s]+\s+([^\s]*,)*$regexp" File2
_

説明:

2行目は、_(Locus_1|Locus_2|Locus_3)_などの文字列を作成します。そして

"^[^\s]+\s+[^s]+\s+([^\s]*,)*"

手段:

  • [Word] [whitespace(s)] [Word] [whitespace(s)] [(optional Word followed by comma) zero or arbitrarily many times]
2
PSkocik
(   t=$(printf \\t) ntt=[^$t]*$t ntc=[^$t,]*
### ^just makes it easy regardless of your sed version.
    sed  -ne"s/..*/^($ntt){2}($ntc,)*&(,$ntc)*$t/p" |
    grep -Ef- ./File2
)   <File1

3   3   Locus_1 Locus_40    etc_849
3   2   Locus_2 Locus_94    *
2   3   Locus_3,Locus_4 Locus_50    *

これにより、先行する($ntc,)*グループの数や後続の(,$ntc)*グループの数に関係なく、File2の3番目の列のFile1の行に一致します。ただし、File1の検索文字列にメタ文字がないことに依存します。 File1にメタ文字がある可能性がある場合は、まずそれをクリーンアップする必要があります。

(   t=$(printf \\t) ntt=[^$t]*$t ntc=[^$t,]*
    sed  -ne's/[]?{(^$|*.+)}\[]/\\&/g' \
          -e"s/..*/^($ntt){2}($ntc,)*&(,$ntc)*$t/p" |
    grep -Ef- ./File2
)   <File1
2
mikeserv

列を「grep」するにはawkが最適なツールです

BEGIN { f="Locus_2" }
$3==f { print $0; }

file1をループできます

for x in `cat File1`
do awk -v X="$x" '$3~X { print $0 }' <File2
done

1
ikrabbe