各ファイルの最初の数行を省略しながら、3つのファイルの「グループのマージ」をどのように実行できますか?

各ファイルの最初の数行を省略しながら、3つのファイルの「グループのマージ」をどのように実行できますか?

再現可能な例は次のとおりです。

  • file01.txt

    line to skip
    line to skip
    line to skip
    line to keep file 01
    heading 1 in the form: 2017243 01 2017243 01
    data 1 file 01
    heading 2 in the form: 2017243 02 2017243 02
    data 2 file 01
    heading 3 in the form: 2017243 03 2017243 03
    data 3 file 01
    
  • file02.txt

    line to skip
    line to skip
    line to skip
    line to keep file 02
    heading 1 in the form: 2017243 01 2017243 01
    data 1 file 02
    heading 2 in the form: 2017243 02 2017243 02
    data 2 file 02
    heading 3 in the form: 2017243 03 2017243 03
    data 3 file 02
    
  • file03.txt

    line to skip
    line to skip
    line to skip
    line to keep file 03
    heading 1 in the form: 2017243 01 2017243 01
    data 1 file 03
    heading 2 in the form: 2017243 02 2017243 02
    data 2 file 03
    heading 3 in the form: 2017243 03 2017243 03
    data 3 file 03
    
  • 希望の出力

    line to keep file 01
    line to keep file 02
    line to keep file 03
    heading 1 in the form: 2017243 01 2017243 01
    data 1 file 01
    data 1 file 02
    data 1 file 03
    heading 2 in the form: 2017243 02 2017243 02
    data 2 file 01
    data 2 file 02
    data 2 file 03
    heading 3 in the form: 2017243 03 2017243 03
    data 3 file 01
    data 3 file 02
    data 3 file 03
    

これまで、私は次のように各入力ファイルから4行目を抽出する非常に簡単な作業を行いました。

awk 'FNR == 4' *.txt >> out_row4

しかし、残りのファイル処理作業が妨げられ、正常に動作する最終的な解決策を思い出すことができませんでした。

処理するファイルと行数が非常に多いため、ソリューションを非常に一般的な方法で維持する必要があります(ファイルあたり5900行以上)。

参照用の一般的なパターン:

  • 常に各ファイルの最初の3行をスキップしてください。
  • 各ファイルの4行目を維持する
  • タイトル1、2、3(...など)は他のファイルとまったく同じです(したがって、必要な出力ファイルで一度だけ報告する必要があります)。
  • すべてのファイルには同じ数の行が含まれています。
  • ファイルには既知の構造形式がなく、プレーンテキストファイルです。

抽出および再配置する一般的なパターンは次のとおりです。

heading n in the form: 2017243 n 2017243 n
data n file ...

どのようなヒントがありますか?

答え1

アプリケーションDSU慣用語、必須のPOSIXツールawk、ソート、および切り取りのすべてのバージョンを使用します。

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

awk -v OFS='\t' '
    FNR == 1 { fileNr++ }
    FNR >= 4 { print FNR-3, fileNr, $0 }
' "${@:--}" |
sort -n -k1,1 -k2,2 |
awk '($1 % 2) || ($2 == 1)' |
cut -f 3-

$  ./tst.sh file01.txt file02.txt file03.txt
line to keep file 01
line to keep file 02
line to keep file 03
heading 1 in the form: 2017243 01 2017243 01
data 1 file 01
data 1 file 02
data 1 file 03
heading 2 in the form: 2017243 02 2017243 02
data 2 file 01
data 2 file 02
data 2 file 03
heading 3 in the form: 2017243 03 2017243 03
data 3 file 01
data 3 file 02
data 3 file 03

すべての入力を一度に処理する必要がある上記の唯一のツールは、要求sortページングなどを使用して大量の入力を処理するように設計されているため、入力ファイルの数は重要ではありません(ARG_MAXを超えない限り)。もちろん)またはどれだけ大きいか。

または、awkを使用し、入力ファイルが不足しているため、「開いたファイルが多すぎます」というエラーが発生したとします。

$ cat tst.awk
BEGIN {
    while ( ! eof ) {
        for ( fileNr=1; fileNr<ARGC; fileNr++ ) {
            if ( (getline vals[fileNr] < ARGV[fileNr]) <= 0 ) {
                eof = 1
            }
        }
        if ( !eof && (++lineNr >= 4) ) {
            if ( lineNr % 2 ) {
                print vals[1]
            }
            else {
                for ( fileNr=1; fileNr<ARGC; fileNr++ ) {
                    print vals[fileNr]
                }
            }
        }
    }
    exit
}

$ awk -f tst.awk file01.txt file02.txt file03.txt
line to keep file 01
line to keep file 02
line to keep file 03
heading 1 in the form: 2017243 01 2017243 01
data 1 file 01
data 1 file 02
data 1 file 03
heading 2 in the form: 2017243 02 2017243 02
data 2 file 01
data 2 file 02
data 2 file 03
heading 3 in the form: 2017243 03 2017243 03
data 3 file 01
data 3 file 02
data 3 file 03

私はgetlineほとんどの入力ファイルを一度にメモリに読み込むのを避けるために上記に注意を払いました。http://awk.freeshell.org/AllAboutGetline使用時期/方法の詳細をご覧ください。

答え2

上記のパターンを3つのファイルに保存しました。これにより、awkフィルタリングを完了するために必要な出力を取得できます。

for i in {4..15}; do awk "FNR == $i" *.txt | sort -u; done

答え3

awk以外のものを使っても大丈夫なら:

for f in $(ls *.txt) ; do awk 'FNR >=4' $f | egrep "." -n ; done | sort -n | uniq | cut -d: -f2-

成功するだろう

説明する:

  • forループは各ファイルから最初の3行を削除し(awkを使用)、数を数えます(egrep -nとgrepに基づいてすべての文字を使用)。
  • その後、出力は行番号でソートされます。
  • これにより、重複したヘッダー行が削除されます。
  • 最後に行番号が削除されます

修正する:

awkはすでにファイル全体を繰り返していて、出力に行番号を追加する可能性があるため、egrepの使用を削除しました(ファイルを2回読み取ることを防ぎます)。

for f in $(ls *.txt) ; do awk 'FNR >=4 {printf("%s#%s\n", FNR-3, $0)}' $f ; done | sort -n | uniq | cut -d# -f2-

関連情報