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.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

command_not_found_handle used for oo-style

Anonymous's picture

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

anonymous's picture

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

Hey! Dojo's picture

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...

arlequin's picture

...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

arlequin's picture

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.

gUI's picture

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?

gouchout's picture

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

Gal Frishman's picture

bash has built-in command called shopt, that can do similar things, like auto correction of directory names (on cd command):
shopt -s cdspell

You can read more about shopt here.

--
My blog: http://frishit.wordpress.com

Is this default in other distributions

thogarty's picture

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

Anonymous's picture

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.

Anonymous's picture

Ubuntu does.

ubuntu@karmic:~$ pascal
No command 'pascal' found, did you mean:
Command 'pscal' from package 'xcal' (universe)
pascal: command not found

Ubuntu does...

smotsie's picture

...and it is REALLY useful as it even copes with mis-spellings:
$ pascal
No 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

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState