입력 문자열을 기반으로 부서의 평균 급여를 계산합니다.

입력 문자열을 기반으로 부서의 평균 급여를 계산합니다.

사용자가 문자열 형식으로 직원 세부 정보를 입력하고 부서의 평균 급여를 출력에 표시하려는 경우 출력은 입력 문자열에 나타나는 순서와 동일해야 합니다.

예를 들어 입력 문자열은 다음과 같습니다.

EMP101:Jack:HR:6000#EMP102:Jill:Management:10000#EMP103:Russell:Testing:10000#EMP104:Monica:HR:15000#EMP105:John:Management:25000#EMP106:Ram:Testing:8000#EMP107:Tan:HR:15000#EMP108:Harry:Management:10000

출력 문자열:

HR:12000#Management:15000#Testing:9000

다음 코드를 시도했습니다.

echo "EMP101:Jack:HR:6000#EMP102:Jill:Management:10000#EMP103:Russell:Testing:10000#EMP104:Monica:HR:15000#EMP105:John:Management:25000#EMP106:Ram:Testing:8000#EMP107:Tan:HR:15000#EMP108:Harry:Management:10000" \
| awk 'BEGIN{RS="#"; OFS=FS=":"} {gsub(/"\n$/,"",$5); print $3,$4}' \
| awk -F ":" '{a[$1] += $2} {b[$1] += 1} END{for (i in a) print i, a[i]/b[i]}' \
| tr " " ":" \
| tr "\n" "#";

결과 출력은 다음과 같습니다.

:0#Testing:9000#Management:15000#HR:12000

이것은 내가 원하는 것도 아니고 왜 :0#문자열 시작 부분에 추가되는지 이해가 되지 않습니다. 누구든지 쉘 스크립트에서 이것을 달성하는 방법을 말해 줄 수 있습니까?

답변1

any를 사용 awk하고 순서를 유지하십시오.

awk 'BEGIN{ RS="#"; FS=OFS=":" }
   { ($3 in dept)?"":ordrDept[++i]=$3; dept[$3]+=$4; seenDept[$3]++; }
END{ for(o=1; o<=i; o++)
         printf "%s%d%s", ordrDept[o] OFS, 
                          dept[ordrDept[o]]/seenDept[ordrDept[o]], 
                          (i==o)?ORS:RS
}' infile

산출:

HR:12000#Management:15000#Testing:9000

우리는 사용했었다ordrDept배열은 각 부서의 순서를 본 순서대로 기억합니다.
우리는 사용했었다dept 각 부서의 총 급여를 요약한 배열입니다.
우리는 사용했었다seenDept그들이 본 횟수를 기억하는 배열입니다.

END{...}블록 에는 i우리가 방문한 기존 부서의 최대 개수가 있으며 ordrDept[++i]=$3, 먼저 방문한 순서대로 인쇄한 ordrDept[o]다음 각 부서의 평균 급여를 계산 total/count하고 (i==o)?ORS:RS구분 기호를 제어합니다.


위 접근 방식을 구현하는 또 다른 방법은 보다 의미 있는 변수 이름을 사용하는 것입니다(@EdMorton에게 감사드립니다).

BEGIN {
    RS = "#"
    FS = OFS = ":"
}
!($3 in depts2sals) {
    depts[++numDepts] = $3
}
{
    depts2sals[$3] += $4
    depts2cnts[$3]++
}
END {
    for (deptNr=1; deptNr<=numDepts; deptNr++) {
        dept = depts[deptNr]
        sal = depts2sals[dept]
        cnt = depts2cnts[dept]
        avg = sal / cnt
        printf "%s%s%s", dept OFS, avg, (deptNr<numDepts ? RS : ORS)
    }
}

답변2

테마의 또 다른 변형

echo 'EMP101:Jack:HR:6000#EMP102:Jill:Management:10000#EMP103:Russell:Testing:10000#EMP104:Monica:HR:15000#EMP105:John:Management:25000#EMP106:Ram:Testing:8000#EMP107:Tan:HR:15000#EMP108:Harry:Management:10000' |
    awk -F'#' '
        # Split into lines at "#"
        {
            for(f=1; f<=NF; f++) {
                print $f;
            }
        }
    ' |
    awk -F: '
        # Record department if unknown. Keep running total and count
        {
            if (!(sum[$3]+0)) { pos[idx++]=$3 };
            sum[$3]+=$4;
            count[$3]++;
        }
        # Output department in order of recording, and calculate average
        END {
            for (idx in pos) {
                dept=pos[idx];
                printf "#%s:%d", dept, sum[dept]/count[dept];
            };
            printf "\n";
        }
    ' |
    cut -c2-

산출

HR:12000#Management:15000#Testing:9000

답변3

이는 :0#입력/파일에 추가 후행 공백 줄이 있기 때문입니다. gsub(...)존재하지 않는 필드에 잘못된 스키마를 제공했기 때문에 이 콘텐츠를 삭제하려는 시도가 실패했습니다 . 이를 수정하거나 확장 RS변수를 사용하십시오. 그 자체로 매우 강력하기 때문에 awk많은 파이프를 실행할 필요가 없습니다. 단지 한 가지만 하면 됩니다 awk.

노력하다

awk 'BEGIN{ORS="#"; RS="[#\n]"; OFS=FS=":"} {SUM[$3]+=$4;CNT[$3]++} END{for (s in SUM) print s, SUM[s]/CNT[s]} ' file3
Management:15000#Testing:9000#HR:12000#

후행 ORS를 제거하는 것은 독자의 연습 과제로 남겨집니다.

답변4

사용하는 방법 중 하나입니다진주.

echo 'your-string' |
perl -lnse 'while ( /:([^:#]+):(\d+)(?=#|$)/g )
{
  my @A = ($2,1);
  exists $s{$1} or $D[@D]=$1;
  $_->{$1} += shift(@A) for \(%s,%k);
}
print map { join(":", $_, $s{$_}/$k{$_}) } @D;
' -- -,=\#

관련된 데이터 구조:

  • 부서별로 키가 지정된 해시 %s에는 같은 부서 직원의 해당 총 급여가 포함됩니다.
  • 부서별 해시 %k는 해당 부서의 직원 수를 보유합니다.
  • @D 배열에는 중복 없이 부서가 표시된 순서대로 포함됩니다.

시도에서 이를 얻는 방법은 다음과 같습니다.

echo 'your_string' |
tr '#' '\n' |
awk -F: '{print $3, NR, $4}' |
sort -k1 -k2n |
awk '
prev != $1 {
  if (NR > 1) print prev, sum/knt
  knt=sum=0;prev=$1
}
{ knt++; sum += $3 }
END { ORS=RS; print prev, sum/knt }
' OFS=: ORS=\#

GNU sed 메소드가 제공됩니다. 같은 부서를 모아 같은 부서 내에서 급여를 나누자는 취지다.

## dc code to compute average of a list of numbers
avg='[+z1<a]sa\3zsnlaxln/f'

echo 'your_string' |
sed -Ee '
  y/#/\n/
  s/^([^:]*:){2}//Mg;ta
  :a
  s/((^|\n)([^:]+):([0-9]+( [0-9]+)?))(\n(.*\n)?)\3:([0-9]+)(\n|$)/\1 \8\6/
  ta
  :b'"
  s#^((.*\n)?[^:]+:)([0-9]+( [0-9]+)+)((\n.*)?)#printf %s '\\1' \"\$(echo '$avg'|dc)\" '\\5'#e
  tb
  y/\n/#/
"

관련 정보