
ユーザーが文字列形式で従業員の詳細を入力し、部門の平均給与を出力に表示する場合は、出力は入力文字列に表示される順序と同じでなければなりません。
たとえば、入力文字列は次のようになります。
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
区切り記号を制御します。
上記のアプローチを実装するもう1つの方法は、より意味のある変数名を使用することです(@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/#/
"