I've read that semicolon is used to separate programs:
$ echo 3; ls -la
Does it mean that
else are separate programs here?
$ if [ $VARIABLE == abcdef ] ; then echo yes ; else echo no ; fi
This question is not about semicolons.
; separates statements (loosely speaking). It is (almost) always possible to replace a
; by a newline.
To say that
; separates two programs, therefore
then must be "programs" is a bit too simplistic as a statement may be made of reserved words, shell functions, built-in utilities and external utilities, and combinations of these using pipes and boolean operators etc. etc.
then are reserved words in the shell grammar, not "programs". Here they are used to build up what's technically called a compound command.
echo is likely a built-in utility in the shell (but doesn't need to be), and
ls is probably an external utility (or "program" as you say).
Whilst that is a fair first approximation when one is starting out learning the very basics of using shells, at the level of "here is how one runs a program" and "here is how one runs multiple programs one after another on a single line", it is not actually true.
The harder to grasp for a beginner but more correct explanation is that the shell language is a computer language. It has a syntax. That syntax comprises various lexical elements including (amongst other things) newlines, operators, words, and reserved words.
fi are all reserved words. They have particular meanings when parsing the input that one gives to a shell, according to its grammar. Similarly,
; is a separator operator.
Input in the shell language is thus, taken as a whole, a computer program that is interpreted by another program, an interpreter, the shell. Its individual grammatical parts are not programs. The shell language is a way of specifying (other) programs for the shell to run.
[ is not a special lexical element in the shell grammar such as an operator. It is an ordinary word, that names one such program named
[. Many shells have a built-in version of this program, combined into the code of the shell program itself, but you can also find an external program by this name somewhere such as
/usr/bin/[, which programs other than shells can invoke. Equally,
] isn't a special shell lexical element either. It's an ordinary word, that becomes an argument for the
[ program. The
[ program requires that its final argument, when it is executed, be
], which it proceeds to then ignore.
Another similar program named in your question is
echo. Again, most shells have a built-in version of this program. But again there is also an external version of the program, somewhere such as
/usr/bin/echo, for programs other than shells to invoke.
A third program named in your question is
ls. Shells generally do not have built-in versions of this program, and it is an external program, to be found somewhere such as
For the Bourne Again shell, you can read more about this in the Basic Shell Features of the GNU Bourne Again shell info documentation. Other shells have different grammars, naturally. The Single Unix Specification describes a syntax that all POSIX-conformant shells (in their POSIX-conformant modes) are supposed to adhere to.
It is actually not far-fetched to think of
else as external programs. In fact, the Thompson shell in the original 1st edition Unix implemented
goto as external programs. This is possible because the subprocess shares file descriptors with the shell process, so a (forward) goto just had to read input until it finds the target label and then exits. See Thompson shell.
else are not programs. The other parts are. Notice there's no
;' directly after them, but after the command they precede.
[ ... ] is a command, and needs the
; if followed by the beginning of another command.
AFAIK, all of control structures in Bash, and probably most *nix shells, are the same. They are instructions to the interpreter. The test or condition, on the other hand, uses a program/process that is "executed" and are commands. Since
then is part of the line that leads to the
echo command it has to be separated by a newline from the previous command
[ ... ]. It doesn't need to be separated from the command it controls, the
Legally, although ugly and hard to read, you could also do this.
if [ $VARIABLE == abcdef ] then echo yes else echo no fi
Notice that there's no need for
; between the controls at all here, even though they are not on their own line.
Interestingly enough, the whole control structure (
if ... fi) is a shell command, and the entirety has to end with a newline or a
;. The last line can't be
fi echo done but must be
fi; echo done. The same as an assignment
VARIABLE='abcdef' is a command.
Even though the whole control structures are commands, they still are not programs.
fi are all reserved keywords used to implement one of the constructs referred to as a compound command in shell, which means there cannot be a command (or rather, another command) by any of those names in shell. The purpose of the
; in general is not to separate commands, but to terminate a command list. For example, the following is a valid
if echo foo; echo bar; echo baz; then echo done; echo really done; fi
The condition of the
if statement is the command list
echo foo; echo bar; echo baz. The parser knows that the condition is over because
then, which immediately follows a semi-colon, cannot be a command because it is a reserved keyword. Thus, it knows that what follows
then is the start of the body. Likewise,
fi is a reserved keyword and thus cannot be a third command in the body of the
if statement, but marks the end of the compound command.