Bash: Handling Command Not Found
After a recent O/S version upgrade (to openSUSE 11.2) I noticed that bash started being a bit more intelligent when I did something stupid: it started giving me a useful error message when I typed the name of a command that wasn't in my PATH but that was in an "sbin" directory. My reaction at the time was "huh, that's nice", but today I decided I needed a bit more information.
As an example of this behavior, if I type ifconfig while not logged in as root I get the get the following:
$ ifconfig
Absolute path to 'ifconfig' is '/sbin/ifconfig', so running it may require superuser privileges (eg. root).
Which is certainly more useful than a "command not found" message.
Turns out that this capability is a standard feature of bash. From bash's man page:
... A full search of the directories in PATH is performed only if the command is not found in the hash table. If the search is unsuccessful, the shell searches for a defined shell function named command_not_found_handle. If that function exists, it is invoked with the original command and the original command's arguments as its arguments, and the function's exit status becomes the exit status of the shell. If that function is not defined, the shell prints an error message and returns an exit status of 127.
I'm not sure if that's a new feature of bash or if it's just something that's recently implemented in openSUSE.
A quick grep in /etc discovered where it was happening. The function itself is in /etc/bash_command_not_found and that function gets included (if it exists) in your bash session via /etc/bash.bashrc.
The function itself is not that complex but there are a couple of useful tidbits that I wanted to point out as a sidebar. The following code determines if the invoking shell was executed from Midnight Commander or is taking input from a pipe:
# do not run when inside Midnight Commander or within a Pipe
if test -n "$MC_SID" -o ! -t 1 ; then
echo $"$1: command not found"
return 127
fi
And the following determines if the invoking shell is a sub-shell:
# do not run when within a subshell
read pid cmd state ppid pgrp session tty_nr tpgid rest < /proc/self/stat
if test $$ -eq $tpgid ; then
echo "$1: command not found"
return 127
fi
End of sidebar.
At the end of the function there's some code that uses /usr/bin/command-not-found (a python script) to lookup commands in installable packages (via zypper), but you need to set an environment variable (COMMAND_NOT_FOUND_AUTO) to activate it, and of course you need to have python installed. To test this try the following:
$ export COMMAND_NOT_FOUND_AUTO=1
$ pascal
pascal: command not found
$ gcj
The program 'gcj' can be found in the following package:
* gcc-java [ path: /usr/bin/gcj, repository: zypp (repo-oss) ]
Try installing with:
sudo zypper install gcc-java
Since this command-not-found functionality is handled by a bash function, you can of course replace the system installed function (if one exists on your system) with one of your own design. All you need is to include it in your .bashrc script. The entire version of openSUSE's script follows:
command_not_found_handle() {
export TEXTDOMAIN=command-not-found
local cmd state rest
local -i pid ppid pgrp session tty_nr tpgid
# do not run when inside Midnight Commander or within a Pipe
if test -n "$MC_SID" -o ! -t 1 ; then
echo $"$1: command not found"
return 127
fi
# do not run when within a subshell
read pid cmd state ppid pgrp session tty_nr tpgid rest < /proc/self/stat
if test $$ -eq $tpgid ; then
echo "$1: command not found"
return 127
fi
# test for /usr/sbin and /sbin
if test -x "/usr/sbin/$1" -o -x "/sbin/$1" ; then
if test -x "/usr/sbin/$1" ; then prefix='/usr' ; else prefix='' ; fi
echo $"Absolute path to '$1' is '$prefix/sbin/$1', so running it may require superuser privileges (eg. root)."
return 127
fi
if test -n "$COMMAND_NOT_FOUND_AUTO" ; then
# call command-not-found directly
test -x /usr/bin/python && test -x /usr/bin/command-not-found && /usr/bin/python /usr/bin/command-not-found "$1" zypp
else
# print only info about command-not-found
echo -e $"If '$1' is not a typo you can use command-not-found to lookup the package that contains it, like this:\n cnf $1"
fi
return 127
}
Mitch Frazier is an Associate Editor for Linux Journal.
Trending Topics
| OpenLDAP Everywhere Reloaded, Part I | May 23, 2012 |
| Chemistry the Gromacs Way | May 21, 2012 |
| Make TV Awesome with Bluecop | May 16, 2012 |
| Hack and / - Password Cracking with GPUs, Part I: the Setup | May 15, 2012 |
| An Introduction to Application Development with Catalyst and Perl | May 14, 2012 |
| Cryptocurrency: Your Total Cost Is 01001010010 | May 09, 2012 |
- OpenLDAP Everywhere Reloaded, Part I
- Strip DRM from WMV File
- Validate an E-Mail Address with PHP, the Right Way
- Boot with GRUB
- Why Python?
- A Statistical Approach to the Spam Problem
- Chapter 16: Ubuntu and Your iPod
- Why Hulu Plus Sucks, and Why You Should Use It Anyway
- Building an Ultra-Low-Power File Server with the Trim-Slice
- Science the GNU Way, Part I
- Editorial Standards?
4 hours 5 min ago - Great one
5 hours 39 min ago - Common form in many
6 hours 1 min ago - Awsome
11 hours 3 min ago - Euro 2012 Coupon Codes - Get 20% Off Pavtube TiVo Converter
3 days 9 hours ago - Euro 2012 Big Sale: 20% Off Instant Savings on TiVo Converter
3 days 9 hours ago - MakeMKV works as well, though
3 days 9 hours ago - Euro 2012 Big Sale: 20% Off Instant Savings on TiVo Converter
3 days 10 hours ago - Awesome
4 days 8 hours ago - Who worries approx the
4 days 10 hours ago





