再現可能な例は次のとおりです。
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-