2017-10-25 7 views
2

'$ value1 value2'という形式の「ヘッダー行」を持つブロックごとに、$ 3列の最小値を探したいとします。ファイルは次のようになりますawkを使用して各ブロックの列で最小値を見つけよう

@ 62.65 -50.35 
0 1.50 1.676 
1.67 1.50 1.677 
1.67 2.25 1.423 
2.90 2.25 2.902 
2.90 4.95 2.903 
3.04 4.95 3.049 
@ 63.61 -50.45 
0 1.50 1.654 
3.42 1.50 1.875 
3.43 2.19 3.430 
5.31 2.19 1.032 
5.32 6.23 5.320 
5.43 6.23 5.434 

これ以降、最小の$ 3でヘッダーと行全体を印刷します。 (私が試した

awk '{if (!/^@/) {if ($3 < min) {$3=min;} print $1, $2, $3, min; min = $3;} else if (/^@/) min = 10; print $1 $2 $3;}' input.txt > output.txt 

私はブロックを分離し、分を「リセット」する悩みを持っている:

だから、私の出力ファイルには、これは私がこれまで試したものです

@ 62.65 -50.35 
1.67 2.25 1.423 
@ 63.61 -50.45 
5.31 2.19 1.032 

のようになります。 'start'の値を高く設定する - つまり10)。

私はプログラミングでは新しく、これまで主にawkを使用していました。あなたが私を助けることができれば、それは本当に素晴らしいでしょう!本当にありがとう!乾杯、ISI

+2

に保存されている行を印刷する必要がありますし、それを提供する。 – anubhava

+1

また、これまでの試みを追加してください。それが動作しない場合は大丈夫です、 – ghoti

+0

ようこそ。何を試しましたか? [ヘルプセンター](https://stackoverflow.com/help)にお気軽にお立ち寄りください。 –

答えて

3

使用のawk:

awk '/^@/{if(h){print h RS m}min=""; h=$0; next}min=="" || $3 < min{min=$3; m=$0}END{print h RS m}' infile 

array

awk '/^@/{h=$0;min="";next}min==""||$3<min{min=$3;l[h]=$0}END{for(i in l)print i RS l[i]}' infile 

ベター読み取り可能な使用:

awk '/^@/{ 
      if(h){ 
       print h RS m 
      } 
      min=""; h=$0; next 
     } 
    min=="" || $3 < min{ 
      min=$3; 
      m=$0 
    } 
    END{ 
      print h RS m 
    } 
    ' infile 

array

awk '/^@/{ 
      h=$0;min=""; 
      next 
    } 
    min==""||$3<min{ 
      min=$3; 
      l[h]=$0 
    } 
    END{ 
      for(i in l) 
       print i RS l[i] 
    } 
    ' infile 
0123を使用しての

試験結果:

$ cat infile 
@ 62.65 -50.35 
0 1.50 1.676 
1.67 1.50 1.677 
1.67 2.25 1.423 
2.90 2.25 2.902 
2.90 4.95 2.903 
3.04 4.95 3.049 
@ 63.61 -50.45 
0 1.50 1.654 
3.42 1.50 1.875 
3.43 2.19 3.430 
5.31 2.19 1.032 
5.32 6.23 5.320 
5.43 6.23 5.434 

出力-1(推奨)

$ awk '/^@/{if(h){print h RS m}min=""; h=$0; next}min=="" || $3 < min{min=$3; m=$0}END{print h RS m}' infile 
@ 62.65 -50.35 
1.67 2.25 1.423 
@ 63.61 -50.45 
5.31 2.19 1.032 

出力-2

$ awk '/^@/{h=$0;min="";next}min==""||$3<min{min=$3;l[h]=$0}END{for(i in l)print i RS l[i]}' infile 
@ 62.65 -50.35 
1.67 2.25 1.423 
@ 63.61 -50.45 
5.31 2.19 1.032 
+1

他の答えと同じ発言:連想配列のキーとして$ 0を使用することは議論の余地があります。 –

+0

@RenaudPacalet:私はあなたに同意しますが、現在の状況では、OPにはほとんどデータが示されていないため、上書きしない1つ目の解決策があります。私が仮定したので、 '@'で始まるユニークな行がある可能性があります。 –

