From /tech/ Wiki
Jump to navigationJump to search

Shell or Shell script is a language and user interface model, based around directing the execution of other programs to do useful units of work. Shell is as nearly as old as computing, being one of the first interface models, originally implemented on teletypes. Modern shell began with the unix bourne shell, which introduced many concepts like piping and redirection with their modern syntax. Today, shell is defined by the posix standard, with the most popular implementation being the GNU bourne-again shell (Bash).


The prehistory of shell is in batch operating systems, where the language to control the execution of a job had many precursors to shell. The real history began with the advent of time-sharing systems, which enabled shell languages to be created. As fancier teletypes and terminals were invented and distributed, shells began to acquire more sophisticated features. Today's shells require extensive emulation of these early machines in order to implement their functionality.


In a shell environment, a user is presented with a bare prompt, on which they can enter a newline-delimited command, typically consisting of the name of a program, built-in, alias, or function, followed by some number of space delimited arguments. In the case of a program, the shell will pass the list of arguments, including the program name, to the program as the c programming language "char* argv[]". The program will have three file descriptors by default: 0 for stdin, which will receive any data the user types, 1 is stdout, for which data will be printed to the terminal, as will data from fd 2, being stderr. Rather than merely type data into stdin, or look at the data emerging from stdout, the user can point these file-descriptors elsewhere. A pipe can take the output from one program and pass it as the input to another program. A redirect can pass the input to a program as a file, or put the output into a new file.

These basic concepts can be augmented by more complex interactions between sequential lines. A program can modify the computer environment, such as the file system or the network, the shell environment, such as file descriptors or variables, or control which programs are executed next.


In the unix environment, all IO is done through file descriptors, being numbers that the OS associates with a process and a file or another resource. When a process, such as a shell, forks to invoke a command, the child process inherits the file descriptors of the parent (unless told not to). So by default, a program will share stdin, stdout, and stderr with the shell. However, before execing to run the new program, the shell can pipe(2), open(2), or close(2) to modify the file descriptors the process will inherit. The exec shell built-in allows the user to modify the file descriptors of the shell, and therefore that will be inherited by child processes.


When a process exits, it leaves behind an exit code, typically a number from 0 to 127. 0 is by convention no error, whereas every other number indicates some error. The user can have one process run or not run depending on the exit status of another process. Several more complex control-clow operations, especially if and while, are based around these operations.


Shell typically allows the user to set variables to string or other values, and to expand those variables in command names using the $ syntax. Using the export built-in, a user can create environment variables, which will be inherited by all child processes. Environment variables can effect the places the kernel will look for programs, the places the linker will look for libraries, the behaviour of many programs with respect to languages, and a variety of other generic behaviours. There are also particular programs that have specific environment variables set aside to change their behaviour, such as their behaviour with respect to paging or colours. Environment variables can be set for a single command by prepending the declaration.

Job Control

Inheriting from their legacy for batch processing, shells have many capabilities with respect to controlling the execution of jobs. A job consists of a pipeline of tasks, or a set of sequential tasks that have been expressly joined together. A job can be instructed, either on invocation or after it has been started, to run in the background. This will not surpress output, but will allow the user to run other commands simultaneously. Such a job can later be brought to the foreground again, or terminated while executing.



$ ls  # print the files in the current dir
$ cat file | grep string  # pipe the contents of file into grep, filtering only lines containing string
$ < file grep string  # same as above
$ make && ./a.out  # runs the executable only if the build was successful
$ if make; then ./a.out; done  # same as above
$ export S1=123
$ S2=456 bash -c 'echo $S1 $S2' # prints 123 456
$ { sleep 100; echo done; } &  # runs in the background
$ { sleep 100; echo done; }
$ bg  # stop the process then send it to the background
$ kill %sleep  # kill the job with job descriptor "sleep" before it finishes