How to know what are commands, system calls, bash built-ins, etc?

A coworker recently asked "What is man"? After being informed that not all things accessible from the Bash CLI are commands, I was wary to call man a command.

man man just calls it an interface:

NAME
       man - an interface to the on-line reference manuals

man has an executable:

$ which man
/usr/bin/man
$ file /usr/bin/man
/usr/bin/man: ELF 64-bit LSB shared object

So is man a program, because it has an executable? What other nouns could man be? What noun would best describe it? Really, I’m interested in the general case of how I could determine what an arbitrary thing on the cli is, man is just an example.

For that matter, what is the word for all things that one could use on the Bash CLI? A word that encompasses commands, aliases, system calls, etc?

Asked By: dotancohen

||

In POSIX terms, anything that can ask the shell to do something is a command:

A directive to the shell to perform a particular task.

So

man man

is a command, as is (technically)

man

man is also a utility:

A program, excluding special built-in utilities provided as part of the Shell Command Language, that can be called by name from a shell to perform a specific task, or related set of tasks.

(The exclusion isn’t significant here; it’s mentioned because special built-in utilities have specific properties.)

To find out what a given command is, use type. This will tell you if it’s built-in, or a program on the PATH (and where), or an alias etc. (or unknown).

Note that system calls aren’t usable as shell commands.

See also Are they commands or utilities? and What is the difference between a builtin command and one that is not?

Answered By: Stephen Kitt

Starting from the lowest level:

System calls

System calls are the way userland tasks (must) use to request some service from the kernel and run into priviledged kernel mode.

Let’s say for example C is your programming language and you want your task to change its current directory, you will need to insert a chdir() instruction into your program.

Of course these procedures are not immediately accessible from the command line. v.g. typing chdir on the command line won’t call the chdir system call.

The list of all available system calls is of course kernel dependent and the only reliable source is certainly the include/linux/syscalls.h header file of your kernel source distribution.

Instructions

A CLI is an interface to the task you are running. Whatever you type in is called an instruction simply because it it supposed to instruct the task to achieve some action.

Commands

What you type will first make its way through an interpreter that will perform some lexical analysis on the tokens you typed and, in the particular case of a shell, might recognize the name of a command (understood as not a variable assignment) and incidentally decide that it needs to resort to another program in order to achieve your request. It will then fork a child process that will exec the binary of that other program.

But there are special cases for which the task might find easier to satisfy the request without resorting to external programs (trivial calculus for instance) or, more important, must execute your request internally.

Built-in commands

Coming back to our initial will to change the active directory, the user will issue the well known shell command cd. What the user actually wants is to get the current directory of his shell to be changed. And because the chdir system call only changes the current working directory of the caller, the shell just cannot fork another process that would not change anything for its parent. The shell must execute the chdir system call internally.

The shell built-in commands are listed in all yourshellname man pages.

Aliases

Aliases are nothing else than synonyms that any user can setup and that will be translated by the command line interpreter into the desired string (supposed to represent whatever legal instruction to the shell.)

The list of all currently active aliases can be obtained thanks to the shell built-in alias command.

Answered By: MC68020

I have a small shellscript, that can help me identify a command: what kind of command it is and if installed via a program package, which package. Maybe use the name what-about,

#!/bin/bash

LANG=C
inversvid="033[7m"
resetvid="033[0m"

