awk を使用して新しい行を印刷する

awk を使用して新しい行を印刷する

特定の行を取り出し、取り出したデータをスプレッドシートに入力する必要があるファイルが多数あります。ファイルの例は次のようになります:

Name: w

Age: x

Height: y

Weight: z

年齢、身長、体重だけが欲しいので、まずは以下を実行しました。

grep -E 'Age|Height|Weight' [input file] > output.txt

ファイルの数が多いため、出力は次のようになります。

Age 1
 
Height 1

Weight 1

Age 2

Height 2

Weight 2

etc...

私が今やりたいのは、awk スクリプトを実行して、新しい output.txt ファイルを調べ、まず「Age」という単語を含むすべての行を見つけてそれを印刷することです。すべての「Age」の行を印刷したら、身長と体重を計算します。私は次のスクリプトを実行しました:

awk -F"\t" '/Age/ {print} /Height/ {print}' output.txt >output2.txt

しかし、元の出力ファイルと同じように印刷するだけです。年齢に関するものをすべて実行した後、身長に関するものを検索するように変更するにはどうすればよいでしょうか?

編集:

私が望む出力は、ファイルが

1歳

2歳

高さ1

高さ2

重量1

重量2

等..

明確にするために言うと、Age 1 はファイル 1 の「age」が含まれる行です。

答え1

awkはデフォルトではファイルを1回だけ実行し、すべてのブロックを順番に実行します。そのため、このような出力が得られます。配列ファイルを一度だけ処理しながら、行を保存するには、次のようにします。

BEGIN {
    AgeIndex = 1
    HeightIndex = 1
}
/Age/ {
    ages[AgeIndex] = $0
    AgeIndex+=1
}
/Height/ {
    heights[HeightIndex] = $0
    HeightIndex+=1
}
END {
    for (x = 1; x < AgeIndex; x++)
        print ages[x] "\n"
    for (x = 1; x < HeightIndex; x++)
        print heights[x] "\n"
}

これを次のように保存してfilter.awk実行します。

awk -f filter.awk output.txt > output2.txt

必要な出力を得るには:

$ awk -f filter.awk < data
Age 1

Age 2

Height 1

Height 2

ここで行っているのは、2 つの配列を作成しagesheightsそれぞれの一致する行をその配列に保存することです。は、AgeIndex配列のどこまで進んだかを保持します。最後に、保存したすべての行 (および必要に応じて追加の改行) を出力します。最初にすべての年齢、次にすべての身長です。

配列は最終的にファイル全体をメモリに保持するため、ファイルが特に大きい場合は、ファイル全体を複数回処理する時間とメモリ使用量をトレードオフする必要があります。この時点では、他の言語のプログラムと基本的に同じです。awk を使用する特別な理由がない場合は、他の言語を使用することをお勧めします。正直なところ、awk ではここではあまりメリットがありません。

答え2

gawk

$ awk -F"\t" '
    { a[$1]++ }
    END {
        n = asorti(a,b);
        for (i = 1; i <= n; i++) {
            print b[i];
            if (i%2 == 0) {
                printf "\n";
            }
        }
    }
' output.txt
Age 1
Age 2

Height 1
Height 2

Weight 1
Weight 2

答え3

空白行は実際のファイルの一部ではないか、少なくとも気にしていないと想定します。その場合、必要なのは以下のとおりですsort

$ cat output.txt
Age 1
Height 1
Weight 1
Age 2
Height 2
Weight 2

$ sort output.txt
Age 1
Age 2
Height 1
Height 2
Weight 1
Weight 2

ただし、ファイルが大きすぎてメモリに保持できない場合を除いて、すべてを 1 つのステップで実行する方が簡単な場合があります。

grep -whE 'Age|Height|Weight' *txt | sort > outfile

上記は、現在のディレクトリ ( )で名前が で終わるすべてのファイルで、AgeまたはHeightまたはを検索します。 は「単語全体のみ一致」を意味します (つまり、たとえばは一致しません) 。 が必要なのは、 がないと、複数の入力ファイルが指定された場合に、一致する行とともにファイル名が印刷されるためです。 は拡張正規表現を有効にし、OR を実現します。Weighttxt*txt-wAgeAgeing-h-E|

注記: 何らかの理由で、各エントリの間に余分な空白行が必要な場合(grepコマンドでは生成されません)、次のように追加できます。

grep -whE 'Age|Height|Weight' *txt | sort | sed 's/$/\n/'

$ for i in {1..3}; do echo -e "Name $i\nAge $i\nHeight $i\nWeight $i" > $i.txt; done
$ for f in *txt; do echo " -- $f --"; cat $f; done
 -- 1.txt --
Name 1
Age 1
Height 1
Weight 1
 -- 2.txt --
Name 2
Age 2
Height 2
Weight 2
 -- 3.txt --
Name 3
Age 3
Height 3
Weight 3

$ grep -whE 'Age|Height|Weight' *txt | sort
Age 1
Age 2
Age 3
Height 1
Height 2
Height 3
Weight 1
Weight 2
Weight 3

いずれにせよ、sortあなたにとっては不十分だとしても、私は Perl で次のようなことをしますawk(これは余分な空白行が必要だと仮定していますが、おそらく必要ないでしょう)。

$ perl -ane '$k{$F[0]}.=$_."\n" if /./; 
    END{print $k{$_},"\n" for sort keys (%k)}' output.txt 
Age 1

Age 2


Height 1

Height 2


Weight 1

Weight 2


 

head -n -2最後の 2 行の空白が不要な場合は、それを通過させて削除することができます。

答え4

pythonこの問題の解決策:

from collections import defaultdict
f = open("output.txt", "r")
d = defaultdict(list)
for line in f:
   line = line.strip()
   if line != '':
     arr = line.split(" ")
     d[arr[0]].append(arr[1])
print d.items()

最初の列を使用してハッシュ化し、リストに入れました。

関連情報