일반 awk - 배열 인덱스를 쉽게 정렬하여 선택한 순서대로 출력합니다.

일반 awk - 배열 인덱스를 쉽게 정렬하여 선택한 순서대로 출력합니다.

[편집: 명확히 하려면갑자기솔루션 및 수정 모호한 "값" 대신 "인덱스"를 정렬해야 합니다(또는 정렬된 방식으로 출력).]

awk에서는 종종 숫자를 세거나 값 세트를 배열에 저장하고 해당 값을 인덱스로 사용합니다(awk의 indexes_are_hashes 메커니즘 사용).

예를 들어: 내가 만난 $2 값의 수와 각 값의 발생 빈도를 알고 싶다면 다음을 수행하세요.

awk '
   ... several different treatments ...
   { count[$2]++ } 
   ... other treatments ...
   END { for(str in count) { 
           print "counted: " str " : " count[str] " times." 
           ... and other lines underneath, with additional infos ...
          }
       }
 '

문제는 (GNU가 아니거나 더 나은) 일반 awk (및 일반 nawk)입니다.

  • [A]는 '만난' 순서대로 다른 값을 출력하지 않으며,
  • [B] 숫자나 알파벳순으로 색인을 탐색하는 쉬운 방법도 제공하지 않습니다.

[A]의 경우: 그다지 어렵지 않습니다. "새로 본" 항목을 색인화하는 또 다른 배열입니다.

문제는 [B]에 관한 것입니다.간단한 호출로 다른 인덱스에 대한 표시 순서를 어떻게 바꿀 수 있습니까?

