シェル操作課題ということで 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でソートして表示しろ
なー、なんとまたソートしなきゃじゃん。めんどー。ちゃんとしたソート関数定義しておけばよかったorzdeclare -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
1 件のコメント:
問 5 の別解 -- http://codepad.org/GTfbJhGK
挿入法を使えば一度に全部を読む必要がないから read が使えていいのでは,と思って書いてみたのですが,たいした差はありませんでした.時間/空間効率はよくなりましたが.
コメントを投稿