99bottles.sh 5.3KB

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