#!/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"