AWK의 배열에는 코드 설명이 필요합니다.

AWK의 배열에는 코드 설명이 필요합니다.

내 임무는 원본 데이터의 코드 중 하나가 참조 목록에서 읽은 새 코드로 대체되는 목록을 만드는 것입니다. 이 경우 변경 사항은 하나만 있지만 필요한 경우 참조 목록에 더 추가할 수 있습니다.

참조 목록(mycodes)에는 다음 값이 있습니다.

100,100007

데이터는 3자리 코드의 스트림이지만 100에 대한 코드는 나머지 스트림과 함께 5자리 코드로 작성되어야 합니다. 아래와 같이 AWK 프로그램을 사용했습니다.

BEGIN{
FS=","
reffile="mycodes"
while(getline<reffile>0) {ref[$1]=$2}
}
{
val=$1
newval=ref[val]

if (newval in ref) { outval=val}
else               {outval=newval}

print outval
}

입력 데이터 파일에는 다음 값이 포함됩니다.

100
101
120
130
100

프로그램이 실행될 때 올바른 출력을 생성합니다.

100007
101
120
130
10007

그러나 참조 파일의 첫 번째 항목 뒤에 공백이 있는 경우에만 작동합니다. 공백이 없으면 프로그램은 100007 이외의 다른 내용을 출력으로 생성하지 않습니다.

나는 이 AWK 프로그램의 논리에서 정확히 무슨 일이 일어나고 있는지 이해하지 못하며 누군가가 이를 설명하는 데 도움을 줄 수 있는지 궁금합니다. 특히 if (newval in ref).

답변1

getline다음을 사용하여 패딩을 사용 하려면 $0다음 방법을 참조하세요.http://awk.freeshell.org/AllAboutGetline):

while ( (getline < reffile) > 0 ) {
    ref[$1] = $2
}

스크립트의 나머지 부분은 한 줄로만 구성되어야 합니다.

{ print ( $1 in ref ? ref[$1] : $1 ) }

따라서 전체 스크립트는 다음과 같습니다.

BEGIN {
    FS = ","
    reffile = "mycodes"
    while ( (getline < reffile) > 0 ) {
        ref[$1] = $2
    }
}
{ print ( $1 in ref ? ref[$1] : $1 ) }

"mycodes"파일 이름을 보관하기 위해 변수를 생성 하고 이를 매개 변수로 전달하고 싶지 않은 타당한 이유가 있다고 가정합니다 .

아니면 이렇게 할 수도 있습니다:

BEGIN { FS = "," }
NR==FNR { ref[$1] = $2; next }
{ print ( $1 in ref ? ref[$1] : $1 ) }

그리고 전화하세요. 이는 루프를 사용하여 패딩하는 것보다 약간 덜 효율적이지만 awk 'script' mycodes file대부분의 응용 프로그램에서는 문제가 될 가능성이 없으며 분명히 더 깨끗하고 오류가 발생하기 쉬운 코드를 사용합니다.refs[]BEGINgetline

do 는 awk가 현재 레코드를 다시 작성하도록 강제하지 않기 때문에 print ( $1 in ref ? ref[$1] : $1 )do 또는 이와 유사한 것보다 더 효율적이지만 if ($1 in ref) $1=ref; print $1다시 말하지만 말이 되지 않습니다.