if [ $# -ne 1 ]
then
 echo "Usage: ${0##*/} <program-name>"
 echo "Will try to find corresponding package"
 echo "and tell what kind of program it is"
 exit 1
fi
command="$1"

str=;for ((i=1;i<=$(tput cols);i++)) do str="-$str";done
tmp="$command"
first=true
curdir="$(pwd)"
tmq=$(which "$command")
tdr="${tmq%/*}"
tex="${tmq##*/}"
if test -d "$tdr"; then cd "$tdr"; fi
#echo "cwd='$(pwd)' ################# d"

while $first || [ "${tmp:0:1}" == "l" ]
do
 first=false
 tmp=${tmp##* }
 tmq="$tmp"
 tmp=$(ls -l "$(which "$tmp")" 2>/dev/null)
 tdr="${tmq%/*}"
 tex="${tmq##*/}"
 if test -d "$tdr"; then cd "$tdr"; fi
# echo "cwd='$(pwd)' ################# d"
 if [ "$tmp" == "" ]
 then
  tmp=$(ls -l "$tex" 2>/dev/null)
  tmp=${tmp##* }
  if [ "$tmp" == "" ]
  then
   echo "$command is not in PATH"
#   package=$(bash -ic "$command -v 2>&1")
#   echo "package=$package XXXXX 0"
   bash -ic "alias '$command' > /dev/null 2>&1" > /dev/null 2>&1
   if [ $? -ne 0 ]
   then
    echo 'looking for package ...'
    package=$(bash -ic "$command -v 2>&1"| sed -e '0,/with:/d'| grep -v '^$')
   else
    echo 'alias, hence not looking for package'
   fi
#   echo "package=$package XXXXX 1"
   if [ "$package" != "" ]
   then
    echo "$str"
    echo "package: [to get command '$1']"
    echo -e "${inversvid}${package}${resetvid}"
   fi
   else
    echo "$tmp"
   fi
 else
  echo "$tmp"
 fi
done
tmp=${tmp##* }
if [ "$tmp" != "" ]
then
 echo "$str"
 program="$tex"
 program="$(pwd)/$tex"
 file "$program"
 if [ "$program" == "/usr/bin/snap" ]
 then
  echo "$str"
  echo "/usr/bin/snap run $command     # run $command "
  sprog=$(find /snap/"$command" -type f -iname "$command" 
   -exec file {} ; 2>/dev/null | sort | tail -n1)
  echo -e "${inversvid}file: $sprog$resetvid"
  echo "/usr/bin/snap list $command    # list $command"
  slist="$(/usr/bin/snap list "$command")"
  echo -e "${inversvid}$slist$resetvid"
 else
  package=$(dpkg -S "$program")
  if [ "$package" == "" ]
  then
   package=$(dpkg -S "$tex" | grep -e " /bin/$tex$" -e " /sbin/$tex$")
   if [ "$package" != "" ]
   then
    ls -l /bin /sbin
   fi
  fi
  if [ "$package" != "" ]
  then
   echo "$str"
   echo " package: /path/program  [for command '$1']"
   echo -e "${inversvid} $package ${resetvid}"
  fi
 fi
fi
echo "$str"
#alias=$(grep "alias $command=" "$HOME/.bashrc")
alias=$(bash -ic "alias '$command' 2>/dev/null"| grep "$command")
if [ "$alias" != "" ]
then
 echo "$alias"
fi
type=$(type "$command" 2>/dev/null)
if [ "$type" != "" ]
then
 echo "type: $type"
elif [ "$alias" == "" ]
then
 echo "type: $command: not found"
fi
cd "$curdir"

Sometimes there are two alternatives, e.g. for echo, both a separate compiled program and shell built-in command. The shell built-in will get priority and be used unless you use the full path of the separate program,

$ what-about echo
-rwxr-xr-x 1 root root 35000 jan 18  2018 /bin/echo
----------------------------------------------------------------------------------
/bin/echo: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically
linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0,
BuildID[sha1]=057373f1356c861e0ec5b52c72804c86c6842cd5, stripped
----------------------------------------------------------------------------------
 package: /path/program  [for command 'echo']
 coreutils: /bin/echo 
----------------------------------------------------------------------------------
type: echo is a shell builtin

Sometimes a command is linked to program, that might be hidden, e.g. the version of rename that I use,

$ what-about rename
lrwxrwxrwx 1 root root 24 maj 12  2018 /usr/bin/rename -> /etc/alternatives/rename
lrwxrwxrwx 1 root root 20 maj 12  2018 /etc/alternatives/rename -> /usr/bin/file-rename
-rwxr-xr-x 1 root root 3085 feb 20  2018 /usr/bin/file-rename
----------------------------------------------------------------------------------
/usr/bin/file-rename: Perl script text executable
----------------------------------------------------------------------------------
 package: /path/program  [for command 'rename']
 rename: /usr/bin/file-rename 
----------------------------------------------------------------------------------
type: rename is /usr/bin/rename

I have an alias for rm in order to avoid mistakes, and the alias has priority over the program in PATH. You can prefix with backslash, rm to skip the alias and run the program directly. (Please remember that the alias applies only for the specific user, and not for sudo and other users, unless they have defined a similar alias.)

$ what-about rm
-rwxr-xr-x 1 root root 63704 jan 18  2018 /bin/rm
---------------------------------------------------------------------------
/bin/rm: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for
GNU/Linux 3.2.0, uildID[sha1]=864c9bbef111ce358b3452cf7ea457d292ba93f0,
stripped
---------------------------------------------------------------------------
 package: /path/program  [for command 'rm']
 coreutils: /bin/rm 
---------------------------------------------------------------------------
alias rm='rm -i'
type: rm is /bin/rm
Answered By: sudodus
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.