(참고: 나는 gnu awk가 [B]에 대해 "쉬운" 방법을 가지고 있다는 것을 알고 있습니다:https://www.gnu.org/software/gawk/manual/html_node/Controlling-Array-Traversal.html...하지만 일반 awk/nawk에서 비슷한 작업을 수행할 수 있는 방법을 원합니다! )

(예: 표시된 다른 인덱스를 출력하고 정렬하고 [이전 awk에서...] "무언가"로 다시 읽어야 합니다(예: 다른 배열이 주문된_seen?). 그리고 이를 사용하여 이를 표시해야 합니다. 당신이 보는 것은 당신이 선택한 순서대로 이루어져야 합니다.내부 awk각 색인 아래에 추가 정보를 출력해야 하는 경우가 종종 있습니다. awk 외부의 "정렬"은 모든 것을 재정렬합니다)

지금까지 나는 이 작업을 수행하는 "공리적" 단선(또는 n선?) 방법을 찾지 못했습니다.

나는 정렬을 통해 각 값을 파일로 출력하는 데 몇 줄이 걸리는 패치워크로 끝났습니다. 그런 다음 정렬된 파일을 다시 읽고 각 줄을 sorted_countindexes[n++]에 순서대로 삽입한 다음 for(i =0;i <= n;i++){ ...출력 개수[sorted_countindexes[n]]... }

일반 awk(또는 nawk)의 경우 정렬된 출력을 기반으로 하는 더 나은/더 단순/더 "공리적인" 인덱스를 환영합니다.

MCVE: 다음은 간단한 예입니다. 인덱스를 알파벳순으로 출력하는 것이 좋을 것입니다.

# create the 2 basic files to be parsed by the awk:
printf 'a b a a a c c d e s s s s e f s a e r r f\ng f r e d e z z c s d r\n' >fileA
printf 's f g r e d f g e z s d v f e z a d d g r f e a\ns d f e r\n'>fileB
# and the awk loop: It outputs in 'whatever order', I want in 'alphabetical order'
for f in file? ; do printf 'for file: %s: ' "$f"
  tr ' ' '\n' < "$f" | awk ' 
       { count[$0]++ } 
   END { for(str in count){ 
           printf("%s:%d ",str,count[str]) 
          }; print "" 
       } '
done
#this outputs:
for file: fileA: d:3 e:5 f:3 g:1 r:4 s:6 z:2 a:5 b:1 c:3
for file: fileB: d:5 e:5 f:5 g:3 r:3 s:3 v:1 z:2 a:2
# I'd like to have the letters outputted in alphabetical order instead!

답변1

GNU를 사용하면 "코프로세싱" 기능( )을 사용하여 양방향으로 상호 작용할 수 있고 awk, 정렬할 데이터를 보내고 gawk를 사용하여 결과를 얻을 수 있지만 이는 gawk에만 해당됩니다.sortinfo gawk coprocprint |& "sort""sort" |& getline

배열을 통한 루프만남의 순서, 녹음할 수 있습니다만남의 순서배열을 채울 때:

awk '
  !seen[$1]++ {sequence[n++] = $1}
  END {
    for (i = 0; i < n; i++)
      print sequence[i], seen[sequence[i]]
  }'

NET에서 정렬 알고리즘을 구현할 수도 있습니다 awk. 빌릴 수도 있고 gawk, quicksort.awk빌릴 수도 있어요매뉴얼에서 찾아보세요(여기에서는 비교 루틴에 대한 리터럴 호출로 대체할 수 있는 또 다른 GNU 특정 기능인 간접 함수 호출을 보여줍니다.) 그것은 다음과 같습니다:

awk '
  function less_than(left, right) {
    return "" left <= "" right
  }
  function quicksort(data, left, right,   i, last)
  {
    if (left >= right)
      return

    quicksort_swap(data, left, int((left + right) / 2))
    last = left
    for (i = left + 1; i <= right; i++)
      if (less_than(data[i], data[left]))
        quicksort_swap(data, ++last, i)
    quicksort_swap(data, left, last)
    quicksort(data, left, last - 1)
    quicksort(data, last + 1, right)
  }
  function quicksort_swap(data, i, j,   temp)
  {
    temp = data[i]
    data[i] = data[j]
    data[j] = temp
  }

  {seen[$1]++}
  END {
    for (i in seen) keys[n++]=i
    quicksort(keys, 0, n-1)
    for (i = 0; i < n; i++)
      print keys[i], seen[keys[i]]
  }'

perl개인적으로 저는 여기서 대신 사용합니다 awk.

답변2

$ cat tst.awk
{ cnt[$0]++ }
END {
    n = sort(cnt,idxs)
    for (i=1; i<=n; i++) {
        idx = idxs[i]
        printf "%s:%d%s", idx, cnt[idx], (i<n ? OFS : ORS)
    }

}

function sort(arr, idxs, args,      i, str, cmd) {
    for (i in arr) {
        gsub(/\047/, "\047\\\047\047", i)
        str = str i ORS
    }

    cmd = "printf \047%s\047 \047" str "\047 |sort " args

    i = 0
    while ( (cmd | getline idx) > 0 ) {
        idxs[++i] = idx
    }

    close(cmd)

    return i
}

# create the 2 basic files to be parsed by the awk:
printf 'a b a a a c c d e s s s s e f s a e r r f\ng f r e d e z z c s d r\n' >fileA
printf 's f g r e d f g e z s d v f e z a d d g r f e a\ns d f e r\n'>fileB

for f in fileA fileB ; do
    printf 'for file: %s: ' "$f"
    tr ' ' '\n' < "$f" |
    awk -f tst.awk
done
for file: fileA: a:5 b:1 c:3 d:3 e:5 f:3 g:1 r:4 s:6 z:2
for file: fileB: a:2 d:5 e:5 f:5 g:3 r:3 s:3 v:1 z:2

위의 내용은 단순히 배열 인덱스에서 개행으로 구분된 문자열을 작성하고(적절하게 인용하기 위해 sh) 해당 문자열을 로 파이프하는 쉘 스크립트를 생성한 sort다음 출력을 반복합니다. s의 동작을 수정하려면 sort함수 호출에 Unix sort인수 문자열을 추가하면 됩니다 sort(예: ) sort(seen,"-fu"). sort()반환 시 반복되는 인덱스 배열을 채우는 대신(원하는 경우) 인쇄하거나 함수 내에서 원하는 다른 작업을 수행하도록 수정될 수 있지만 함수는 응집력이 있습니다.

그러나 시스템의 최대 명령줄 길이로 제한됩니다.

\047코드의 s는 쉘이 -구분된 문자열이나 스크립트 '에 포함될 수 없음 을 의미합니다. 따라서 위에서 했던 것처럼 파일에서 읽는 awk 스크립트에서 직접 사용할 수 있지만, 다른 곳에서 사용하려는 경우 명령줄 대체할 항목이 필요 하고 스크립트가 명령줄과 파일 모두에서 해석될 때 작동하기 때문에 -replacement에 대한 가장 이식성이 뛰어난 옵션 입니다.''awk 'script' file'\047'

s '( \047s) str는 문자열이 파이프를 통해 정렬될 때 쉘이 변수를 확장하지 않고 따옴표가 일치하지 않는지 확인하기 위해 존재합니다. 즉, 다음을 수행합니다.

$ echo 'foo'\''bar $(ls) $HOME' | awk '{
    str=$0; gsub(/\047/, "\047\\\047\047", str); print "str="str
    cmd="printf \047%s\047 \047" str "\047"; print "cmd="cmd
}'
str=foo'\''bar $(ls) $HOME
cmd=printf '%s' 'foo'\''bar $(ls) $HOME'

따라서 우리는 깨지기 쉽고 버그가 있는 이와 같은 것을 얻지 못하고 대신 다음과 같은 결과를 얻습니다.

$ echo 'foo'\''bar $(ls) $HOME' | awk '{
    str=$0; print "str="str
    cmd="printf \"%s\" \"" str "\""; print "cmd="cmd
}'
str=foo'bar $(ls) $HOME
cmd=printf "%s" "foo'bar $(ls) $HOME"

관련 정보