Linux Commands: Making Bash Error Messages Friendlier - page 2
Trapping and Befriending Error Messages
For instance, if the user typed the name of a file instead of an executable program, wouldn't it be handy to check the type of the file and suggest programs they might have intended? Something like this:
$ /tmp bash: /tmp: is a directory status was 126 Perhaps you meant: cd /tmp $ schedule.html bash: ./schedule.html: Permission denied schedule.html is an HTML file. Did you want to run: firefox schedule.html
That's easy to do. You can check for a directory with the shell construct if [[ -d. If it's a file, you can use the file command to guess what type it is.
if [[ -e $cmd ]]; then
if [[ -d $cmd ]]; then
echo "Perhaps you meant: cd $cmd"
elif [[ ! -x $cmd ]]; then
echo ""
filetype=$(file $cmd)
# HTML must come before text, since file says "HTML document text"
if [[ $filetype = *HTML* ]]; then
echo "$cmd is an HTML file. Did you want to run: firefox $cmd"
[ ... ]
You can use similar methods for each file type you want to handle --
you might want to suggest apps the user could call for image files,
text files, movies and so on.
What about that case of a filename missing a slash? That's easy to check for too: look through the list of arguments, check whether each file exists, and if it doesn't, see if adding a slash to the beginning gives you the name of an existing file:
if [[ $status -eq 2 ]]; then
# loop over args looking for the first one that might be missing a slash
for f in $cmd "${args[@]}"; do
if [[ $f = */* && ! -e $f ]]; then
if [[ -e /$f ]]; then
echo ""
echo "$f doesn't exist, but /$f does."
echo "Did you forget a leading slash?"
return
fi
fi
done
You can even check for cases where the file isn't readable, and suggest that the user might need to be root.
if [[ -e $cmd ]]; then
if [[ -d $cmd ]]; then
echo "Perhaps you meant: cd $cmd"
elif [[ ! -x $cmd ]]; then
if [[ ! -r $cmd ]]; then
echo ""
echo "$cmd is a file but it's not readable or executable."
echo "Maybe you need to be root?"
Akkana Peck is a freelance programmer and writer, and author of the book Beginning GIMP: From Novice to Professional. She also fiddles with way too many kernels and Linux distros.
A sample error-handling script
Here's a basic example of a bash error handler. You can add this to the end of your .bashrc; or save it as a separate file, like ~/.bash-error, then add this line to your .bashrc:
. $HOME/.bash-errs
Here's the script:
#
# Offer slightly friendlier error messages for certain types of errors.
#
function err_handle {
status=$?
if [[ $status -ne 2 && $status -ne 126 && $status -ne 127 ]]; then
return
fi
# Ucky pipeline which is, amazingly enough,
# the only way to get the last typed command from bash.
# fc -n -l -1 doesn't always have the command yet,
# !! doesn't work from inside functions
# and BASH_COMMAND gets confused by functions like ls().
lastcmd=$(history | tail -1 | sed 's/^ *[0-9]* *//')
# cool way to split a string into component words:
read cmd args <<< "$lastcmd"
# Handle possible errors involving forgetting a leading slash if the
# command was okay but the error was 2, "no such file or directory".
if [[ $status -eq 2 ]]; then
# loop over args looking for the first one that might be missing a slash
for f in $cmd "${args[@]}"; do
if [[ $f = */* && ! -e $f ]]; then
if [[ -e /$f ]]; then
echo ""
echo "$f doesn't exist, but /$f does."
echo "Did you forget a leading slash?"
return
fi
fi
done
return
fi
if [[ -e $cmd ]]; then
if [[ -d $cmd ]]; then
echo "Perhaps you meant: cd $cmd"
elif [[ ! -x $cmd ]]; then
if [[ ! -r $cmd ]]; then
echo ""
echo "$cmd is a file but it's not readable or executable."
echo "Maybe you need to be root?"
echo "You could try sudo less $cmd"
echo "or sudo $(myeditor) $cmd"
return 127
fi
#
# By now, we know it's a file and it's readable.
# Figure out the file's type, and print appropriate messages:
#
echo ""
filetype=$(file $cmd)
# HTML must come before text, since file says "HTML document text"
if [[ $filetype = *HTML* ]]; then
echo "$cmd is an HTML file. Did you want to run: firefox $cmd"
elif [[ $filetype = *text* ]]; then
echo "$cmd is a text file. Did you want to run:"
echo " less $cmd"
echo " vim $cmd"
elif [[ $filetype = *image* ]]; then
echo "$cmd is an image file. Did you want to run:"
echo " pho $cmd"
echo " gimp $cmd"
else
# "file" gives terribly complex output for MS Office documents
# so get the mime type to detect those:
mimetype=$(xdg-mime query filetype $cmd | sed 's/;.*$//')
if [[ $mimetype == application/msword ]]; then
echo "$cmd is a Microsoft Word file."
echo "Perhaps run: ooffice $cmd"
elif [[ $mimetype =~ application/.*ms- ]]; then
echo "$cmd is a file of type"
echo " $mimetype (Microsoft)."
echo "Perhaps try: ooffice $cmd"
else
#
# Unknown file type -- bomb out.
#
echo "$cmd is a file of type $mimetype."
echo "What do you want to do with it?"
fi
fi
else
echo "Hmm, $cmd exists and is executable -- not sure what went wrong"
fi
# else
# echo "Sorry, $cmd doesn't exist"
# If we want to be REALLY nice we could look for similarly named progs.
# But it turns out Ubuntu's command-not-found-handle does that already.
fi
}
# Trap errors.
trap 'err_handle' ERR
- Skip Ahead
- 1. Trapping and Befriending Error Messages
- 2. Trapping and Befriending Error Messages
Solid state disks (SSDs) made a splash in consumer technology, and now the technology has its eyes on the enterprise storage market. Download this eBook to see what SSDs can do for your infrastructure and review the pros and cons of this potentially game-changing storage technology.