1

awkは次のスクリプトごとに、非常に簡単にこれを行うことができます。

awk ' 
    $1=="@" { first=1; key=$0; next } 
    first==1 { lowest=$3; line[key]=$0; first=0; next } 
       { if ($3 < lowest) { lowest=$3; line[key]=$0 } } 
    END  { for (key in line) { printf "%s\n%s\n", key, line[key] } } 
' <<EOF 
@ 62.65 -50.35 
0 1.50 1.676 
1.67 1.50 1.677 
1.67 2.25 1.423 
2.90 2.25 2.902 
2.90 4.95 2.903 
3.04 4.95 3.049 
@ 63.61 -50.45 
0 1.50 1.654 
3.42 1.50 1.875 
3.43 2.19 3.430 
5.31 2.19 1.032 
5.32 6.23 5.320 
5.43 6.23 5.434 
EOF 
要求されたよう

、出力は次のとおり

@ 62.65 -50.35 
1.67 2.25 1.423 
@ 63.61 -50.45 
5.31 2.19 1.032 

明確化のためにコードを破る:

$1=="@" {    # For all header lines: 
    first=1    # Flag that you're starting a new block. 
    key=$0    # Save the key. 
    next     # Go back for next line. 
} 
first==1 {    # For first line in each block: 
    lowest=$3   # It must be lowest. 
    line[key]=$0   # Store line. 
    first=0    # Now processing subsequent lines in block. 
    next     # Go back for next line. 
} 
{      # For non-first-lines-in-block: 
    if ($3 < lowest) { # Only if this one is lower. 
     lowest=$3  # Store value and line. 
     line[key]=$0 
    } 
} 
END {     # At end, simply output associative array. 
    for (key in line) { 
     printf "%s\n%s\n", key, line[key] 
    } 
} 

これはヘッダー行が一意である前提に留意してください。重複があり、それらを区別して扱いたい場合は、NR$0の組み合わせでキーを作成できます。

+0

ああ、すごいよ!どうもありがとうございます!それは本当に大きな助けです。 :) – IsiSa

+1

連想配列のキーとして '$ 0'を使うことは議論の余地があります。 –

+0

@RenaudPacalet: '$ 0'と組み合わせて単に' NR'を使うことができます。これは、行が重複しても各ブロックを一意に識別するためです。私はオプションとしてそれを追加します。 – paxdiablo

1

ここでは全く異なるawkアプローチがあります。

awk 'BEGIN {RS="@"} {s=$3 OFS $4 OFS $5; n=$5; for (i=5;i<=NF;i+=3) {if ($i<n) {s=$(i-2) OFS $(i-1) OFS $i; n=$5} }} n { print "@ " $1 OFS $2 ORS s }' infile 

それとも簡単にコメントのために壊れた:

BEGIN { 
    RS="@"       # "@" as our record separator 
} 

{ 
    s=$3 OFS $4 OFS $5    # store the first line... 
    n=$5 
    for (i=5;i<=NF;i+=3) {   # for each 3rd field on a line, 
    if ($i<n) {     # test its value and 
     s=$(i-2) OFS $(i-1) OFS $i # store a new value if the 
     n=$5      # condition matches 
    } 
    } 
} 

n { 
    print "@ " $1 OFS $2 ORS s  # print records once we have them. 
} 
0

には、次のはまだawkコマンドを使用して別のソリューションです。 Bが定義されている場合、印刷(最初の行のAとBの上で定義されていない)

プリント@

で始まる行ラインの

awk ' 
/^@/{if(b)print a;print $0;next} 
!b||$3<b{b=$3;a=$0} 
END{print a} 
' infile 

/^@/{if(b)print a;print $0;next} 

し、次の行に

を行きます
!b||$3<b{b=$3;a=$0} 

@によって始まる行はbが定義されていない場合whithout各ラインの

場合、または$ 3がbより小さいです、Bで$ 3を保持し、最後に

END{print a} 

でラインを保つために、我々はあなたの予想される出力は何ですか、あなたが質問を編集することができます

+0

これはどのように機能しますか?いくつかの説明は素晴らしいでしょう。 – ghoti

+0

@ghoti私は説明のために私のポストを更新します。 –

関連する問題