テーブルの再フォーマット

テーブルの再フォーマット

table.txt次のように誤って作成され、結果に重複しているように見えるいくつかのテーブル()があります。

YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1972 1     1   549
1972 1     2   746
...

代わりに、私は以下が欲しい:

YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1972 1     1   549
1972 1     2   746
...

だから問題は、結果が表に2回表示されることです。これは、(提供された例を使用して)「1971」の後に再び「1971」ではなく「1972」年を予想する必要があることを意味します。 sh / bashを使用して重複した結果を削除する方法はありますか?

私のデータは1971年から2099年までで、2000年以降も以下の形式とまったく同じ形式であることに注意してください。

YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
...
2000 1     1   875
2000 1     2   456
...
2099 12    31  321

答え1

以下は、相互に排他的な2つのsedループです。

sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' <<""
YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1972 1     1   549
1972 1     2   746
...
1972 12    31  999
1972 1     1   933
1972 1     2   837
...
1972 12    31  343

YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
...
1971 12    31  685
1972 1     1   549
1972 1     2   746
...
1972 12    31  999

デフォルトでは、rintとrintsedの2つの状態があります。p食べる。最初の状態(pリント状態)では、各入力行をsed自動的にpリントし、パターンと比較して確認します/ 12 * 31 /。現在のパターンスペースが!一致しない場合はd削除され、sed次の入力行が引き出され、 elete コマンドの後に何もp実行せず、rint コマンドでスクリプトが一番上から再起動されます。d

入力ラインがあるときする/ 12 * 31 /しかし、試合はsed台本の後半に入ります -食べるリングの形。最初に:という分岐ラベルを定義しn、現在のパターン空間を外部入力ラインで上書きし、現在のnパターン空間を//最後の一致パターンと比較します。以前に一致した行がextによって上書きされたため、nこの最初の繰り返しは食べるループが一致せず、毎回ラベルに逆!追跡されないため、外部入力ラインを取得して最後の一致パターンと再比較します。sed b:nn//

最後に、別の一致(後で約365行)を作成するnと、スクリプトが完了したときに自動的に印刷せずに次の入力行を取得し、最初のsed状態の開始のrintコマンドで-n一番上から再起動します。pしたがって、各ループ状態は、次のキーをできるだけ少なく見つけながら、同じキーの次の状態にジャンプします。

単一の編集ルーチンを呼び出すことなくスクリプト全体を完了でき、単一の正規表現のみをコンパイルできます。結果として生じる自動装置は非常に簡単です。言い換えれば、[123 ]とだけ理解します[^123 ]。さらに、比較の少なくとも半分はコンパイルなしで実行されます。食べるループは単に//空のループです。したがって、sed入力行ごとに1回の呼び出しでループを完全に完了できます。regexec()sed 可能pリントループと同様の操作を行います。


タイミング


ここにあるさまざまな答えがどのように実行されるのか疑問に思い、自分だけのテーブルを思いつきました。

dash <<""
    d=0 D=31 IFS=: set 1970 1
    while   case  "$*:${d#$D}" in (*[!:]) ;;
            ($(($1^($1%4)|(d=0))):1:)
                     D=29 set $1 2;;
            (*:1:)   D=28 set $1 2;;
            (*[3580]:)
                     D=30 set $1 $(($2+1));;
            (*:)     D=31 set $(($1+!(t<730||(t=0)))) $(($2%12+1))
            esac
    do      printf  '%-6d%-4d%-4d%d\n' "$@" $((d+=1)) $((t+=1))
    done|   head    -n1000054 >/tmp/dates

dash <<<''  6.62s user 6.95s system 166% cpu 8.156 total

これにより、100万以上の行が入力され、/tmp/dates1970年から3338年まで、毎年出力が2倍になります。ファイルは次のようになります。

tail -n1465 </tmp/dates | head; echo; tail </tmp/dates

3336  12  27  728
3336  12  28  729
3336  12  29  730
3336  12  30  731
3336  12  31  732
3337  1   1   1
3337  1   2   2
3337  1   3   3
3337  1   4   4
3337  1   5   5

3338  12  22  721
3338  12  23  722
3338  12  24  723
3338  12  25  724
3338  12  26  725
3338  12  27  726
3338  12  28  727
3338  12  29  728
3338  12  30  729
3338  12  31  730

...とにかく、いくつかあります。

それから別のコマンドを試しました。

for  cmd in "sort -uVk1,3" \
            "sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn'" \
            "awk '"'{u=$1 $2 $3 $4;if (!a[u]++) print;}'\'
do   eval   "time ($cmd|wc -l)" </tmp/dates
done

500027
( sort -uVk1,3 | wc -l; ) \
1.85s user 0.11s system 280% cpu 0.698 total

500027
( sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' | wc -l; ) \
0.64s user 0.09s system 110% cpu 0.659 total

500027
( awk '{u=$1 $2 $3 $4;if (!a[u]++) print;}' | wc -l; ) \
1.46s user 0.15s system 104% cpu 1.536 total

sortそして、両方のコマンドはsed半分以内の時間内に完了しましたawk。これらの結果は一般的です。私はそれらを何度も実行しました。すべてのコマンドが正しい行数を作成しているようです。したがって、おそらくすべてが機能します。

sort各実行の完了時間はかなり良いですが(通常は少し前進します)、sed結果を得るには他の2つのコマンドよりも実際の作業が必要です。タスクを完了するために並列タスクを実行しており、マルチコアCPUの利点を大きく享受しています。そして、処理中に割り当てられた単一のコアに固定されたままになります。sedsortawksed

ここの結果は標準の最新のGNUバージョンから得られたものですが、sed別のバージョンを試してみました。実際、他のバイナリで3つのコマンドをすべて試しましたが、sed実際にはこのコマンドだけがガボツールで動作しました。私は他の人が非標準構文のために作業を始める前にエラーで終了したと思います。

可能な限り標準構文を使用することをお勧めします。ほとんどの場合、よりシンプルで完全で効率的な実装を自由に使用できます。

PATH=/usr/heirloom/bin/posix2001:$PATH; time ...

500027
( sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' | wc -l; ) \
0.31s user 0.12s system 136% cpu 0.318 total

答え2

awkでパイピングしてみてください

awk '!a[$0]++' files.txt > new_files.txt
mv new_files.txt files.txt

これにより、行が一度だけ出力されます。

編集:(varを接続しても問題が解決するかどうかわからない)

awk '{u=$1 $2 $3 $4 ; if ( !a[u]++ ) print ; } ' ...

答え3

$ (head -1 table.txt ; tail -n +2 table.txt | sort -u -V -k1,3)
YEAR MONTH DAY RES
1971 1     1   245
1971 1     2   587
1971 2     1   587
1971 12    31  685
1972 1     1   549
1972 1     2   746
2000 1     1   875
2000 1     2   456
2099 12    31  321

関連情報