사용자가 문자열 형식으로 직원 세부 정보를 입력하고 부서의 평균 급여를 출력에 표시하려는 경우 출력은 입력 문자열에 나타나는 순서와 동일해야 합니다.
예를 들어 입력 문자열은 다음과 같습니다.
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/#/
"