#!/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 # 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 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 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 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 $breakLine 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 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*) let $((n = n+1));; esac echo $n } main() { local breakLine # bottle-break (or not) line local standardBreakLine # ^^ standard variant local wastefulBreakLine # ^^ wasteful (bottle breaks) variant local nb # beginning number standardBreakLine="Take one down and pass it around" wastefulBreakLine="If one of those bottles should happen to fall" breakLine=$( [ "$1" = --careless ] \ && echo $wastefulBreakLine \ || echo $standardBreakLine ) nb=ninety-nine while [ -n "$nb" ]; do verse $nb echo nb=$(pred $nb) done | poeticize | grammar | punctuate | capitalize } main "$@"