11

I came across this command whilst setting up ssh:

$ w

 20:01:30 up 6 days,  2:20,  3 users,  load average: 0.06, 0.11, 0.10
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
rick     tty7     :0               16Jun20  6days 16:51   2.37s /sbin/upstart --user
rick     pts/21   192.168.0.12     18:44    1:14m  0.19s  0.00s sleep 60
rick     pts/22   192.168.0.12     18:45    0.00s  0.44s  0.01s w

Which led me to wonder what all the one letter commands are in Ubuntu. I didn't find an exact reference but I found this website:

Here the w command is listed along with:

  • . the command to source a file which I've used
  • v is a command not installed in Ubuntu. It is said to be equivalent to ls -l -b. This in turn is similar to the the ll alias setup by Ubuntu except files beginning with . (including the infamous . and ..) are not included.

Are there other one letter commands installed within Ubuntu by default?

1 Answer 1

29

TL;DR: This tells you about each command whose name is a single character, though you may want to simplify or otherwise adjust it:

(set -f; type -a $(compgen -cX '!?' | sort -u))

Listing one-character commands with compgen

Bash has tab completion and offers a compgen builtin, which provides insight into how text is completed. By running compgen -c, you can list all the commands on your system. So one way to list all commands that are just one character long is to filter the output of compgen -c:

compgen -c | grep '^.$'

As steeldriver has pointed out, compgen accepts an -X option to do its own filtering. Somewhat nonintuitively, the operand to -X is a pattern whose matches are removed. It supports globbing syntax that works like filename expansion, but also supports an initial ! to negate it, achieving the desired effect here. (See Programmable Completion Builtins for more information about the syntax.) ? matches any single character, so !? matches anything that is not a single character, so -X '!?' removes everything that is not a single character, leaving only single-character commands:

compgen -cX '!?'

That shows the same results, in the same order, as piping to grep. You can pass -c and -X in one argument as -cX or (as steeldriver shows it) in two arguments as -c -X.

On my system, I get:

l
p
{
}
!
.
:
[
s
t
[
X
w

s and t on my system are scripts I wrote; you most likely don't have commands with those names. You likely do have the others.

Whether these are all really commands or not depends on what one thinks of as a command. {, }, and ! are shell keywords, like if, case, while, and for. The others (including shell builtins) are unambiguously commands in the usual sense.

The meaning of repeated results

Some commands, such as [ in the above output, may be repeated. This happens when more than one command of the same name exists. Bash offers [ as a shell builtin, but it is also an executable located in /usr/bin. Running type cmd shows what type of command cmd is and, if it is an external command, where it is located. When there is more than one command of the same name, it shows the one that will actually be used. As Kulfy has pointed out, you can see them all using type -a cmd.

$ type -a [
[ is a shell builtin
[ is /usr/bin/[

There's another source of possible duplication: on some systems, /bin and /usr/bin are actually the same directory (more precisely: /bin is a symlink to /usr/bin) and you'll see entries in both /bin and /usr/bin. For example, you may have both /bin/[ and /usr/bin/[ as well as the [ builtin, as well as both /bin/w and /usr/bin/w and both /bin/X and /usr/bin/X. These are technically separate commands, but they actually refer to the same executable.

Traditionally, /bin and /usr/bin were separate directories (so that commands in /bin would be usable even when /usr is an NFS volume that is not yet mounted). But these days this doesn't usually offer a benefit. So newer systems tend to unify them. If you're using a newer release, you probably have this, unless you upgraded to it from an older release that did not.

If you like, you can sort the output of compgen and remove duplicates:

compgen -c | grep '^.$' | sort -u
compgen -cX '!?' | sort -u

Showing details on all the commands

For more detail, you might wish to know what type of command each one is and:

  • for the external commands, where they are located.
  • for aliases and shell functions, how they are defined.

As mentioned above, Bash's type builtin will tell you all this. You can write a command that supplies the filtered output of compgen to type.

type $(compgen -c | grep '^.$')
type $(compgen -cX '!?')

That will behave badly if you have a * or ? command. That's quite unlikely, but not impossible. To safeguard against that, you may want to disable globbing:

(set -f; type $(compgen -c | grep '^.$'))
(set -f; type $(compgen -cX '!?'))

But this (with any of those four alternatives) is imperfect, because all commands of the same name have the same explanation, which is only correct for the first one. For example, on my system, I get:

l is aliased to `ls -CF'
p is aliased to `printf "\e[?2004l"'
{ is a shell keyword
} is a shell keyword
! is a shell keyword
. is a shell builtin
: is a shell builtin
[ is a shell builtin
s is /home/ek/bin/s
t is /home/ek/bin/t
[ is a shell builtin
X is /usr/bin/X
w is /usr/bin/w

You may very well only want to see information about the first occurrence of each command of the same name (e.g., the shell builtin [). After all, those are the ones that actually run, when you write a command whose first word is any of those one-character names (e.g., [ -f /bin/nano ] runs the shell builtin [, not /usr/bin/[). If so, you can sort the output of compgen and remove duplicates before handing the results to type:

type $(compgen -c | grep '^.$' | sort -u)
type $(compgen -cX '!?' | sort -u)

Or, to also safeguard against globbing:

(set -f; type $(compgen -c | grep '^.$' | sort -u))
(set -f; type $(compgen -cX '!?' | sort -u))

However, if you want information about all the commands (e.g., /usr/bin/[ as well as [), then you can pass -a to type. I recommend that you still sort and remove duplicates from the results first, or you'll get lots of repetition in the output.

type -a $(compgen -c | grep '^.$' | sort -u)
type -a $(compgen -cX '!?' | sort -u)

Or, to also safeguard against globbing:

(set -f; type -a $(compgen -c | grep '^.$' | sort -u))
(set -f; type -a $(compgen -cX '!?' | sort -u))

What commands you have varies across shell environments

Note that which one-character commands you have, like which commands you have in general, is a property of your shell environment. Different users, or different running shells by the same user, may have different commands available. The l command in Ubuntu, for example, is defined in /etc/skel/.bashrc, which is copied to ~/.bashrc when user accounts are created in the usual way.

# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

You can thus change or remove the definition of l in your per-user ~/.bashrc. Or simply run unalias l to affect just your current shell.

2
  • 3
    I think it'll be better to use -a option with type since [ is a shell builtin, [ is /usr/bin/[ and [ is /bin/[ (at least in my system).
    – Kulfy
    Commented Jun 23, 2020 at 10:37
  • 1
    I was playing with this last night and came up with compgen -c -X '!?' (using compgen's built-in pattern matching / pattern exclusion feature as an alternative to using grep) Commented Jun 23, 2020 at 11:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .