#!/bin/bash -u # This script extracts the current weather information from the Web. #------------------------------------------------------------ # Syntax: # $0 [ city ] #------------------------------------------------------------ # Purpose: # Accept a city name on the command line. Prompt and read the # city if it is missing. If the city is empty, default to "Ottawa". # If the city is 3 UPPER-CASE letters, use it directly; otherwise, # convert the city to the 3-letter airport code using a built-in table. # Contact the Environment Canada Web site for the given airport code. # Download the current weather HTML page for that airport code. # Extract the current weather information and display it nicely. #------------------------------------------------------------- # Student Name: Ian! D. Allen # Algonquin EMail Address: alleni # Student Number: 000-000-000 # Course Number: CST 8129 # Lab Section Number: 010 # Professor Name: Ian! D. Allen # Assignment Name/Number/Date: Assignment #3 due December 6 # Comment: This is a sample assignment label. # see http://idallen.com/teaching/assignment_standards.html #------------------------------------------------------------- # Set standard search path and umask. # PATH=/bin:/usr/bin ; export PATH umask 022 # Function definitions go here ------------------------------------ # The CityToCode function turns its argument (a Canadian city) into # the 3-character upper-case code for the city. The argument city # may be upper, lower, or mixed case. Return 1 if not found (no output). # CityToCode () { # Translate the city name to lower-case for looking up in our table. # city=$( echo "$1" | tr 'A-Z' 'a-z' ) # Translate the argument city to the 3-letter city code. # case "$city" in ottawa) code=YOW ;; montreal) code=YUL ;; toronto) code=YYZ ;; *) return 1 ;; esac echo "$code" # function exits with return status of last command } # The GetField function locates the first occurrence of a field in the # web page and returns the text following the field at the end of the line. # First argument: user-supplied title of this field, for error messages # Second argument: regular expression to match at start of line # Third argument: name of file in which to look for the regexp # The function echoes all the text found after the regexp. # Return code is 1 if nothing was found; nothing is echoed to stdout. # GetField () { title="$1" regexp="$2" file="$3" # Scan the whole input file trying the sed substitution on every line. # Turn off default sed output; only print if the substitute works. # The "head -1" makes sure we only get the first occurrence. # We pick off everything that follows the regexp and echo it. # (Another way, perhaps less clear: sed -n -e "s/$regexp//p") # field=$( sed -n -e "s/$regexp"'\(.*\)/\1/p' "$file" | head -1 ) if [ -z "$field" ] ; then echo 1>&2 "$0: Error fetching '$title' using '$regexp' from '$file'." return 1 # failed fi echo "$field" return 0 # success } # End of function definitions --------------------------------------- # Main program starts here. # Pick up a missing city by prompting on stderr and reading # it from standard input. Accept only zero or one city name. # If more than 1, echo the count and incorrect input on stderr and exit. # case "$#" in 0) echo 1>&2 "Enter a Canadian City or UPPER-CASE 3-letter City Code:" read city ;; 1) city="$1" ;; *) echo 1>&2 "$0: Expecting one City argument, found $# ($*)" exit 1 ;; esac # The default, if no city is given, is to use this city. # Echo the city that is going to be used. # if [ -z "$city" ] ; then city='Ottawa' echo "Using default city of '$city'." fi echo "Now retrieving weather for '$city'." # If the argument is exactly 3 upper-case letters, assume it is # already a City Code. If not 3 upper-case letters, assume it is a city # name and look up the code. (Some cities are only 3 letters, e.g. Oka.) # case "$city" in [A-Z][A-Z][A-Z]) code="$city" echo "Using UPPER-CASE City Code '$code'." ;; *) if ! code=$( CityToCode "$city" ) ; then echo 1>&2 "$0: Cannot find the City Code for '$city'." exit 1 fi echo "City Name '$city' has City Code '$code'." esac # Establish a few temp file names for downloading and editing web pages. # tmp=/tmp/weather1$$ tmp2=/tmp/weather2$$ rm -f "$tmp" "$tmp2" # Download the weather for the given City Code. # weatherpage='http://weatheroffice.ec.gc.ca/scripts/citygen.pl?client=ECCDN_e' wget --quiet -O "$tmp" "$weatherpage&city=$code" # Check that the file is non-empty and that wget worked. # if [ ! -s "$tmp" -o "$?" -ne 0 ] ; then echo 1>&2 "$0: wget of web page '$weatherpage' into '$tmp' failed" echo 1>&2 "...try again later?" exit 1 fi # Pre-process the downloaded web page: # Remove carriage returns from the pages. # Change
and

(any case) to spaces (improves output). # Remove all the other HTML tags. # Substitute out a few common web-escaped special characters. # (Note that the input and output files MUST NOT be the same!) # tr -d '\r' <"$tmp" \ | sed -e 's/<[Bb][Rr]>/ /g' -e 's/<[Pp]>/ /g' \ -e 's/<[^<>]*>//g' \ -e 's/°//g' \ -e 's/ç/c/g' \ -e 's/ / /g' >$tmp2 mv $tmp2 $tmp # rename the secondary output file back to $tmp # Look for errors in the downloaded web page in $tmp. # This puts the error messages on the screen, which you may not like. # You could redirect stdout to /dev/null to suppress the output. # if grep -i -w error "$tmp" ; then echo 1>&2 "$0: Error fetching weather for City Code '$code'." exit 1 fi # Extract a fiew fields from the downloaded web page. # title=$( GetField 'City Title' '^Weather for ' "$tmp" ) temp=$( GetField 'Temperature' '^Temp\.:' "$tmp" ) pressure=$( GetField 'Pressure' '^Pressure:' "$tmp" ) visibility=$( GetField 'Visibility' '^Visibility:' "$tmp" ) humidity=$( GetField 'Humidity' '^Humidity:' "$tmp" ) wind=$( GetField 'Wind' '^Wind:' "$tmp" ) # Some pages have Dew Point, some have Wind Chill. # I don't think they are ever both there at once. # Try both. Only print an error if neither is found. # (Note how stderr is redirected on both these lines.) # dewpoint=$( GetField 2>/dev/null 'Dew Point' '^DewPoint:' "$tmp" ) windchill=$( GetField 2>/dev/null 'Wind Chill' '^WindChill:' "$tmp" ) if [ -z "$dewpoint" -a -z "$windchill" ] ; then echo 1>&2 "$0: Cannot find Dew Point or Wind Chill in $tmp" fi # Remove the temporary file when done. # rm "$tmp" # Output the current weather. # Only output the Dew Point or Wind Chill if they are found. # echo " " echo "The Current Weather for $title ($code)" echo " Temperature: $temp" echo " Pressure: $pressure" echo " Visibility: $visibility" echo " Humidity: $humidity" if [ ! -z "$dewpoint" ] ; then echo " Dew Point: $dewpoint" fi if [ ! -z "$windchill" ] ; then echo " Wind Chill: $windchill" fi echo " Wind: $wind"