문제가 있기는 하지만 기존 스크립트는 설명하는 대로 실패하지 않을 수 있으며 실제 문제는 DOS 줄 끝입니다(참조https://stackoverflow.com/questions/45772525/why-does-my-tool-output-overwrite-itself-and-how-do-i-fix-it?).

답변2

val=$1
newval=ref[val]
if (newval in ref) { outval=val }
else               { outval=newval }

따라서 val기본 입력 파일에서 이러한 값을 읽습니다(예: 100또는 123. ref와 같은 항목이 포함된 쌍 의 경우 키로서의 존재 여부 (즉, 요소가 존재하는지 여부 ) ref[100]=100007를 확인할 수 있습니다 . 키(또는 요소)로 존재하는 경우에는 그렇지 않습니다 .valrefref[val]ref[val]ref[ref[val]]

그러니 그냥 하세요 if (val in ref).

글쎄, 그렇다면하다newval존재하는 경우 거기에서 발견된 값(현재 위치 ) 을 사용할 수 있습니다 .아니요, 이전 값( val). 그러니 이렇게 해라

val=$1
newval=ref[val]
if (val in ref) { outval=newval }
else            { outval=val }

@αГsнιι가 언급한 것 외에도 이제 할당 문제가 있습니다.newval=ref[val] 만들다 ref[val]빈 문자열이 아직 존재하지 않는 경우 값으로 사용하므로 이에 대해 조치를 취해야 합니다.

완전히 제거하고 테스트 후에 만 newval사용하십시오 .ref[val]

val=$1
# newval=ref[val]     # remove this line
if (val in ref) { outval=ref[val] }
else            { outval=val }

또는 거기에 두고 빈 문자열에 대해 테스트합니다.

val=$1
newval=ref[val]
if (newval != "") { outval=newval }
else              { outval=val }

mycodes두 번째 필드에 Null 값이 포함될 수 있으면 둘 사이의 차이가 의미가 있습니다.

답변3

주어진

reffile="mycodes"
while(getline<reffile>0) {ref[$1]=$2}

의 빈 행은 값이 빈 문자열이고 인덱스도 빈 문자열인 mycodes요소를 만듭니다 .ref

그런 다음 입력 스트림을 처리할 때,

newval=ref[val]

newval매번 $1( 100, 101, 120, ) 에는 요소가 비어 있지 않은 유일한 인덱스인 130빈 문자열이 할당됩니다 (인덱스는 (비어 있음) 의 초기 합계 와 이후에 생성된 각 빈 요소의 인덱스 , 즉 , , ) . 이 경우 빈 문자열은 인덱스 중 하나 이므로100ref100""newval=ref[val]101120130ref

if (newval in ref) { outval=val}
else               {outval=newval}

if (newval in ref)성공하고 인쇄된 값은 입력 스트림( val, from ) val=$1의 현재 값 입니다.

반면 ref에 빈 문자열로 인덱싱된 요소가 없으면(빈 줄이 없을 때 발생 mycodes) 빈 문자열은 if (newval in ref)매번 실패합니다. newval그런 다음 newval(빈 줄)이 인쇄됩니다.

두 경우 모두 yes $1이면 100실패 하므로 인쇄 됩니다.newval10007if (newval in ref)newval

파일에 빈 줄이 없더라도 입력 스트림의 빈 줄은 동일한 수수께끼 동작을 유발할 수 있습니다 mycodes.

귀하의 질문에서 하나 이상의 10007, 100007및 "5자리 코드"가 철자가 틀리고 실제로 항상 10007(5자리 코드)를 의미한다고 가정하면 AWK 프로그램을 다음과 같이 다시 작성합니다.

awk -v FS=, '
  NR == FNR {
    ref[$1] = $2
    next
  }
  ($1 in ref) {
    $1 = ref[$1]
  }
  1
' ./mycodes -

또는

awk '
  BEGIN {
    FS=","
    while ( (getline < "mycodes") > 0 )
      ref[$1] = $2
  }
  ($1 in ref) {
    $1 = ref[$1]
  }
  1
' -

(감사해요αГsнim실제로는 인덱스로 newval=ref[val]추가된다는 점을 지적하므로 이는 배열에서 일치하는 인덱스를 검색하는 더 안전한 방법입니다.valref($1 in ref)

답변4

주요 문제는 이 줄에 있습니다 newval=ref[val]. 여기서 변수는 val키 역할을 합니다( $1에서 같음 val=$1). 이는 실제로 를 의미합니다 newval=ref[$1]. 이 줄에서 키가 존재하면 newval변수의 내용이 ref[] 배열의 값으로 설정됩니다. ref[val]그렇지 않으면 빈 값으로 설정됩니다. 즉, newval=ref[val]키가 배열에서 발견되지 않으면 키가 null 값으로 배열에 추가되고 변경을 원하지 않을 때 향후 문제가 발생할 수 있습니다. 배열 크기/인덱스, 또는 파일이 큰 경우 사용 가능한 메모리를 초과하거나 스크립트 속도가 크게 느려질 수 있습니다.

...if-else 문에서 테스트 중이면 newval항상 실행됩니다.else부분.

if (newval in ref) {
        outval = val;    ## this section never runs
} else {
        outval = newval  ## this sections runs for every tests
}

newval=ref[val]따라서 키가 행에 존재하는 한 outval해당 키의 값이 사용되며, 그렇지 않으면 null로 설정됩니다. 에 인쇄하면 print outval해당 키의 값이 ref[] 배열에 있으면 출력되고, 그렇지 않으면 빈 줄이 출력됩니다.

간단한 수정(추가 개선이 필요하지 않음)은 다음과 같이 줄을 변경합니다.

newval = (val in ref)?ref[val]:val

명령은 다음과 같이 작성할 수 있습니다.

$ awk 'BEGIN{ FS="," }
    NR==FNR   { ref[$1]=$2; next }
   ($1 in ref){ $1=ref[$1] }1' reference infile
100007
101
120
130
100007

$ cat reference
100,100007
$ cat infile
100
101
120
130
100

관련 정보