第8回シェル芸勉強会問題集を解いてみた - 拡張 POSIX シェルスクリプト Advent Calendar 2013 - ダメ出し Blog

2013-12-22(Sun) [sh][shell] [更新履歴]

拡張 POSIX シェルスクリプト Advent Calendar 2013、22日目の記事です。

なるべく POSIX sh で、無理なものは bash, ksh で解きました。 外部コマンドなし縛り。(md5sum は除く)

問題1 数字の並びを 2つずつフリップする

#!/bin/sh
echo 1 2 3 4 5 6 7 8 9 10 |
(
set -- `read l; echo $l`
r=''
while [ $1 ]; do r="$r $2 $1"; shift 2; done
echo $r
)
# 2 1 4 3 6 5 8 7 10 9

問題2 「ユニケージ」「ユニゲージ」「UPS」「USP」の各個数を数える

#!/bin/bash
echo ユニゲージユニケージユニゲージUSP友の会USP友の会 UPS友の会UPS友の会 |
(
read l
ns=
for k in ユニケージ ユニゲージ UPS USP; do
  n=0
  while :; do
    x="${l/$k/}"
    [[ $x = $l ]] && break
    ((n++))
    l="$x"
  done
  ns="$ns $n"
done
echo $ns
)
# 1 2 2 2

問題3 4個のファイルを作り同じ内容のものを探し出すワンライナー

パス。あとでやる。

問題4 数字の列を偶数と奇数に分ける

sh 版。

#!/bin/sh
echo 3 8 2 10 1 8 9 |
(
read l
e=
o=
for n in $l; do
  case $n in *[13579]) o="$o $n";; *) e="$e $n";; esac
done
echo $e $o
)
# 8 2 10 8 3 1 9

bash 版。

#!/bin/bash
echo 3 8 2 10 1 8 9 |
(
read l
e=
o=
for n in $l; do
  ((n%2)) && o="$o $n" || e="$e $n"
done
echo $e $o
)
# 8 2 10 8 3 1 9

問題5 ビット列の 0 と 1 の連続数を数える

#!/bin/sh
echo 000001111111111001010 |
(
read l
r=
[ "${l#0}" = "$l" ] && m=0 || m=1
while :; do
  p=${l%%$m*}
  [ -z "$p" ] && break
  [ $m -eq 0 ] && m=1 || m=0
  r="$r $m"
  [ ${#p} -gt 1 ] && r="$r${#p}"
  l=${l#$p}
done
echo $r
)
# 05 110 02 1 0 1 0

問題6 連続する数列をハイフンでまとめる

以前やったネタだけど、 出力形式が微妙に異なるので調整した。

#!/bin/bash
echo 1 2 3 5 6 8 10 11 12 15 |
(
read l
s=${l%% *}
((p=x=s-1))
for n in $l x; do
  ((n-p-1)) && { 
    ((p-x)) && o=$o-$p
    o="$o $n"
    x=$n
  }
  p=$n
done
o=$s${o#-$s}
echo ${o% x}
)
# 1-3 5-6 8 10-12 15

問題7 数字3桁のパスワードの MD5 ハッシュ値から平文パスワードを求める

#!/bin/bash
echo 250cf8b51c773f3f8dc8b4be867a9a02 |
(
read l
n=0
while [[ $n -lt 1000 ]]; do
  p=$(printf '%03d' $n)
  h=$(echo -n $p |md5sum)
  if [[ ${h%% *} = $l ]]; then
    echo $p
    break
  fi
  ((n++))
done
)
# 456

問題8 /usr/share/dict/wordでしりとり

ksh で適当に実装。 「é」とか含まれているので bash だと動かない。

#!/bin/ksh

typeset -a words
typeset -l word_lower

for letter in {a..z}; do
  eval typeset -a words_by_"$letter"
done

while read -r word; do
  word_lower="$word"
  words+=("$word")
  letter="${word_lower:0:1}"
  eval words_by_$letter+='($word)'
done </usr/share/dict/words

word="${words[$RANDOM % ${#words[@]}]}"
unset words

echo "$word"
while :; do
  word_lower="$word"
  letter="${word_lower:${#word}-1:1}"
  eval words_by_letter_num='${#words_by_'"$letter"'[@]}'
  [[ words_by_letter_num -eq 0 ]] && break
  index=$(($RANDOM % $words_by_letter_num))
  eval word='${words_by_'"$letter"'['"$index"']}'
  eval unset 'words_by_'"$letter"'['"$index"']'
  echo "$word"
done

ふつうにスクリプトになってしまった。何のひねりもない…。


ところで、12月25日はクリスマスな上に、 OSS 界隈で地味に活躍されているふみやすさんの誕生日ですね!
http://www.amazon.co.jp/registry/wishlist/27M7TV8CEEF6G?sort=priority

逆に、あなたの書いた OSS や Blog や Advent Calendar が気に入ったら何か送りたく なってしまうかもしれないので、プロフィールや Web サイトに あなたの Amazon 欲しいものリストの URL を貼っておいてくださいね!


私が勤める OSSTech っていう某弊社で社員募集しているようです。 人材紹介会社を介さなければ、入社後に 20万円のボーナス! 「ふみやすっていう人に紹介された」と言ってもらえると私にもボーナス!! → https://www.osstech.co.jp/recruit/


よろしければ、これまで参加した/参加予定のほかの Advent Calendar もどうぞ。