Bash, version 2

The current version of Bash, the one you have running on your machine, is most likely version 2.xx.yy, 3.xx.yy, or 4.xx.yy.

bash$ echo $BASH_VERSION
3.2.25(1)-release

The version 2 update of the classic Bash scripting language added array variables, string and parameter expansion, and a better method of indirect variable references, among other features.

String expansion

#!/bin/bash

# String expansion. # Introduced with version 2 of Bash.

# Strings of the form $'xxx' #+ have the standard escaped characters interpreted.

echo $'Ringing bell 3 times \a \a \a' # May only ring once with certain terminals. # Or ... # May not ring at all, depending on terminal settings. echo $'Three form feeds \f \f \f' echo $'10 newlines \n\n\n\n\n\n\n\n\n\n' echo $'\102\141\163\150' # B a s h # Octal equivalent of characters.

exit

Indirect variable references - the new way

#!/bin/bash

# Indirect variable referencing. # This has a few of the attributes of references in C++.

a=letter_of_alphabet letter_of_alphabet=z

echo "a = $a" # Direct reference.

echo "Now a = ${!a}" # Indirect reference. # The ${!variable} notation is more intuitive than the old #+ eval var1=\$$var2

echo

t=table_cell_3 table_cell_3=24 echo "t = ${!t}" # t = 24 table_cell_3=387 echo "Value of t changed to ${!t}" # 387 # No 'eval' necessary.

# This is useful for referencing members of an array or table, #+ or for simulating a multi-dimensional array. # An indexing option (analogous to pointer arithmetic) #+ would have been nice. Sigh.

exit 0

# See also, ind-ref.sh example.

Simple database application, using indirect variable referencing

#!/bin/bash # resistor-inventory.sh # Simple database / table-lookup application.

# ============================================================== # # Data

B1723_value=470 # Ohms B1723_powerdissip=.25 # Watts B1723_colorcode="yellow-violet-brown" # Color bands B1723_loc=173 # Where they are B1723_inventory=78 # How many

B1724_value=1000 B1724_powerdissip=.25 B1724_colorcode="brown-black-red" B1724_loc=24N B1724_inventory=243

B1725_value=10000 B1725_powerdissip=.125 B1725_colorcode="brown-black-orange" B1725_loc=24N B1725_inventory=89

# ============================================================== #

echo

PS3='Enter catalog number: '

echo

select catalog_number in "B1723" "B1724" "B1725" do Inv=${catalog_number}_inventory Val=${catalog_number}_value Pdissip=${catalog_number}_powerdissip Loc=${catalog_number}_loc Ccode=${catalog_number}_colorcode

echo echo "Catalog number $catalog_number:" # Now, retrieve value, using indirect referencing. echo "There are ${!Inv} of [${!Val} ohm / ${!Pdissip} watt]\ resistors in stock." # ^ ^ # As of Bash 4.2, you can replace "ohm" with \u2126 (using echo -e). echo "These are located in bin # ${!Loc}." echo "Their color code is \"${!Ccode}\"."

break done

echo; echo

# Exercises: # --------- # 1) Rewrite this script to read its data from an external file. # 2) Rewrite this script to use arrays, #+ rather than indirect variable referencing. # Which method is more straightforward and intuitive? # Which method is easier to code?

# Notes: # ----- # Shell scripts are inappropriate for anything except the most simple #+ database applications, and even then it involves workarounds and kludges. # Much better is to use a language with native support for data structures, #+ such as C++ or Java (or even Perl).

exit 0

Using arrays and other miscellaneous trickery to deal four random hands from a deck of cards

#!/bin/bash # cards.sh

# Deals four random hands from a deck of cards.

UNPICKED=0 PICKED=1

DUPE_CARD=99

LOWER_LIMIT=0 UPPER_LIMIT=51 CARDS_IN_SUIT=13 CARDS=52

declare -a Deck declare -a Suits declare -a Cards # It would have been easier to implement and more intuitive #+ with a single, 3-dimensional array. # Perhaps a future version of Bash will support multidimensional arrays.

initialize_Deck () { i=$LOWER_LIMIT until [ "$i" -gt $UPPER_LIMIT ] do Deck[i]=$UNPICKED # Set each card of "Deck" as unpicked. let "i += 1" done echo }

initialize_Suits () { Suits[0]=C #Clubs Suits[1]=D #Diamonds Suits[2]=H #Hearts Suits[3]=S #Spades }

initialize_Cards () { Cards=(2 3 4 5 6 7 8 9 10 J Q K A) # Alternate method of initializing an array. }

pick_a_card () { card_number=$RANDOM let "card_number %= $CARDS" # Restrict range to 0 - 51, i.e., 52 cards. if [ "${Deck[card_number]}" -eq $UNPICKED ] then Deck[card_number]=$PICKED return $card_number else return $DUPE_CARD fi }

parse_card () { number=$1 let "suit_number = number / CARDS_IN_SUIT" suit=${Suits[suit_number]} echo -n "$suit-" let "card_no = number % CARDS_IN_SUIT" Card=${Cards[card_no]} printf %-4s $Card # Print cards in neat columns. }

seed_random () # Seed random number generator. { # What happens if you don't do this? seed=`eval date +%s` let "seed %= 32766" RANDOM=$seed } # Consider other methods of seeding the random number generator.

deal_cards () { echo

cards_picked=0 while [ "$cards_picked" -le $UPPER_LIMIT ] do pick_a_card t=$?

if [ "$t" -ne $DUPE_CARD ] then parse_card $t

u=$cards_picked+1 # Change back to 1-based indexing, temporarily. Why? let "u %= $CARDS_IN_SUIT" if [ "$u" -eq 0 ] # Nested if/then condition test. then echo echo fi # Each hand set apart with a blank line.

let "cards_picked += 1" fi done

echo

return 0 }

# Structured programming: # Entire program logic modularized in functions.

#=============== seed_random initialize_Deck initialize_Suits initialize_Cards deal_cards #===============

exit

# Exercise 1: # Add comments to thoroughly document this script.

# Exercise 2: # Add a routine (function) to print out each hand sorted in suits. # You may add other bells and whistles if you like.

# Exercise 3: # Simplify and streamline the logic of the script.