awk: ファイルを列名に分割し、各ファイルにヘッダー行を追加します。

awk: ファイルを列名に分割し、各ファイルにヘッダー行を追加します。

ヘッダー行を含むパイプで区切られたファイルがありますa.txt。最初の列にはファイル名が含まれます。

a.txt最初の列で名前が決まる複数の異なるファイルに分割したいと思います。また、a.txt各ファイルの上部にヘッダー行が繰り返されることを望みます。

だから私は持っていますa.txt

filename|count|age
1.txt|1|15
1.txt|2|14
2.txt|3|1
41.txt|44|1
2.txt|1|3

作りたい1.txt

filename|count|age
1.txt|1|15
1.txt|2|14

そして2.txt

filename|count|age
2.txt|3|1
2.txt|1|3

そして41.txt

filename|count|age
41.txt|44|1

私は基本的な分業を持っています。

awk -F\| '{print>$1}' a.txt

ところで、タイトルを含める方法を把握しようとしています。誰でも助けることができますか?ありがとうございます!

答え1

解決策は、ヘッダーを別の変数に保存し、$1新しい値(= filename)が最初に表示されたときに印刷することです。

awk -F'|' 'FNR==1{hdr=$0;next} {if (!seen[$1]++) print hdr>$1; print>$1}' a.txt 
  • これにより、最初の行全体がa.txt変数に保存されますが、hdrそれ以外はその特定の行が未処理のままになります。
  • すべての後続の行は、最初に$1さまざまな値の発生回数を保持する配列からその値(=希望の出力ファイル名)を検索して、すでに見つかったことを確認します。カウンタの現在の値がまだゼロの場合、表示されたファイルにヘッダを出力し、カウンタをインクリメントして将来のすべてのヘッダ出力を抑制します。残りは自分で見つけました。seen$1$1$1

付録:

入力ファイルが複数あり、ヘッダー行がすべてある場合は、次のように呼び出しにすべてawk引数として渡すことができます。

awk -F'|' ' ... ' a.txt b.txt c.txt ...

ただし、最初のファイルにのみヘッダー行がある場合は、最初のルールでそれを変更する必要がありますFNRNR

警告する

Ed Mortonが指摘したように、この単純なアプローチは、さまざまな出力ファイルの数が少ない場合(最大10個)でのみ機能します。 GNUはawkまだ動作しますが、必要に応じてバックグラウンドでファイルを自動的に閉じて開くため、遅くなります。他のawk実装では、「開いたファイルが多すぎて失敗する」ことがあります。

答え2

これは、awk、sort、およびcutを使用して強力で効率的に機能します。

$ cat tst.sh
#!/usr/bin/env bash

awk 'BEGIN{FS=OFS="|"} {print (NR>1), $1, NR, $0}' "$@" |
sort -t'|' -k1,1n -k2,2 -k3,3n |
cut -d'|' -f4- |
awk '
    BEGIN { FS=OFS="|" }
    NR == 1 { hdr = $0; next }
    $1 != prev {
        close(prev)
        print hdr " > " $1
        prev = $1
    }
    { print $0 " > " $1 }
'

$ ./tst.sh a.txt
filename|count|age > 1.txt
1.txt|1|15 > 1.txt
1.txt|2|14 > 1.txt
filename|count|age > 2.txt
2.txt|3|1 > 2.txt
2.txt|1|3 > 2.txt
filename|count|age > 41.txt
41.txt|44|1 > 41.txt

テストが完了した場合" > "にのみ、実際に出力ファイルを生成するように変更されました。>

先頭 awk|sort|cut はすべての入力行をファイル名 ($1) にグループ化し、最終 awk は一度に 1 つの出力ファイルの内容のみを処理するため、一度に 1 つの出力ファイルのみが開きます。 gawk出力ファイルが12個ある場合、またはgawkを使用して出力ファイルを開いたり閉じたりして実行速度が遅くなると、「オープンファイル名が多すぎます」というエラーが発生しなくなります。

以下は、各初期段階で発生することです。最後のawkスクリプトのデータを設定して、1つの出力ファイルのみを開き、出力ファイル名ごとに元の入力順序を維持しながら解析できるようにします。

$ awk 'BEGIN{FS=OFS="|"} {print (NR>1), $1, NR, $0}' a.txt
0|filename|1|filename|count|age
1|1.txt|2|1.txt|1|15
1|1.txt|3|1.txt|2|14
1|2.txt|4|2.txt|3|1
1|41.txt|5|41.txt|44|1
1|2.txt|6|2.txt|1|3

$ awk 'BEGIN{FS=OFS="|"} {print (NR>1), $1, NR, $0}' a.txt |
    sort -t'|' -k1,1n -k2,2 -k3,3n
0|filename|1|filename|count|age
1|1.txt|2|1.txt|1|15
1|1.txt|3|1.txt|2|14
1|2.txt|4|2.txt|3|1
1|2.txt|6|2.txt|1|3
1|41.txt|5|41.txt|44|1

$ awk 'BEGIN{FS=OFS="|"} {print (NR>1), $1, NR, $0}' a.txt |
    sort -t'|' -k1,1n -k2,2 -k3,3n |
    cut -d'|' -f4-
filename|count|age
1.txt|1|15
1.txt|2|14
2.txt|3|1
2.txt|1|3
41.txt|44|1

関連情報