Inspired by the command not found hook that you'll find in many GNU/Linux distributions I wanted to use a similar approach in order to create dynamic functions. Let's write a simple currency converter! First we're going to need a source to get exchange rates from. A quick search led me to http://rate-exchange.appspot.com/ (if you plan to keep using the API you can help out the host by donating).
The reason I chose this particular site is because it doesn't require registration and it will return the data in json which is awesome for two reasons. It's a simpler (less fat) format than xml and it gives me an excuse to show off jshon
. The program can be found at http://kmkeen.com/jshon/ and hopefully also in your distributions repositories. If you scroll to the bottom of the page you'll see a REAL EXAMPLE and some additional notes on using jshon
in the shell pipeline.
Let's take jshon
for a spin. Running the following command will pretty-print the data to make it more readable.
% wget -qO- "http://rate-exchange.appspot.com/currency?from=eur&to=usd" | jshon
{
"to": "usd",
"rate": 1.3737299999999999,
"from": "eur"
}
To extract a value we will pass the -e
parameter to jshon followed by the index rate
that will return to us the value of 1.3737299999999999
.
Let's skip ahead to the function.
function currconv()
{
if [ $# != 3 ]; then
echo "usage: $0 <from> <to> <amount>"
return 0
fi
local from="$1" to="$2" amount="$3"
local reply=$(wget -qO- "http://rate-exchange.appspot.com/currency?from=$from&to=$to" | jshon -QC -erate)
if [ $reply = "null" ]; then
return 127
else
printf "%.2f\n" $(( $amount * $reply ))
fi
}
- declare a function that expects exactly 3 arguments, else display help
- assign variables local to the function
- run
wget
against the API with the parameters supplied to the script, pipe to jshon that will disable error reporting (-Q
), continue on errors (-C
) and output the value ofrate
- if the response is
null
it means we supplied a non supported rate conversion (or something else could've gone wrong, error handling could be improved). A return code of 127 implies an error and printscommand not found
if called fromcommand_not_found_handler()
- if we got a valid response, perform the arithmetic evaluation and return the value with 2 decimal places
% currconv eur usd 5
6.87
Next step is to get this function in command_not_found_handler()
function command_not_found_handler()
{
if [[ $1 =~ "[a-zA-Z]{3}2[a-zA-Z]{3}" ]]; then
local from=${1%2*} to=${1#*2} amount=$2
currconv $from $to $amount
else
return 127
fi
}
- match the command that was not found against a regex pattern
- 3 characters matching a..z upper and lower case
- the digit 2
- same as first
- if match
- create local variables
from
andto
by splitting the command- say we call the command
eur2usd
from the shell - from =
eur
- to =
usd
- say we call the command
- since the command (
eur2usd
) was the first argument, the amount will be stored in the second ($2
) - call the function
- create local variables
- no match
- return 127 (command not found)
The result
% eur2usd 5
6.87
% usd2wut 10
zsh: command not found: usd2wut