Running a command

We have described how to enter a command on the command line, how to redirect input and output, how to enter several commands on a single command line that either act independently or whose input and output are tied together, and how to run multiple commands concurrently.

We have described how the command line is processed before execution. Now, lets examine what actually happens when a command is run.

When you start a command or program running, it becomes a process. But a process is more than the program. A process is a program's code and the resources needed for the program to run. This includes the memory allocated by the operating system (the OS) to hold the program code, any memory needed by the program while it is running for work, the interface to any and all I/O devices, and information needed by the OS to track and control the running process. It also includes information copied from the process that invoked the program.

For example, when you run ls from the bash command prompt, some of the information known by bash is shared with the ls command, whether it needs it or not. This includes the command line arguments and any variables and functions marked as exportable.

When logged in, you are interacting with a copy of the bash shell program running as a process. This copy was started by the operating system as part of the login procedure and assigned a unique process id. This id assignment allows the OS to run many copies of the bash shell and still keep track of each individual copy, who is using it, what terminal is assigned to it, and what commands and files it is interacting with.

When you issue a command, several actions are taken to execute it. The OS forks the command interpreter. The forking process causes the command shell or other program invoking the new command to be duplicated in memory and assigned a new unique process id. This called the child process. Any exportable information is also copied into the new memory location. Any I/O opened with redirection is also copied. If successful, the forking process returns the new processes' id to the parent and a zero to the new process or child.

If the new process is to run in the foreground, the parent process then goes to sleep.

If the new child process is a background process, then the parent process continues to run. In the case of the bash interpreter, you will immediately be issued a new command-line prompt.

The child process fetches the command specified into the newly allocated memory replacing the code copied from the parent program. The other information, such as environmental variables, remains in memory.

The new code executes and is free to create new variables, change existing variables, and open new redirection. These changes exist only while the child process exists.

On completion of activity, the new code returns a status value. This code is passed back to the parent process. Newly created variables or changes in variables passed in to the child will be lost on termination. If the parent process was sleeping, it will begin to execute again.

Keep in mind, built-in shell commands do not invoke the forking process. They often need to access and change local variables. Examples of builtins are echo, set, declare, export, source, etc. See the section "Shell Builtin Commands" around line 3365 in the bash man pages.

It is possible to fetch an external command directly into the current shell environment. This is useful if you want to run a command in the foreground and have it terminate the login on completion of the task. The command to do this exec. This is a shell builtin that performs the same action that happens in a forked child process except the forking never occurs.

exec find / -name "*.h" > found 2> /dev/null

The preceding example loads the find command into the current processes' working memory, replacing the command interpreter, and runs it. Upon completion, since there is no longer a command interpreter to return execution to, the login session terminates. You can see this in a simpler example.

#( Try the following. First start a second copy of the bash interpreter. This is so when the command terminates the shell process, you don't have to log back in. )

bash

#( First run ps normally, then use exec to invoke ps )

ps > psout1
exec ps > psout2

#( If things ran correctly, you will back in the original shell you invoked bash from. )

#( Now compare the two outputs )

diff psout1 psout2

The psout1 should list a copy of bash and the ps command. The psout2 should list only the ps command. Also note that the process id of bash in psout1 matches the process id of ps in psout2.


Unix provides several commands for monitoring and controlling processes you have initiated.

We have used ps and its options several times to see what is running.

Unix provides another command for listing background (concurrent) processes started from the current shell. A process running in the background of the current shell is called a job. And you may list any jobs running with the jobs command.

#(Try the following )

#(Run the sleep process in the background three times )

sleep 130 & sleep 125 & sleep 150 & sleep 100 &

#(Now list the jobs running. The -l includes the process id. )

ps -f
jobs -l

#( The jobs started will terminate on their own in 1 to 2 minutes)

Notice that jobs only lists the processes started concurrently or in the background. The 1st column lists a number in the brackets, []. These are the jobs id and will be sequentially numbered in the order the jobs were started.

The next column will either be blank or contain a + or -. The most recently manipulated job will have a plus and the one before that the minus. The rest will be blank.

The third column lists the process ids. This will usually be a larger number, most likely in ascending order but also be randomly spaces. Job ids are unique to the current shell. Process ids are unique to the whole system.

The forth column lists the status of the jobs. For now they should all be running.

Finally, the command will be listed.

Invoking jobs without the option will list the same except for the process ids.

To bring a jobs into the foreground, use the fg %job-id command, where job-id is the specific job number..

fg %2

If fg is invoked with no job id, the job with the + in front of it will begin to run as a foreground process.

To put the process back in the background, type in [ctrl]z. This signals the process to become a background job with suspended or stopped status. And this job's id will be displayed. To restart it in the background, use bg. If you perform bg immediately after the [ctrl]z, you will not need to specify the job id. If you need to specifically target a suspended background job, use the %job-id with the command.

bg and fg are builtin shell commands. As such, they will only work on jobs started from the current terminal and they will only take job ids.