Comments
command_not_found_handle used for oo-style
Try the oobash! It is an oo-style string library for bash 4. It has support for german umlauts. It is written in bash. Many functions are available: -base64Decode, -base64Encode, -capitalize, -center, -charAt, -concat, -contains, -count, -endsWith, -equals, -equalsIgnoreCase, -reverse, -hashCode, -indexOf, -isAlnum, -isAlpha, -isAscii, -isDigit, -isEmpty, -isHexDigit, -isLowerCase, -isSpace, -isPrintable, -isUpperCase, -isVisible, -lastIndexOf, -length, -matches, -replaceAll, -replaceFirst, -startsWith, -substring, -swapCase, -toLowerCase, -toString, -toUpperCase, -trim, and -zfill.
Look at the contains example
###############################
First call the constructor:
[Desktop]$ String a testXccc
Now use object.method:
[Desktop]$ a.contains tX
true
[Desktop]$ a.contains XtX
false
http://sourceforge.net/projects/oobash/
The command-not-found handler
The command-not-found handler originated from Ubuntu, I think, and was accepted into bash somewhere in v3+, but didn't pass the arguments to the function until bash v4.
Well known feature of packagekit copied from debian
http://blogs.gnome.org/hughsie/2008/12/05/command-not-found/
Why no gravatar support for comments? I don't want a login account. It's another password I have better things to do than remember.
Don't have to remember...
...just get a wallet and store your passwords in an ecrypted file:
apt-cache show revelation
Gabriel Menini
Linux Registered User #207262
Can be seen as a security hole
Sometimes giving ambiguous info about the command is better than showing the full path and permissions in order to execute it.
However, this can be assumed as a security-through-obscurity approach and some people may not like it.
Take ssh login for example. If you don't type the correct username & password you won't get access to the remote box. If you match the user but the password is still wrong, the remote dæmon won't let you know that _only_ the password was incorrect.
I've found some powerful bash shells over there (say Ubuntu and OpenSUSE) that suggests you the package where the 'command not found' can be found, if not installed.
User-friendly: sure! Safe: not so sure....
Gabriel Menini
Linux Registered User #207262
I don't think you're right.
I don't think you're right. This information (what package to get the command) can so simply be found looking for " package" on a search engine on Internet, that it can't be considered as a secret.
So be friendly with million people is definitively more important than assuming a really little security option for some rare cases.
How about Debian squeeze?
So Ubuntu does this - anyone know if there's similar functionality in squeeze (that needs to be enabled) because it doesn't do it for me at the moment?
Another related usefull bash feature
bash has built-in command called shopt, that can do similar things, like auto correction of directory names (on cd command):
shopt -s cdspellYou can read more about shopt here.
--
My blog: http://frishit.wordpress.com
Is this default in other distributions
Sounds like a great feature, good job openSUSE for enabling it by default.
Does anyone know if Fedora or Ubuntu enable similar behavior by default?
Very useful :-)
Ubuntu has for a long time
a couple of responses have described mis-spelling responses, but Ubuntu goes a step further. If you properly spell the command, but the package is not installed, BASH will tell you how to install the package so the command will be available.
Ubuntu does.
Ubuntu does.
ubuntu@karmic:~$ pascal
No command 'pascal' found, did you mean:
Command 'pscal' from package 'xcal' (universe)
pascal: command not found
Ubuntu does...
...and it is REALLY useful as it even copes with mis-spellings:
$ pascalNo command 'pascal' found, did you mean:
Command 'pscal' from package 'xcal' (universe)
pascal: command not found
$ pling
No command 'pling' found, did you mean:
Command 'plink' from package 'putty-tools' (universe)
Command 'ping' from package 'inetutils-ping' (universe)
Command 'ping' from package 'iputils-ping' (main)
pling: command not found
$
so even with my occasionally rubbish typing I get what I need and a good idea of what to install to enable it.
--
Smotsie
Dad.husband.linux-loving-geek.radio-presenter.eco-geek