2016-07-11 6 views
0

whileループ内でuntilループをどのように使用できるかは不明です。複数の列を並べ替え、キーごとの出力ファイルを使用

私はこのようになり50万ラインの入力ファイルがあります。

9  1  1 0.6132E+02 
    9  2  1 0.6314E+02 
    10  3  1 0.5874E+02 
    10  4  1 0.5266E+02 
    10  5  1 0.5571E+02 
    1  6  1 0.5004E+02 
    1  7  1 0.5450E+02 
    2  8  1 0.5696E+02 
    11  9  1 0.6369E+02 
    ..... 

をそして私は達成するために望んでいることは、私はすべて引き出すことができるよう、番号順に最初の列に数字を並べ替えることです(同じ番号で始まる行)を新しいテキストファイル"cluster${i}.txt"にコピーします。そこから("cluster${i}.txt")ファイルの第4列を番号順にソートしたいと思います。ソート後、ソートされた各"cluster${i}.txt"ファイルの最初の行を1つの出力ファイルに書きたいと思います。

1  6  1 0.5004E+02 
1  7  1 0.5450E+02 
1  11  1 0.6777E+02 
.... 

だけでなく、次のようになりoutput.txtとファイル:ここで

1  6  1 0.5004E+02 
2  487  1 0.3495E+02 
3  34  1 0.0344E+02 
.... 

は、私が書いたものです:

#!/bin/bash 

input='input.txt' 
i=1 

sort -nk 1 $input > 'temp.txt' 

while read line; do 
    awk -v var="$i" '$1 == var' temp.txt > "cluster${i}.txt" 
    until [[$i -lt 20]]; do 
    i=$((i+1)) 
    done 
done 

for f in *.txt; do 
    sort -nk 4 > temp2.txt 
    head -1 temp2.txt 
    rm temp2.txt 
done > output.txt 
"cluster1.txt"のサンプル出力は、このことを希望します
+0

http://shellcheck.net/はあなたの友人です。 –

+0

... btw、これは、シェルループの中に小さな小さなawk呼び出しの束ではなく、1つの大きな 'awk'スクリプトで(もっと速く、より効率的に)行うほうが**大変です**。 –

+0

...同様に、 '> foo'は実際には高価な操作です - そのリダイレクションの行が実行されるたびに、コマンドの前に' foo'を開き、その後にそれを閉じます。あなたは効率的な観点から、ファイルをもっと長く開いたほうがはるかに優れています。 –

答えて

3

指数表記の処理方法がわかっているsort -nの場合は、1行しかかかりません。

sort -nk 1,4 <in.txt | awk '{ of="cluster" $1 ".txt"; print $0 >>of }' 

...か、またoutput.txtへの各インデックスの最初の行を記述します

sort -nk 1,4 <in.txt | awk ' 
    { 
    if($1 != last) { 
     print $0 >"output.txt" 
     last=$1 
    } 
    of="cluster" $1 ".txt"; 
    print $0 >of 
    }' 

awk実装の使用を検討 - などのGNU AWKとして - ファイルディスクリプタをキャッシュするのではなく、再開れます各出力ファイルはすべての追加のために;これによりパフォーマンスが大幅に向上します。ところで


、のは、元のスクリプトで間違っていたものを見てみましょう:

  • それは遅かったです。本当には本当にです。 while readの全体のポイントは、個々の行を反復処理することであるため

    のでwhile readawkを入れて、(入力のライン毎にawk 20回の新しいインスタンスを起動するには、行ごとに一度awk少なくともを実行しようとしています)はパフォーマンスに非常に大きな影響を与えます。ない

  • while read line外側のループは、標準入力からtemp.txtinput.txtのない読んでいた...ので、それは実際には、これをやったこと。 したがって、stdinが何も書かれていない場合、またはstdinが/dev/nullのような内容のソースを指していない場合、ループの内容を全く実行していない場合、スクリプトはハングしていました。

  • 内側のループは、実際に外側のループによって読み取られたlineを処理していませんでした。 lineが読まれましたが、temp.txtのすべてが操作されていました。
  • awkは実際には内側のループの内側ではなく、外側のループの内側にありました。の前にの内側ループです。その結果、iの異なる値で20回実行されませんでしたが、1回の読み取りで1回だけ実行され、iの値は以前に実行されたコードから残されていました。
  • ホワイトスペースは、コマンドの解析方法にとって重要です。 [[foo]]が間違っています。 [[ foo ]]である必要があります。

次のようになります、私はあなたが書くためのものを想像何をするために、内部ループを「修正」するには:

# this is slow and awful, but at least it'll work. 
while IFS= read -r line; do 
    i=0 
    until [[ $i -ge 20 ]]; do 
    awk -v var="$i" '$1 == var' <<<"$line" >>"cluster${i}.txt" 
    i=$((i+1)) 
    done 
done <temp.txt 

...か、幾分良好(ただし、静止画として良くありません上部に提案された解決策):output.txtへのリダイレクトがループ全体のために、一度だけをどのように行われるか

# this is a somewhat less awful. 
for ((i=0; i<=20; i++)); do 
    awk -v var="$i" '$1 == var' <temp.txt >"cluster${i}.txt" 
    head -n 1 "cluster${i}.txt" 
done >output.txt 

注 - これは、我々は一度だけファイルを開いていることを意味します。

+0

ありがとう - これは確かに非常に速く、効率的です – EA00

+0

喜んで助けてください。私はまた、古いスクリプトが間違っていたことの内訳を修正しました。これは役に立つかもしれません。 –

関連する問題