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
...
그래서 문제는 결과가 표에 두 번 나타난다는 것입니다. 이는 (제공된 예를 사용하여) "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
다음은 상호 배타적인 두 개의 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와 rint sed
의 두 가지 상태가 있습니다.p
먹다. 첫 번째 상태( p
린트 상태) 에서는 각 입력 행을 sed
자동으로 p
린트한 다음 패턴과 비교하여 확인합니다 / 12 * 31 /
. 현재 패턴 공간이 !
일치 하지 않으면 d
제거되고 sed
다음 입력 줄이 당겨지며 elete 명령 다음에 아무것도 p
실행하지 않고 rint 명령에서 스크립트가 맨 위에서 다시 시작됩니다.d
입력라인이 있을 때하다/ 12 * 31 /
그러나 경기는 sed
대본 후반부에 들어갑니다 -먹다반지 모양. 먼저 :
이라는 분기 레이블을 정의한 n
다음 현재 패턴 공간을 외부 입력 라인으로 덮어쓴 다음 n
현재 패턴 공간을 //
마지막 일치 패턴과 비교합니다. 이전에 일치한 줄이 ext 에 의해 덮어쓰기 되었기 때문에 n
이 첫 번째 반복은먹다루프가 일치하지 않으며 매번 레이블 로 !
역추적 하지 않아 외부 입력 라인을 가져와서 마지막 일치 패턴과 다시 비교합니다.sed
b
:n
n
//
마지막으로 또 다른 일치 항목(나중에 약 365개 추가 줄)을 만들면 n
스크립트가 완료될 때 자동으로 인쇄하지 않고 다음 입력 줄을 가져온 다음 sed
첫 번째 상태 시작의 rint 명령에서-n
맨 위에서 다시 시작합니다. p
따라서 각 루프 상태는 다음 키를 가능한 한 적게 찾으면서 동일한 키의 다음 상태로 점프합니다.
단일 편집 루틴을 호출하지 않고 전체 스크립트를 완료할 수 있으며 단일 정규식만 컴파일하면 됩니다. 결과로 나오는 자동 장치는 매우 간단합니다. 즉, [123 ]
및 만 이해합니다 [^123 ]
. 게다가 비교의 적어도 절반은 컴파일 없이 수행될 것입니다.먹다루프는 단순히 //
빈 루프입니다. 따라서 sed
입력 행당 한 번의 호출로 루프를 완전히 완료할 수 있습니다.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/dates
1970년부터 3338년까지 매년 출력이 두 배로 늘어납니다. 파일은 다음과 같습니다.
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
결과를 얻으려면 다른 두 명령보다 더 많은 실제 작업이 필요합니다. 작업을 완료하기 위해 병렬 작업을 실행하고 있으며 멀티 코어 CPU의 이점을 크게 누리고 있습니다. 그리고 처리되는 동안 할당된 단일 코어에 고정된 상태로 유지됩니다.sed
sort
awk
sed
여기에 있는 결과는 표준 최신 GNU 버전에서 가져온 것이지만 sed
다른 버전을 시도해 보았습니다. 사실, 다른 바이너리로 세 가지 명령을 모두 시도했지만 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