99bottles.sh 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #!/bin/bash
  2. #
  3. # Bottles of Beer song in Bash
  4. #
  5. # A way-overcomplicated implementation of the bottles of beer song in bash.
  6. # Optional argument:
  7. #
  8. # * `--careless` - Generate the "happen to fall" version
  9. #
  10. # NOTE: This is an altered version of a "99 bottles of beer" song
  11. # Bash script by Bill Brown. The intent of the altered version is
  12. # to show the style, so the above text is preserved from [the
  13. # original][99b].
  14. #
  15. # [99b]: http://99-bottles-of-beer.net/language-bash-1831.html
  16. #
  17. standardBreakLine="Take one down and pass it around"
  18. wastefulBreakLine="If one of those bottles should happen to fall"
  19. breakLine=$([ "$1" = --careless ] \
  20. && echo $wastefulBreakLine \
  21. || echo $standardBreakLine)
  22. pred() {
  23. #
  24. # Get predecessor to a number $1
  25. #
  26. case $1 in
  27. *nine) echo ${1%nine}eight;;
  28. *eight) echo ${1%eight}seven;;
  29. *seven) echo ${1%seven}six;;
  30. *six) echo ${1%six}five;;
  31. *five) echo ${1%five}four;;
  32. *four) echo ${1%four}three;;
  33. *three) echo ${1%three}two;;
  34. *two) echo ${1%two}one;;
  35. one) echo zero;;
  36. *-one) echo ${1%-one};;
  37. *one) echo ${1%one};;
  38. ten) echo nine;;
  39. eleven) echo ten;;
  40. twelve) echo eleven;;
  41. *teen) teenpred $1;;
  42. *ty) tenspred $1;;
  43. zero) echo ""; #to terminate
  44. esac
  45. }
  46. teenpred() {
  47. #
  48. # Get predecessor of a teen $1
  49. #
  50. case $1 in
  51. thirteen) echo twelve;;
  52. *) echo $(crunchprefix $(pred $(uncrunchprefix ${1%teen})))teen;;
  53. esac
  54. }
  55. tenspred() {
  56. #
  57. # Get predecessor of a multiple of ten $1
  58. #
  59. case $1 in
  60. twenty) echo nineteen;;
  61. *) echo $(crunchprefix --tens $(pred $(uncrunchprefix ${1%ty})))ty-nine;;
  62. esac
  63. }
  64. crunchprefix() {
  65. #
  66. # Crunch number prefix $1 to its conventional form
  67. #
  68. # ...such as `three` --> `thir`
  69. #
  70. # option `--tens` - multiples of ten are a bit different
  71. #
  72. [ $1 = --tens ] && { tensop=true; shift; }
  73. case $1 in
  74. two) [ -n "$tensop" ] && echo twen || echo $1;;
  75. three) echo thir;;
  76. four) [ -n "$tensop" ] && echo 'for' || echo $1;;
  77. five) echo fif;;
  78. eight) [ -n "$tensop" ] && echo eigh || echo $1;;
  79. *) echo $1;;
  80. esac
  81. }
  82. uncrunchprefix() {
  83. #
  84. # Reverse crunchprefix $1
  85. #
  86. case $1 in
  87. twen) echo two;;
  88. thir) echo three;;
  89. 'for') echo four;;
  90. fif) echo five;;
  91. eigh) echo eight;;
  92. *) echo $1;;
  93. esac
  94. }
  95. grammar() {
  96. #
  97. # Apply peculiarities of English grammar to text on stdin
  98. #
  99. local oneBottle=false #can effect the following line
  100. while read line; do
  101. line="${line/one more bottles/one more bottle}"
  102. case "$line" in
  103. *"one of those bottles"*) line="$([ $oneBottle = true ] &&
  104. echo ${line/one of those bottles/that lone bottle} ||
  105. echo $line )"
  106. ;;
  107. *"one down"*) line="$([ $oneBottle = true ] &&
  108. echo ${line/one down/it down} ||
  109. echo $line )"
  110. ;;
  111. *bottles*) oneBottle=false;;
  112. *bottle*) oneBottle=true;;
  113. esac
  114. #Some say the twenties should have no hyphen
  115. line="${line/twenty-/twenty }"
  116. echo $line
  117. done
  118. }
  119. capitalize() {
  120. #
  121. # Fix capitalization of each line on stdin
  122. #
  123. while read line; do
  124. echo -n ${line:0:1} | tr '[:lower:]' '[:upper:]'
  125. echo ${line#?}
  126. done
  127. }
  128. punctuate() {
  129. #
  130. # Add punctuation to each line on stdin
  131. #
  132. while read line; do
  133. case "${line}" in
  134. [Ii]f*) echo ${line},;;
  135. '') echo;;
  136. *) echo ${line}.;;
  137. esac
  138. done
  139. }
  140. verse () {
  141. #
  142. # Write one verse with number $1
  143. #
  144. local nb=$1
  145. echo $nb bottles of beer on the wall
  146. echo $nb bottles of beer
  147. if [ $nb = zero ]; then
  148. echo Go to the store and buy some more
  149. nb=ninety-nine
  150. else
  151. echo $breakLine
  152. nb=$(pred $nb)
  153. fi
  154. echo $nb bottles of beer on the wall
  155. }
  156. poeticize() {
  157. #
  158. # Make text on stdin nice
  159. #
  160. while read first rest; do
  161. case "$rest" in
  162. *beer*)
  163. first=${first/zero/no}
  164. local syl=$(syllables ${first% *})
  165. case $syl in #improve meter
  166. 1|2) echo $first 'more' $rest;;
  167. *) echo $first $rest;;
  168. esac
  169. ;;
  170. *) echo $first $rest
  171. esac
  172. done
  173. }
  174. syllables() {
  175. #
  176. # Estimate number of syllables in word $1
  177. #
  178. local n=1
  179. case $1 in
  180. eleven) n=2;; #sounds better if not considered 3
  181. *teen) n=2;;
  182. *ty) n=2;;
  183. *-*) n=3;; #don't care about more than 3
  184. esac
  185. case $1 in
  186. *seven*) let $((n = n+1));;
  187. esac
  188. echo $n
  189. }
  190. nb=ninety-nine
  191. while [ -n "$nb" ]; do
  192. verse $nb
  193. echo
  194. nb=$(pred $nb)
  195. done | poeticize | grammar | punctuate | capitalize