I’m learning shell scripting, and am finding it hard finding a good way to learn. I have created a script below which lets the user search various different Internet engines through options. I would be really grateful if someone could look through this and point out what I’m doing wrong, how to improve it, etc.
#!/bin/bash
## Get user search-engine option
while getopts aegwy: OPTIONS ; do
case "$OPTIONS" in
a) ENGINE="http://www.amazon.com/s/ref=nb_sb_noss/?field-keywords";;
e) ENGINE="http://www.ebay.com/sch/i.html?_nkw";;
g) ENGINE="http://www.google.com/search?q";;
w) ENGINE="http://en.wikipedia.org/wiki/?search";;
y) ENGINE="http://www.youtube.com/results?search_query";;
?) ERRORS=true;;
esac
done &>/dev/null
## Ensure correct command usage
[ $# -ne 2 ] || [ $ERRORS ] && printf "USAGE: $(basename $0) [-a Amazon] [-e eBay] [-g Google] [-w Wikipedia] [-y YouTube] \"search query\"\n" && exit 1
## Ensure user is connected to the Internet
ping -c 1 209.85.147.103 &>/dev/null ; [ $? -eq 2 ] && printf "You are not connected to the Internet!\n" && exit 1
## Reformat the search query
QUERY=`printf "$2" | sed 's/ /+/g'`
## Execute the search and exit program
which open &>/dev/null ; [ $? -eq 0 ] && open "$ENGINE"="$QUERY" &>/dev/null && exit 0 || xdg-open "$ENGINE"="$QUERY" &>/dev/null && exit 0 || printf "Command failed!\n" && exit 1
Thanks in advance everyone, means a lot!
Best posted in codereviews, as indicated above, but here are some mostly stylistic comments. I should stress that the script is pretty much fine as-is; these are just minor improvements that I think will help make the code easier to read/maintain, more robust in a couple cases, etc.
You don’t need to use all-caps for variable names just because environment variables are all-caps; shell variables and environment variables aren’t the same thing.
Since your
$OPTIONSvariable only holds one option at a time, a singular name would be better (e.g.$option). Or you could go with$opt, which is somewhat traditional here.The
:in your getopts string (aegwy:) indicates that the-yoption expects an argument, as in-ysomething instead of just-yby itself. Since you aren’t doing anything with$OPTARG, I’m guessing that’s not intentional.As others have said, an
if/then/elif/elsewould probably be clearer than the chain of&&and||.The test
[ $ERRORS ]is somewhat unclear because it can mean a lot of different things depending on the content of the$ERRORSparameter. A more explicit indication that you only care about whether or not it’s set would be[ -n "$ERRORS" ].Comparisons like
[ -ne ]and friends are mostly holdovers from before the shell had built-in integer arithmetic; the more modern idiom would be(( $# != 2 )).Your usage message implies that the -a, -e, -g, -w, and -y options take arguments of the form Amazon, eBay, Google, etc. It would be clearer what the actual syntax of the command is without those additions; you can include an extra paragraph in the help text listing what each option stands for.
As a rule, error messages should go to stderr instead of stdout (
>&2).It’s fine to use
basename $0for consistency of output, but there’s something to be said for leaving$0alone as it will reflect however the user actually invoked the command. Something to consider.Not much point in using
printfif you’re not using a format string; just useecho, which automatically appends the newline. Usage messages traditionally don’t include quotation marks, either; it’s up to the user to quote the arg or not depending on whether it’s needed.Checking a command for success is exactly how
ifworks, so there’s no need to do explicit checks of$?unless you really care about the exact exit value. In the case of your connectivity ping, you probably don’t care about why it failed, only that it did:Your search query reformat might need to do more than just turn spaces into plus signs; what if it has an ampersand? But if you’re just doing the spaces-to-pluses thing, you could use bash parameter expansion do it without sed:
QUERY="${QUERY// /+}"If your program relies on open/xdg-open etc, you should probably check for its availability at the top; no sense doing anything else if you know you can’t possibly perform the requested operation anyway. And you can use a variable so you don’t wind up repeating yourself in multiple clauses:
And then later you can finish up with just this one line:
“$open” “$ENGINE=$QUERY” &>/dev/null