Ubuntu – Does the error message for two colons as a command (::) in bash have three colons, but a single colon give no output

bashcommand line

If I type

::

into a bash shell, I get:

-bash: ::: command not found

But, only one : results in no output. Why is this?

Best Answer

  • The : shell built-in vs non-existent ::

    The : shell built-in command exists (note the difference between external and built-in commands) which does nothing; it just returns success, just like the true command. The : built-in is standard and defined by the POSIX standard, where it's also known as the "null utility". It is frequently used for testing or for running infinite loops as in while : ; do ...;done

    bash-4.3$ type :
    : is a shell builtin
    

    However, :: - two colon characters together - are interpreted as one "word" to the shell, and it assumes to be a command the user entered. The shell will go through the process of checking built-ins, then any directory in the PATH variable for existence of that command. But there is neither a built-in :: nor external command ::. Therefore, that produces an error.

    Well, what is a typical format for an error?

    <shell>: <command user typed>: error message
    

    Thus, what you see isn't 3 colons but what you typed pasted into the standard error format.

    Note also, that : can take command-line arguments, i.e. it is legal to do:

    : :
    

    In this case, the shell will consider that as two "words", one of which is a command and the other a positional parameter. That will also produce no error! (See also the historical note (later in this answer) about the use the of : with positional parameters.)


    In shells other than bash

    Note that formatting can vary between different shells as well. For bash, ksh, and mksh the behavior is consistent. For instance, Ubuntu's default /bin/sh shell (which is actually /bin/dash):

    $ dash
    $ ::
    dash: 1: ::: not found
    

    where 1 is the command number (equivalent to the line number in a script).

    csh by contrast produces no error message at all:

    $ csh
    % ::
    %
    

    In fact, if you run strace -o csh.trace csh -c ::, the trace output in csh.trace file reveals that csh exits with exit status 0 (no errors). But tcsh does output the error (without outputting its name, though):

    $ tcsh
    localhost:~> ::
    ::: Command not found.
    

    Error messages

    In general, the first item in the error message should be the executing process or function (your shell tries to execute ::, hence the error message comes from the shell). For instance, here the executing process is stat:

    $ stat noexist
    stat: cannot stat 'noexist': No such file or directory
    

    In fact, POSIX defines the perror() function, which according to the documentation takes a string argument, then outputs error message after colon, and then newline. Quote:

    The perror() function shall map the error number accessed through the symbol errno to a language-dependent error message, which shall be written to the standard error stream as follows:

    • First (if s is not a null pointer and the character pointed to by s is not the null byte), the string pointed to by s followed by a colon and a <space>.

    • Then an error message string followed by a <newline>.

    And the string argument to perror() technically could be anything, but of course for clarity it's typically the function name or argv[0].

    By contrast, GNU has its own set of functions and variables for error handling, which a programmer can use with fprintf() to stderr stream. As one of the examples on the linked page shows, something like this could be done:

      fprintf (stderr, "%s: Couldn't open file %s; %s\n",
               program_invocation_short_name, name, strerror (errno));
    

    Historical note

    In old Unix and Thompson shell, : was used with goto statement (which according to user named Perderabo on this thread wasn't a shell built-in). Quote from the manual:

    The entire command file is searched for a line beginning with a : as the first non-blank character, followed by one or more blanks, and then the label. If such a line is found, goto repositions the command-file offset to the line after the label and exits. This causes the shell to transfer to the labelled line.

    So you could do something like this to make an infinite loop script:

    : repeat
    echo "Hello World"
    goto repeat
    
  • Related Question