3

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?

dotancohen
  • 15,864
  • 2
    Bash has a shell built-in type ..., which tells you where external commands are, and the type of anything else it recognises. type alias man fi type for example. – Paul_Pedant Jun 09 '22 at 08:51
  • 2
  • @Kusalananda Thank you. That page is informative, but none of the posts have any upvotes, most were from low-rep users, none was accepted as a correct answer, and most contradict this comment by a high-rep user. There exists information on the page, but as the OP of that very question states, it seems to just be repeating poor usage of terminology by other (mostly online) sources. – dotancohen Jun 09 '22 at 09:28
  • "arbitrary thing on the cli" is not going to be a system call - you can't just run a system call on the CLI directly. – muru Jun 09 '22 at 12:14
  • 1
    I'm not sure how the creat() example aligns here, since it's not something you can directly call from a shell command line. (Of course pretty much any program might use it, same as a shell redirection like > somefile, though then again creat() is redundant, as you might as well call open() with the appropriate flags, but in any case, you don't make system calls by name from the shell command line) – ilkkachu Jun 09 '22 at 12:30
  • oh, and, given how the shell language specification talks about "compound commands" and "simple commands", pretty much everything you run on a shell command line is a command. ls or man? Simple commands.if-statement? Compound command. A call to a shell function? Simple command, just that the command is a function and not an external binary. Shell builtin? Same. somecmd | grep whatever? That's a pipeline, containing two simple commands. A function definition?? It's a "Function Definition Command", believe it or not... – ilkkachu Jun 09 '22 at 12:33
  • 2
    @ilkkachu with a little help from ctypes.sh ... ;-) – Stephen Kitt Jun 09 '22 at 12:34
  • 1
    @StephenKitt, nggg... oh deary. Let's try keep that away from any children, please... I do like how they've included the "Here is what people have been saying about ctypes.sh" section there. I like what they've included there. Yes, that sounds like something that might make mortal men lose their sanity. – ilkkachu Jun 09 '22 at 12:45
  • 1
    How did you conclude that not all things accessible from the Bash CLI are commands? The page that you link to doesn't describe a thing that's accessible from the Bash CLI. – Tanner Swett Jun 09 '22 at 20:51
  • @dotancohen I don't see anything relevant to the command-line interface in the answer that you link to, or in its comment. Since you link to it both in the question and in the reply to my comment, I assume it is important to your query, but I still don't quite understand how. – Kusalananda Jun 09 '22 at 21:30

3 Answers3

9

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?

Stephen Kitt
  • 434,908
5

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.

John Kugelman
  • 2,057
  • 2
  • 16
  • 23
MC68020
  • 7,981
  • I must say instruction is more commonly tied to "machine instructions" or "assembly instructions" in the field of computers. Better substitutes include statement and directive. – iBug Jun 10 '22 at 20:39
  • @iBug : For having a long time programmed in assembly language, I would like to agree with you. However, strictly speaking, according to sh and bash manuals, these programs are defined as "command line interpretors". You enter "commands" composed of "command names" and "operands". But, in the context of a programming language I find hard to call some variable assignment a "command". And even more "then" "else"… that are nothing but keywords. But, let's go with your suggestion of "directive". That can indeed fit for all. I'll modify my contribution. – MC68020 Jun 10 '22 at 22:20
  • If you're going to mention system calls you surely need at least mention library calls. getpwent(3) vs open(2), read(2), close(2) – Chris Davies Jun 15 '22 at 06:37
4

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="\0033[7m" resetvid="\0033[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
sudodus
  • 6,421
  • Thank you, that bash script is terrific. I'm learning quite a lot reading it. – dotancohen Jun 09 '22 at 15:40
  • @dotancohen, You are welcome, I'm glad I could help you. – sudodus Jun 09 '22 at 15:41
  • Your answer does not attempt to answer the question, which is about terminology. – Kusalananda Jun 09 '22 at 18:08
  • 2
    @Kusalananda, About terminology, yes, but also 'how I could determine what an arbitrary thing on the cli is', which I interpret as a [software] method to find out. Anyway, the other answers focus on terminology, so both aspects are given attention. – sudodus Jun 09 '22 at 20:41