awk '! a [$ 0] ++'는 어떻게 작동합니까?


39

이 단일 라이너는 사전 정렬없이 텍스트 입력에서 중복 라인을 제거합니다.

예를 들면 다음과 같습니다.

$ cat >f
q
w
e
w
r
$ awk '!a[$0]++' <f
q
w
e
r
$ 

인터넷에서 찾은 원래 코드는 다음과 같습니다.

awk '!_[$0]++'

_Perl과 같이 awk에서 특별한 의미를 갖기 위해 나에게 이것은 더 당혹 스럽지만 배열의 이름 일뿐이었습니다.

이제는 하나의 라이너 뒤에있는 논리를 이해합니다. 각 입력 줄은 해시 배열의 키로 사용되므로 완료되면 해시는 도착 순서대로 고유 한 줄을 포함합니다.

내가 배우고 싶은 것은이 표기법이 awk에 의해 어떻게 정확하게 해석되는지입니다. 예를 들어 뱅 기호 ( !)의 의미와이 코드 스 니펫의 다른 요소입니다.

어떻게 작동합니까?


제목이 오해의 소지가 있으므로 $ o (o)가 아니라 $ 0 (영)이어야합니다.
Archemar

2
해시이므로 순서가 맞지 않기 때문에 "도착 순서대로"는 실제로 정확하지 않습니다.
Kevin

답변:


35

보자

 !a[$0]++

먼저

 a[$0]

우리는 a[$0]( a전체 입력 라인 ( $0)을 키로 사용 하여 배열) 의 값을 봅니다 .

존재하지 않는 경우 ( !테스트에서 부정은 참으로 평가됩니다)

 !a[$0]

입력 줄을 인쇄합니다 $0(기본 동작).

또한에 ( ++)을 추가 a[$0]하여 다음 !a[$0]에 false로 평가합니다.

좋아, 찾아라! 코드 골프를 봐야합니다!


1
따라서 본질은 다음과 같습니다. 작은 따옴표로 묶은 표현식은 awk각 입력 행에 대한 테스트로 사용됩니다 . 테스트가 성공할 때마다 awk중괄호로 작업을 실행합니다 {print}. 생략하면입니다 . 감사!
Alexander Shcheblikin

3
@ Archemar :이 대답은 잘못되었습니다.
cuonglm

@AlexanderShcheblikin in awk기본 조치는 {print $0}입니다. 이것은 true로 평가 된 모든 것이 이것을 기본값으로 실행한다는 것을 의미합니다. 예를 들어 awk '1' file모든 줄을 awk '$1' file인쇄하고 첫 번째 필드가 비어 있거나 0이 아닌 모든 줄을 인쇄합니다.
fedorqui

6
@Gnouc이 답변에는 심각한 오류가 표시되지 않습니다. 그것이 당신이 말하는 것이라면, 증분은 실제로 표현식의 값이 계산 된 후에 적용됩니다. 인쇄 전에 증분이 발생한다는 것은 사실이지만 기본 설명에 영향을 미치지 않는 작은 부정확성입니다.
Gilles 'SO- 악마 그만해

1
나는 quora에서 초보자가 이해할 수있는 가장 좋은 설명을 찾았습니다 : qr.ae/TUIVxM
GP92

29

처리 과정은 다음과 같습니다.

  • a[$0]: $0연관 배열에서 키 값을보십시오 a. 존재하지 않는 경우 작성하십시오.

  • a[$0]++:의 값을 늘리고 a[$0]이전 값을 expression의 값으로 반환합니다. 경우 a[$0], 존재 반환하지 않습니다 0및 증가 a[$0]1( ++운영자 반환 숫자 값).

  • !a[$0]++: 표현의 가치를 부정합니다. 경우 a[$0]++반환 0, 전체 표현식이 true로 평가, 메이크업은 awk기본 작업을 수행 한 print $0. 그렇지 않으면 전체 표현식이 false로 평가되어 awk아무 것도 수행하지 않습니다.

참고 문헌 :

함께 gawk, 우리는 사용할 수 있습니다 (또는 dgawk awk --debug최신 버전) 디버그하는 gawk스크립트를. 먼저 다음과 같은 gawk스크립트를 작성하십시오 test.awk.

BEGIN {                                                                         
    a = 0;                                                                      
    !a++;                                                                       
}

그런 다음 다음을 실행하십시오.

dgawk -f test.awk

또는:

gawk --debug -f test.awk

디버거 콘솔에서 :

$ dgawk -f test.awk
dgawk> trace on
dgawk> watch a
Watchpoint 1: a
dgawk> run
Starting program: 
[     1:0x7fe59154cfe0] Op_rule             : [in_rule = BEGIN] [source_file = test.awk]
[     2:0x7fe59154bf80] Op_push_i           : 0 [PERM|NUMCUR|NUMBER]
[     2:0x7fe59154bf20] Op_store_var        : a [do_reference = FALSE]
[     3:0x7fe59154bf60] Op_push_lhs         : a [do_reference = TRUE]
Stopping in BEGIN ...
Watchpoint 1: a
  Old value: untyped variable
  New value: 0
main() at `test.awk':3
3           !a++;
dgawk> step
[     3:0x7fe59154bfc0] Op_postincrement    : 
[     3:0x7fe59154bf40] Op_not              : 
Watchpoint 1: a
  Old value: 0
  New value: 1
main() at `test.awk':3
3           !a++;
dgawk>

당신은 Op_postincrement전에 실행 된 것을 볼 수 있습니다 Op_not.

다음을 사용 si하거나 stepi대신 s또는 step보다 명확하게 볼 수도 있습니다 .

dgawk> si
[     3:0x7ff061ac1fc0] Op_postincrement    : 
3           !a++;
dgawk> si
[     3:0x7ff061ac1f40] Op_not              : 
Watchpoint 1: a
  Old value: 0
  New value: 1
main() at `test.awk':3
3           !a++;

3
@Archemar : 귀하의 답변은 !이전에 적용 되었음을 나타냅니다 ++.
cuonglm

6
이 답변은 잘못되었습니다. !연산자 의 결과 가 계산 된 후에 증분이 발생합니다 . 연산자 우선 순위 ( !a[$0]++와 같이 구문 분석 됨 !(a[$0]++))를 평가 순서와 혼동 하고 있습니다 (새 값의 할당은 a[$0]표현식 값이 계산 된 후 발생 함).
Gilles 'SO- 악마 그만해

5
@Gnouc 그것은 당신이 인용 한 구절에서 올바르게 말하고, 설명 된 방식으로 작동하면이 코드는 원하는 효과를 얻지 못할 것입니다. 먼저 값 !x이 계산됩니다. 여기서 x이전 값은 a[$0]입니다. 그런 다음 a[$0]로 설정되어 1+x있습니다.
Gilles 'SO- 악마 그만해

7
awk의 기능에 대한 귀하의 분석이 정확하다고 생각합니다. 어제 달리 암시하면 죄송합니다. 그러나 Archemar의 답변에 대한 귀하의 비판은 잘못되었습니다. Archemar는 우선 순위를 오해하지 않으며 우선 순위를 평가 순서와 혼동합니다 (이전 의견 참조). Archemar의 답변에 대한 언급을 제거하면 답변이 정확해야합니다. 그대로 Archemar를 잘못 증명하는 데 중점을 두지 만 이는 사실이 아닙니다.
Gilles 'SO- 악마 그만해

5
글쎄, 적어도 지금은 awk의 디버거에 대해 알고 있습니다 ...
Archemar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.