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