web-dev-qa-db-ja.com

awkで500個のファイルをフィルタリングしてから、猫の結果を1つのファイルに

私は数千のファイルを含むディレクトリにいますが、すべてのフィルターを適用するファイルの構文は次のとおりです:_*.imputed.*_info_

Awkを使用して、データの5番目の列の値が0.50を超える各ファイルのレコードをフィルターで除外し、awk '{if($5 >= .5) {print}}' filenameを使用してそれを行うことができました。

それもうまくいきました。次に、500ほどのファイルすべてをループして、この基準に一致する各ファイルのレコードを連結しようとしました。

以下を試しましたが、構文が正しくありません。

_touch snplist.txt
for name in *.imputed.*_info; do
    snps="awk '{if($5 >= .5) {print}}' $name"
    cat snplist.txt "$snps" > snplist.txt
done
_
5
akaDrHouse

コードは各反復で出力ファイルを上書きします。また、実際にはawkを呼び出しません。

やりたいことは

_awk '$5 >= 0.5' ./*.imputed.*_info >snplist.txt
_

これは、すべてのファイルを使用してawkを一度に呼び出し、シェルが展開パターンを展開する順序で1つずつファイルを処理します。ファイルのいずれかの行の5番目の列が0.5以上の場合、その行は(_snplist.txt_に)出力されます。これは、条件に関連付けられているアクション(_{...}_ブロック)がない場合、デフォルトのアクションが現在の行を出力するために機能します。

大きなファイルの数(数千)がある場合、これは「引数リストが長すぎます」エラーを生成する可能性があります。その場合は、ループすることをお勧めします。

_for filename in ./*.imputed.*_info; do
    awk '$5 >= 0.5' "$filename"
done >snplist.txt
_

awkの結果を変数に格納する必要がないことに注意してください。ここでは、出力されただけで、ループ(したがって、ループ内のすべてのコマンド)が_snplist.txt_にリダイレクトされます。

何千ものファイルの場合、awkをファイルごとに個別に呼び出す必要があるため、これは非常に遅くなります。

スピードアップするために、awkの1回の呼び出しではファイルが多すぎる場合は、次のようにxargsの使用を検討してください。

_printf '%s\0' ./*.imputed.*_info | xargs -0 awk '$5 >= 0.5' >snplist.txt
_

これにより、printfを使用してファイル名のリストが作成され、NUL終了リストとしてxargsに渡されます。 xargsユーティリティはこれらを受け取り、awkできるだけ多くで一度にバッチで開始します。パイプライン全体の出力は_snplist.txt_にリダイレクトされます。

このxargsの代替案は、LinuxなどのUnixを使用していることを前提としています。これには、NUL終了入力を読み取るための非標準の_-0_オプションを実装するxargsコマンドがあります。また、_組み込みbashユーティリティ(printf、OpenBSDのデフォルトのシェル)があるkshのようなシェルを使用していることも前提としています。このような組み込みユーティリティがないため、ここでは機能しません)。


zshシェルの場合(つまり、bashではない):

_autoload -U zargs
zargs -- ./*.imputed.*_info -- awk '$5 >= 0.5' >snplist.txt
_

これはzargsを使用します。これは、基本的にxargsをロード可能なzshシェル関数として再実装したものです。詳細については、_zargs --help_(関数のロード後)およびzshcontrib(1)マニュアルを参照してください。

14
Kusalananda

これを行うだけです:

awk '$5 >= .5' *.imputed.*_info > snplist.txt
5
Gilles Quenot

私はこのようなことのためにfindを使う習慣があります。

find . -type f -name "*.imputed.*_info" -exec awk '$5 >= 0.5' {} \; > ./snplist.txt
3
voices