2012-07-28

シェル操作課題への回答

http://d.hatena.ne.jp/Yamashiro0217/20120727/1343371036
シェル操作課題ということで bash のみでやってみた。CSVじゃなくてあくまでもカンマ区切りということでクォーティング処理とかは全くなし。ファイル名が与えられてないので、すべてhoge.logとします。

問1 このファイルを表示しろ

$(< file) は個人的には好きではないのだけど、外部コマンドなしだとこれになる。
echo "$(< hoge.log)"

問2 このファイルからサーバー名とアクセス先だけ表示しろ

IFS元に戻すの面倒なのでサブシェル内で。
(IFS=","; while read server t uid dest; do set -- "$server" "$dest"; echo "$*"; done < hoge.log)

問3 このファイルからserver4の行だけ表示しろ

これまた念のためサブシェル内で。
(while read line; do [[ "$line" == server4,* ]] && echo "$line"; done < hoge.log)

問4 このファイルの行数を表示しろ

これもやっぱりサブシェル内で。
(declare -i i; while read line; do let "i++"; done < hoge.log; echo $i)

問5 このファイルをサーバー名、ユーザーIDの昇順で5行だけ表示しろ

うーーーーーーん、ソートを実装するのは面倒だけどしゃーない。ワンライナーは厳しいので適宜改行入りです。それと7bit前提。バブルソートとか書くの久しぶり。
function lt() {
  # return 0 if $1 < $2, else return 1
  local IFS=,
  local -a x y
  x=($1)
  y=($2)
  local -ir server=0
  local -ir uid=2
  [[ "${x[server]}" < "${y[server]}" ]] && return
  [[ "${x[server]}" > "${y[server]}" ]] && return 1
  (( ${x[uid]} < ${y[uid]} )) && return
  return 1
}
IFS=$'\n'
logs=($(< hoge.log))
declare -i i j
for ((i=0; i < ${#logs[@]}-1; i++)); do
  for ((j=1; j < ${#logs[@]}-i; j++)); do
    lt "${logs[j]}" "${logs[j-1]}" || continue
    s=${logs[j-1]}
    logs[j-1]="${logs[j]}"
    logs[j]="$s"
  done
done
for ((i=0; i<5; i++)); do
  echo "${logs[i]}"
done

問6 このファイルには重複行がある。重複行はまとめて数え行数を表示しろ

せっかく前問でソート作ったので、ソートして重複除去でもいいんだけど。。。
declare -a cache
function is_cached() {
  local -i i
  for ((i=0; i < ${#cache[@]}; i++)); do
    [[ "${cache[i]}" == "$1" ]] && return
  done
  return 1
}
declare -i i=0
while read line; do
  is_cached "$line" && continue
  let "i++"
  cache+=("$line")
done < hoge.log
echo $i

問7 このログのUU(ユニークユーザー)数を表示しろ

またユニークあるんだったら、今度は連想配列で。
declare -A uids
IFS=","
while read server t uid dest; do
  uids[${uid}]=1
done < hoge.log
echo ${#uids[@]}

問8 このログのアクセス先ごとにアクセス数を数え上位1つを表示しろ

だんだんbashで書くのが面倒になってきた(汗
declare -iA dests
IFS=","
while read server t uid dest; do
  dests[${dest}]+=1
done < hoge.log
declare -i max=0
for dest in "${!dests[@]}"; do
  ((max >= dests[${dest}])) && continue
  max=${dests[${dest}]}
  d=${dest}
done
echo ${max} "${d}"

問9 このログのserverという文字列をxxxという文字列に変え、サーバー毎のアクセス数を表示しろ

ソートしろとか言われてないのでこれで許してください
declare -iA servers
IFS=","
while read server t uid dest; do
  servers[${server}]+=1
done < hoge.log
for server in "${!servers[@]}"; do
  echo ${servers[$server]} ${server/server/xxx}
done

問10 このログのユーザーIDが10以上の人のユニークなユーザーIDをユーザーIDでソートして表示しろ

なー、なんとまたソートしなきゃじゃん。めんどー。ちゃんとしたソート関数定義しておけばよかったorz
declare -A uid_set
IFS=","
while read server t uid dest; do
  [[ ${uid} -ge 10 ]] && uid_set[${uid}]=${uid}
done < hoge.log
declare -ai uids
uids=("${!uid_set[@]}")
declare -i i j
for ((i=0; i < ${#uids[@]}-1; i++)); do
  for ((j=1; j < ${#uids[@]}-i; j++)); do
    ((uids[j] < uids[j-1])) || continue
    u=${uids[j-1]}
    uids[j-1]="${uids[j]}"
    uids[j]="$u"
  done
done
for ((i=0; i<${#uids[@]}; i++)); do
  echo "${uids[i]}"
done

prometheusのrate()関数の罠

 久しぶりのAdventカレンダー挑戦、うまくいく気がしません。 閑話休題。実のところ、rate()関数というよりは、サーバー側のmetric初期化問題です。 さて、何らかのサーバーAがあったとして、それが更に他のサーバーBにRPCを送っているとします。サーバーBの方でホワイトボ...