123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- #!/bin/bash
-
- #
- # Bottles of Beer song in Bash
- #
- # A way-overcomplicated implementation of the bottles of beer song in bash.
- # Optional argument:
- #
- # * `--careless` - Generate the "happen to fall" version
- #
- # NOTE: This is an altered version of a "99 bottles of beer" song
- # Bash script by Bill Brown. The intent of the altered version is
- # to show the style, so the above text is preserved from [the
- # original][99b].
- #
- # [99b]: http://99-bottles-of-beer.net/language-bash-1831.html
- #
-
- #
- # Default transition mode
- #
- # 'standard' to use the standard form; 'careless' to have the
- # song consider effects of gravity on the bottles.
- #
- # Default is 'standard'.
- #
- NN_BOTTLES__MODE=${NN_BOTTLES__MODE:-standard}
-
- #
- # Starting bottle amount
- #
- # Initial amount of bottles specified as numeral. Numerals up
- # to 99 are supported.
- #
- # Default is 'ninety-nine'.
- #
- NN_STARTING_AMOUNT=${NN_STARTING_AMOUNT:-ninety-nine}
-
- pred() {
- #
- # Get predecessor to a number $1
- #
- local numeral=$1 # number in word form
- case $numeral in
- *nine) echo "${numeral%nine}eight" ;;
- *eight) echo "${numeral%eight}seven" ;;
- *seven) echo "${numeral%seven}six" ;;
- *six) echo "${numeral%six}five" ;;
- *five) echo "${numeral%five}four" ;;
- *four) echo "${numeral%four}three" ;;
- *three) echo "${numeral%three}two" ;;
- *two) echo "${numeral%two}one" ;;
- one) echo "zero" ;;
- *-one) echo "${numeral%-one}" ;;
- *one) echo "${numeral%one}" ;;
- ten) echo "nine" ;;
- eleven) echo "ten" ;;
- twelve) echo "eleven" ;;
- *teen) teenpred "$numeral" ;;
- *ty) tenspred "$numeral" ;;
- zero) echo ""; #to terminate
- esac
- }
-
- teenpred() {
- #
- # Get predecessor of a teen $1
- #
- local numeral=$1 # number in word form
- case $numeral in
- thirteen) echo twelve;;
- *) echo "$(crunchprefix "$(pred "$(uncrunchprefix "${numeral%teen}")")")teen" ;;
- esac
- }
-
- tenspred() {
- #
- # Get predecessor of a multiple of ten $1
- #
- local numeral=$1 # number in word form
- case $numeral in
- twenty) echo nineteen;;
- *) echo "$(crunchprefix --tens "$(pred "$(uncrunchprefix "${numeral%ty}")")")ty-nine";;
- esac
- }
-
- crunchprefix() {
- #
- # Crunch number prefix $1 to its conventional form
- #
- # ...such as `three` --> `thir`
- #
- # option `--tens` - multiples of ten are a bit different
- #
- local numeral # number in word form
- local tensop # 'true' if --tens option is active
- [ "$1" = --tens ] && { tensop=true; shift; }
- numeral=$1
- case $numeral in
- two) [ -n "$tensop" ] && echo twen || echo "$numeral";;
- three) echo thir;;
- four) [ -n "$tensop" ] && echo 'for' || echo "$numeral";;
- five) echo fif;;
- eight) [ -n "$tensop" ] && echo eigh || echo "$numeral";;
- *) echo "$numeral" ;;
- esac
- }
-
- uncrunchprefix() {
- #
- # Reverse crunchprefix $1
- #
- local prefix=$1 # numeral crunch-prefix
- case $prefix in
- twen) echo two;;
- thir) echo three;;
- 'for') echo four;;
- fif) echo five;;
- eigh) echo eight;;
- *) echo "$prefix";;
- esac
- }
-
- grammar() {
- #
- # Apply peculiarities of English grammar to text on stdin
- #
- local oneBottle=false # 'true' if this line affects the following line
- local line # every line
- while read -r line; do
- line="${line/one more bottles/one more bottle}"
- case "$line" in
- *"one of those bottles"*) line="$(
- [ $oneBottle = true ] \
- && echo "${line/one of those bottles/that lone bottle}" \
- || echo "$line"
- )"
- ;;
- *"one down"*) line="$(
- [ $oneBottle = true ] \
- && echo "${line/one down/it down}" \
- || echo "$line"
- )"
- ;;
- *bottles*) oneBottle=false;;
- *bottle*) oneBottle=true;;
- esac
- #Some say the twenties should have no hyphen
- line="${line/twenty-/twenty }"
- echo "$line"
- done
- }
-
- capitalize() {
- #
- # Fix capitalization of each line on stdin
- #
- local line # every line
- while read -r line; do
- echo -n "${line:0:1}" | tr '[:lower:]' '[:upper:]'
- echo "${line#?}"
- done
- }
-
- punctuate() {
- #
- # Add punctuation to each line on stdin
- #
- local line # every line
- while read -r line; do
- case "${line}" in
- [Ii]f*) echo "${line},";;
- '') echo;;
- *) echo "${line}.";;
- esac
- done
- }
-
- verse() {
- #
- # Write one verse with number $1
- #
- local nb=$1 # numeral
- echo "$nb bottles of beer on the wall"
- echo "$nb bottles of beer"
- if [ "$nb" = zero ]; then
- echo "Go to the store and buy some more"
- nb=ninety-nine
- else
- echo "$TransitionLine"
- nb=$(pred "$nb")
- fi
- echo "$nb bottles of beer on the wall"
- }
-
- poeticize() {
- #
- # Make text on stdin nice
- #
- local first # first word of a line
- local rest # remainder of a line
- local syl # number of syllables in the first word
- while read -r first rest; do
- case "$rest" in
- *beer*)
- first=${first/zero/no}
- syl=$(syllables "${first% *}")
- case $syl in #improve meter
- 1|2) echo "$first more $rest";;
- *) echo "$first $rest" ;;
- esac
- ;;
- *) echo "$first $rest"
- esac
- done
- }
-
- syllables() {
- #
- # Estimate number of syllables in word $1
- #
- local word=$1 # word (numeral) to do the estimation on
- local n=1
- case $word in
- eleven) n=2;; #sounds better if not considered 3
- *teen) n=2;;
- *ty) n=2;;
- *-*) n=3;; #don't care about more than 3
- esac
- case $word in
- *seven*) ((n = n+1)) ;;
- esac
- echo "$n"
- }
-
- main() {
- local TransitionLine # transition line between verses
- local togo=$NN_STARTING_AMOUNT # bottles to go
- local mode=$NN_BOTTLES__MODE # transition mode
- while true; do case $1 in
- --careless) mode=careless; shift ;;
- *) break ;;
- esac done
- TransitionLine=$(
- case $mode in
- standard) echo "Take one down and pass it around" ;;
- careless) echo "If one of those bottles should happen to fall" ;;
- esac
- )
- togo=ninety-nine
- while [ -n "$togo" ]; do
- verse "$togo"
- echo
- togo=$(pred "$togo")
- done | poeticize | grammar | punctuate | capitalize
- }
-
- main "$@"
|