﻿# Processes, signals and filters

> Every command becomes a process: find it with ps, move it between foreground and background, end it with the right signal and catch signals with trap. Then filters and pipes – find, grep, sort, tr and wc chained into one-liners.

Source: https://laszlonemes.com/university/computer-systems/processes-filters · Author: László Nemes

Session 2 · Computer Systems

Every command becomes a process: find it with ps, move it between foreground and background, end it with the right signal and catch signals with trap. Then filters and pipes – find, grep, sort, tr and wc chained into one-liners.

18 min read `PID, PPID and process states` `Foreground and background jobs` `Signals, kill and trap` `strace and system calls` `Filters and pipes` `grep -x, -c and -o`

In [the first session](https://laszlonemes.com/university/computer-systems/unix-basics) you typed a command, waited, and got the prompt back. This session looks at what happens in between. Every command you start becomes a **process**: a running program with its own number, its own parent, and a state the kernel keeps track of. You can list processes, pause them, push them into the background and send them signals – and a script can decide what to do when a signal arrives.

The second half is about **filters**: small programs that read lines, change them and write lines. Connected with pipes, a handful of them answer questions that would take a page of code in Java. This is the style the practical test expects, so it is worth getting fluent in it now.

> **The model in one breath:** a running program is a process, the kernel shares the CPU between processes and passes them signals, and a pipe turns one process's output into the next one's input.

After this session you can

- Find a process and its parent with ps, and read the STAT column.
- Run a command in the background, stop it, and move it between foreground and background.
- End a process with the right signal – SIGTERM first, SIGKILL only as a last resort.
- Use trap to clean up temporary files when a script is interrupted.
- Build a pipeline from filters (find, grep, sort, tr, wc) and explain what each step reads and writes.

## Processes and PIDs

Unix and Linux are **multitasking** systems: many programs run at once, even on a machine with a single core. They do it by **time-sharing**. The kernel lets one process run for a few milliseconds, then switches to the next. The switching is so fast that it looks simultaneous (Fig. 1). With several cores, some processes really do run in parallel – one per core – but there are always far more processes than cores, so the switching never stops.

> **Fig. 1** · Time-sharing: the core runs one process per slice; because the slices are short, every process seems to run all the time.
>
> *Diagram:* One CPU core runs only one process at a time, but the kernel switches between processes every few milliseconds. To the user, processes A, B and C all seem to run at the same time.

To tell processes apart, the kernel gives each one a **PID** (process ID), an integer. Two copies of the same program – two people running `vim` on the server – are two processes with two PIDs. The first user-space process gets PID 1 (on most distributions it is `systemd`); every other process is started by an existing one, its **parent**, whose PID it remembers as its **PPID**. That makes all processes one big tree (Fig. 2).

> **Fig. 2** · When you log in over SSH, sshd starts your shell. Everything you type in that shell becomes its child.
>
> *Diagram:* Processes form a tree. systemd has PID 1 and starts the services, sshd starts your login shell, and every command you type becomes a child of that shell: its PPID is the shell's PID.

PIDs are not a counter that grows forever. When the numbers reach a maximum they wrap around, and the PID of a process that has ended can be handed out again. So a PID identifies a process only while it runs – don't save one in a file and trust it next week. The maximum is in a file under `/proc`, the directory where the kernel shows its process information as files:

```shell
cat /proc/sys/kernel/pid_max
```

- **process** – A running instance of a program: code, memory, open files and a state, managed by the kernel.
- **PID** – Process ID – the number the kernel gives a process while it runs.
- **PPID** – Parent process ID – the PID of the process that started this one.
- **kernel** – The core of the operating system: it schedules processes, manages memory and talks to the hardware.

## Looking at processes: ps, top, htop

`ps` takes a snapshot of the process list. Without options it shows only the processes of your current terminal – usually just your shell and `ps` itself:

```text
  PID TTY          TIME CMD
 4321 pts/3    00:00:00 bash
 4402 pts/3    00:00:00 ps
```

| Column | Meaning |
| --- | --- |
| `PID` | The process ID. |
| `TTY` | The terminal the process belongs to (`pts/3` is an SSH session; `?` means none). |
| `STAT` | The state of the process – see below. |
| `TIME` | CPU time used so far – not how long it has existed. |
| `CMD` | The command that started it. |

For everything on the machine use `ps aux`. It adds the owner, the CPU and memory share, the state and the start time. Combined with `grep`, it is the quickest way to find the PID of one program:

```shell
ps                          # the processes of this terminal
ps -f                       # full format: adds the owner (UID) and the parent (PPID)
ps aux                      # every process on the system, with CPU, memory and state
ps aux | grep sleep         # find the PID of one program
cat /proc/sys/kernel/pid_max   # the largest PID before the numbers wrap around
```

```text
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1 168000 11800 ?        Ss   08:00   0:12 /sbin/init
student   4321  0.0  0.0  10100  5200 pts/3    Ss   10:02   0:00 -bash
student   4398  0.0  0.0   5500   900 pts/3    S    10:05   0:00 sleep 300
student   4402  0.0  0.0  11800  3400 pts/3    R+   10:05   0:00 ps aux
```

Common mistake

**ps aux is not ps -aux**

`ps` accepts two option styles: BSD style without a dash (`ps aux`) and Unix style with one (`ps -ef`). `ps -aux` mixes them and means something else on paper – it asks for the processes of a user called "x". When no such user exists, Linux guesses what you meant – but don't rely on it. Write `ps aux`, and read `man ps` when you need more.

### The STAT column

The first letter of `STAT` is the state of the process. Most processes on a server are sleeping almost all the time – they wait for a key press, a network packet or a timer – and only a few are running at any moment (Fig. 3).

> **Fig. 3** · The states behind the STAT letters, and what moves a process from one to another.
>
> *Diagram:* The STAT letters of ps are process states. A running or runnable process (R) sleeps (S) while it waits for an event, waits in D for disk I/O, is stopped (T) by Ctrl+Z, and becomes a zombie (Z) when it exits until its parent collects the exit code.

| Letter | State | What it tells you |
| --- | --- | --- |
| `R` | running or runnable | Using a CPU now, or in the queue waiting for one. |
| `S` | interruptible sleep | Waiting for an event; a signal can wake it. The normal state. |
| `D` | uninterruptible sleep | Waiting for disk or network I/O; it does not react even to kill until the I/O ends. |
| `T` | stopped | Paused by Ctrl+Z or SIGSTOP, or held by a debugger. |
| `Z` | zombie | Finished, but the parent has not collected its exit code yet. It uses no CPU and no memory. |
| `X` | dead | Being removed – you should never see it. |

Extra characters after the letter add detail: `s` session leader (your login shell), `l` multi-threaded, `+` in the foreground of its terminal, `<` high priority, `N` low priority ("nice" to the others), `L` pages locked in memory. So `Ss` is a sleeping session leader, and `R+` is the `ps` you just ran.

### top and htop

`ps` is a photo; `top` is a live view that refreshes every few seconds. The header shows the load, the CPU and memory use of the whole machine; below it the processes are sorted by CPU use, so the one that makes the server slow is at the top. Press `q` to quit. `htop` shows the same in colour, with bars per CPU core, mouse support and scrolling – use it if the system has it installed.

## What a program does: time and strace

Write `time` before a command, and after the command ends you get three numbers:

```shell
time ls
time sleep 2
```

```text
$ time sleep 2

real    0m2.003s
user    0m0.001s
sys     0m0.002s
```

- **real** – wall-clock time from start to end, as a stopwatch would measure it.
- **user** – CPU time spent in the program's own code.
- **sys** – CPU time the kernel spent working for the program (opening files, writing output).

The `sleep 2` example is the interesting one: two seconds of real time, almost zero CPU. A sleeping process costs nothing – the S state at work. When `real` is much larger than `user + sys`, the program was waiting (for the disk, the network, you); when they are close, it was computing.

### strace: the conversation with the kernel

A program cannot open a file, read the keyboard or print to the screen by itself. Only the kernel touches the hardware, so the program asks it through **system calls**. `strace` runs a command and prints every system call it makes, with the arguments and the return value (Fig. 4). It is a debugging tool: when a program fails with an unclear message, the trace shows which file it was looking for and what the kernel answered.

> **Fig. 4** · ls asks the kernel to open the directory, read its entries, close it, and write the names to standard output (file descriptor 1).
>
> *Diagram:* A program cannot touch files or the screen itself. It asks the kernel with system calls – open the directory, read its entries, close it, write the text to the terminal – and strace prints every one of these requests with its result.

```shell
strace ls                       # every system call ls makes, on the terminal
strace -o trace.txt ls          # the same, written to trace.txt
strace -e trace=openat ls       # only one kind of call
```

strace ls – the lines that matter

```text
execve("/usr/bin/ls", ["ls"], 0x7ffd5e1c /* 24 vars */) = 0
...
openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
getdents64(3, /* 5 entries */, 32768)   = 144
getdents64(3, /* 0 entries */, 32768)   = 0
close(3)                                = 0
write(1, "a.txt  b.txt  scripts\n", 22)  = 22
exit_group(0)                           = ?
```

| System call | What the program asks for |
| --- | --- |
| `open` | Open a file; `openat` is the modern form. Returns a file descriptor – a small number. |
| `read` | Read bytes from a file descriptor. |
| `write` | Write bytes to a file descriptor (`1` is standard output, `2` standard error). |
| `close` | Close a file descriptor. |
| `stat` | Information about a file: size, owner, permissions (`lstat` for links). |
| `access` | Check whether the process may read, write or run a file. |
| `ioctl` | Control a device, for example ask the terminal how wide it is. |
| `mmap` | Map a file or memory into the process's address space, so it can be used like ordinary memory. |

Good to know

**Errors in the trace**

A failed call ends in `= -1` and an error name, such as `ENOENT (No such file or directory)` or `EACCES (Permission denied)`. Searching the trace for `= -1` is often the fastest way to find why a program does not start.

## Foreground and background

By default a command runs in the **foreground**: the shell waits for it, you get no prompt, and what you type – including Ctrl+C – goes to that command. Put an `&` at the end and it runs in the **background**: the shell starts it and gives you the prompt back at once.

The shell calls each command it manages a **job** and numbers them per terminal. When you start a background job it prints two numbers: `[1]` is the **job number**, valid only in this shell, and the second is the **PID**, valid in the whole system. Job commands take `%1`; `kill` and `ps` take the PID (and `kill` accepts `%1` as well).

a terminal session – $ marks what you type

```text
$ sleep 300 &
[1] 4398
$ sleep 400
^Z
[2]+  Stopped                 sleep 400
$ jobs
[1]-  Running                 sleep 300 &
[2]+  Stopped                 sleep 400
$ bg %2
[2]+ sleep 400 &
$ fg %1
sleep 300
```

> **Fig. 5** · Job control: Ctrl+Z, bg and fg move a job between the three states; & starts it straight in the background.
>
> *Diagram:* A job is in the foreground, stopped or in the background. Ctrl+Z stops the foreground job, bg lets a stopped job continue in the background, fg brings a job back to the foreground, and a trailing & starts a command in the background.

| You type | Effect |
| --- | --- |
| `command &` | Start in the background; the prompt returns immediately. |
| Ctrl+Z | Stop (pause) the foreground job. It stays in memory and does nothing. |
| `jobs` | List this shell's jobs. `+` marks the current job (what a bare `fg` or `bg` uses). |
| `bg %2` | Let stopped job 2 continue in the background. |
| `fg %1` | Bring job 1 to the foreground; the shell waits for it again. |

Common mistake

**Ctrl+Z is not Ctrl+C**

Ctrl+Z only pauses. The job is still there, holding its memory and any files it has open. If you log out with stopped jobs, bash warns "There are stopped jobs". Run `jobs` and either finish them with `fg` or end them with `kill %1`.

Good to know

**A background job still writes to your terminal**

`&` frees the prompt, not the screen: a background job's output still appears in the middle of whatever you are typing. Redirect it to a file when it is chatty – redirection is covered properly in [the next session](https://laszlonemes.com/university/computer-systems/shell-scripts).

## Signals and kill

A **signal** is a short notification the kernel delivers to a process: something happened, or the process should do something. Signals come from the keyboard (Ctrl+C), from other processes (`kill`), or from the kernel itself (a division by zero, a timer that expired). Each one has a name and a number; `kill -l` lists them all.

| Signal | Number | Meaning |
| --- | --- | --- |
| `SIGHUP` | 1 | The terminal connection was closed (hang-up). Many servers reload their configuration on it. |
| `SIGINT` | 2 | Interrupt – what Ctrl+C sends. |
| `SIGQUIT` | 3 | Quit, and leave a core dump – what Ctrl+\ sends. |
| `SIGFPE` | 8 | An invalid arithmetic operation, such as an integer division by zero. |
| `SIGKILL` | 9 | Kill now. The process cannot catch or ignore it. |
| `SIGALRM` | 14 | A timer set by the process has expired. |
| `SIGTERM` | 15 | Please terminate. The default of kill; the process can clean up first. |

Every signal has a **default action** – for most of them, ending the process. A program may install its own handler instead, or ignore a signal. Two signals are special: `SIGKILL` and `SIGSTOP` never reach the program's code. The kernel acts on them directly, which is exactly why they always work (Fig. 6).

> **Fig. 6** · What happens to a signal: a handler, the default action, or nothing – except for SIGKILL and SIGSTOP, which the kernel carries out itself.
>
> *Diagram:* A signal goes through the kernel to the process. If the process set a trap, its handler runs; otherwise the default action happens, usually ending it; it can also ignore the signal. SIGKILL and SIGSTOP cannot be caught or ignored – the kernel acts directly.

The command for sending signals is called `kill`, for historical reasons – most signals it sends do not have to kill anything. The syntax is `kill -signal PID`, and without a signal it sends `SIGTERM`:

```shell
kill -l               # every signal this system knows
kill 4398             # SIGTERM (15): "please terminate" – the polite default
kill -15 4398         # the same, by number
kill -TERM 4398       # the same, by name
kill -1 4398          # SIGHUP
kill -9 4398          # SIGKILL: the kernel ends it, no cleanup – the last resort
kill %1               # a job number works as well as a PID
```

Common mistake

**Reaching for kill -9 first**

`SIGKILL` gives the process no chance to finish writing a file, delete its temporary files or tell its children. Send `SIGTERM` (plain `kill PID`) first, wait a moment, and use `kill -9` only if the process ignores it. Also: you can signal only your own processes; other users' need root.

Good to know

**Ctrl+D is not a signal**

Ctrl+D sends nothing to the process. It tells the terminal that the input has ended (EOF – end of file). A program reading the keyboard – `cat`, `sort`, the shell itself – sees "no more lines" and finishes normally. That is why Ctrl+D at an empty prompt logs you out.

## trap: a script that reacts to signals

A shell script is a process too, so it receives signals. With the built-in `trap` you say what the shell should run when a given signal arrives, instead of the default action. The command goes in single quotes; the signal can be given by name (`INT`, `SIGINT`) or by number (`2`):

```shell
trap 'echo ELTE!' INT        # Ctrl+C now prints ELTE! instead of ending the shell
trap '' INT                  # ignore SIGINT completely
trap - INT                   # back to the default action (same as: trap - 2)
```

The practical use is cleaning up. A script that writes a temporary file should delete it even when someone presses Ctrl+C halfway through. `$$` expands to the PID of the running shell, which makes a file name that no other copy of the script will use at the same time:

cleanup on interrupt – the lines that matter

```shell
tmp="/tmp/report.$$"                    # $$ = the PID of this shell: a unique name
trap 'rm -f "$tmp"; exit 1' INT TERM    # Ctrl+C or kill: delete the file, then stop

sort data.txt > "$tmp"
# ... work with "$tmp" ...
rm -f "$tmp"
```

Note the `exit` inside the trap. Without it the handler would run, and then the script would carry on from where it was interrupted – the signal has been "handled", so nothing ends it.

In your assignment

**Temporary files in your shell script**

If your assignment script needs an intermediate file, name it with `$$`, set the trap right after creating the name, and delete the file at the normal end as well. A script that leaves junk in `/tmp` or in the user's folder after Ctrl+C is sloppy, and the fix is two lines.

## Midnight Commander

`mc` is a two-panel file manager that runs inside the terminal, in the tradition of Norton Commander. Each panel shows a directory; you copy, move, view and edit files between them with the function keys. It is handy for looking around an unfamiliar directory tree on the server, and a panel can also show a remote file system.

```shell
mc            # start Midnight Commander
# Tab         switch between the two panels
# F3 / F4     view / edit the selected file
# F5 / F6     copy / move to the other panel
# F10         quit
```

On your own Debian or Ubuntu machine it installs with `sudo apt install mc`. On the course server you have no `sudo` – use it if it is there, and don't let it replace the commands: the test is about commands and scripts.

## Filters and pipes

A **filter** is a program that reads lines from its standard input, does one thing to them, and writes the result to its standard output: `grep` keeps some lines, `sort` reorders them, `tr` changes characters, `wc` counts. Most filters also accept a file name, but the point is that they work on a stream.

The pipe `|` connects two processes: the standard output of the left one becomes the standard input of the right one. Both run at the same time, and the lines flow through as they are produced. Error messages go to a third stream, standard error, which the pipe does not carry (Fig. 7). The full story of the three streams and redirection is in [the next session](https://laszlonemes.com/university/computer-systems/shell-scripts); here you need only `|`, plus `<` (read standard input from a file) and `>` (write standard output to a file).

> **Fig. 7** · A pipeline is a chain of processes. Data flows left to right through stdout and stdin; stderr goes straight to the terminal.
>
> *Diagram:* In find . -type d | grep -v temp | wc -l each pipe connects the standard output of the command on its left to the standard input of the command on its right. Standard error is not piped: error messages still go to the terminal.

### Counting and listing

`wc -l` counts lines, so `ls | wc -l` counts the entries of the current directory: when its output goes into a pipe, `ls` prints one name per line. `find . -type d` lists every directory under the current one, recursively; `grep -v` inverts the match and keeps the lines that do *not* contain the pattern. `who` lists the logged-in sessions, one per line, with the user name in the first column.

### sort: text or numbers

`sort` orders lines alphabetically, `sort -r` in reverse. "Alphabetically" means character by character, which surprises everyone the first time they sort numbers (Fig. 8). Add `-n` to compare the values as numbers.

> **Fig. 8** · The same four lines, sorted as text and as numbers. As text, 10 comes first because '1' comes before '2'.
>
> *Diagram:* sort compares lines as text, character by character, so 10 comes before 2 and the file looks unsorted. sort -n compares the values as numbers and gives 2, 4, 10, 30.

### tr: replacing characters

`tr` translates characters: every character of the first set becomes the matching character of the second. It reads only standard input – it takes no file name – so the file goes in with `<` and the result comes out with `>`:

```shell
tr '[:lower:]' '[:upper:]' < file.txt > output.txt
```

`tr` never modifies `file.txt`. It writes a new stream, and the shell puts that stream into `output.txt`.

Common mistake

**Redirecting into the file you are reading**

`tr … < file.txt > file.txt` leaves you with an empty file. The shell sets up the redirections before the command starts, and `>` empties `file.txt` first – so `tr` reads nothing. Write to a different file, then rename it with `mv`.

### grep: lines, matches and whole lines

`grep` keeps the lines that contain a pattern. Three options change what is counted, and mixing them up gives a wrong number that looks right. With this `fruits.txt`:

fruits.txt

```text
apple pear apple
pear apple
apple
```

```shell
grep -cx 'apple' fruits.txt            # 1 – lines that are exactly "apple"
grep -c 'apple' fruits.txt             # 3 – lines that contain "apple"
grep -o 'apple' fruits.txt | wc -l     # 4 – every occurrence, one per output line
```

`-x` requires the *whole line* to match, `-c` counts matching lines, and `-o` prints each match on its own line – the way to count occurrences rather than lines. Read the task: "how many lines are exactly apple" is `-cx`, "how many times does apple appear" is `-o | wc -l`.

A pattern can be a **regular expression**. With `-E`, `^` means the start of the line, `$` the end, `[0-9]` one digit, `+` one or more of the previous item, and `?` an optional item. So these keep the lines that are integers:

```shell
grep -E '^[0-9]+$' numbers.txt          # 0, 12, 123
grep -E '^-?[0-9]+$' numbers.txt        # ... and -5, -42
grep -E '^[+-]?[0-9]+$' numbers.txt     # ... and +10
```

Without `^` and `$`, `10a` would match too – it contains digits. Regular expressions get a full treatment in [the next session](https://laszlonemes.com/university/computer-systems/shell-scripts); to experiment, paste a pattern into a regex tester such as regex101.com.

In your assignment

**Build the pipeline one step at a time**

In the practical test, don't type a five-command pipeline at once. Run the first command, look at the output, add `| next-command`, look again. When the result is wrong you know exactly which step broke it – and the same pipeline then goes into your script unchanged.

## Practice

Do these on the course server. The first three are about processes and signals, the rest are filters. Each filter task is a one-liner – the hint shows it, but try first, and check the output.

Task 1

**Find and stop a background process**

Start `sleep 300` in the background. Find its PID in two different ways, then end it with a polite signal and check that it is gone.

**Approach:**

The shell prints the PID when it starts the job; `ps aux | grep` is the other way. Plain `kill` sends SIGTERM. The `grep` also finds itself in the list – ignore that line.

```shell
sleep 300 &              # the shell prints [1] and the PID
ps aux | grep 'sleep 300'
kill 4398                # use the PID you saw – SIGTERM
jobs                     # [1]+  Terminated   sleep 300
```

Task 2

**A tour of job control**

Start `sleep 400` in the foreground. Stop it, list your jobs, continue it in the background, bring it back to the foreground and end it from the keyboard. Watch the `STAT` column with `ps` from a second terminal while you do it: which letter does it show at each step?

**Approach:**

Ctrl+Z, then the three job commands. Stopped shows T, running in the background S (it is sleeping).

```shell
sleep 400                # foreground – then press Ctrl+Z
jobs                     # [1]+  Stopped
bg %1                    # continues in the background
fg %1                    # back in front – now Ctrl+C ends it
```

Task 3

**Catch Ctrl+C**

Make Ctrl+C print `ELTE!` instead of interrupting. Test it on a `sleep`, then restore the default behaviour and test again.

**Approach:**

`trap` with the command in single quotes and the signal name; `trap -` resets it.

```shell
trap 'echo ELTE!' INT
sleep 30                 # press Ctrl+C: ELTE! appears
trap - INT
```

Task 4

**Count the entries of the current directory**

Print one number: how many files and directories are in the current directory.

**Approach:**

`ls` into `wc -l`. This is a teaching answer: `ls` hides dot files, and a file name with a line break in it would count twice. `ls -A` includes hidden entries.

```shell
ls | wc -l
ls -A | wc -l            # hidden entries too, without . and ..
```

Task 5

**List the subdirectories**

List every directory under the current one, but leave out hidden paths (anything with a component starting with a dot) and anything with `temp` in its name.

**Approach:**

`find -type d` lists directories; `-not -path '*/.*'` drops hidden paths from the output (find still walks into them), and `grep -v` removes the `temp` lines. Put a directory name instead of `.` to search somewhere else.

```shell
find . -type d -not -path '*/.*' | grep -v 'temp'
```

Task 6

**Who is logged in – names only**

List the user names of the people logged in to the server, and only the names.

**Approach:**

`who` prints one line per session; the name is the first space-separated field. `cut` keeps a field, `sort -u` keeps each name once (one user can have several sessions).

```shell
who
who | cut -d ' ' -f 1 | sort -u     # first column only, each name once
```

Task 7

**Sort a file**

Print the lines of a text file in alphabetical order, then in reverse. Then make a `numbers.txt` with the lines below and sort it so that the result is 2, 4, 10, 30.

numbers.txt

```text
10
2
30
4
```

**Approach:**

Plain `sort` gives 10, 2, 30, 4 – text order. You need numeric order.

```shell
sort file.txt
sort -r file.txt
sort -n numbers.txt
```

Task 8

**Uppercase a file**

Replace every lowercase letter of a file with its uppercase pair and save the result in a new file.

**Approach:**

`tr` with the two character classes, input with `<`, output with `>`. Accented letters may stay as they are, depending on the system's locale.

```shell
tr '[:lower:]' '[:upper:]' < file.txt > output.txt
cat output.txt
```

Task 9

**Lines that are exactly “apple”**

Count the lines of `fruits.txt` that contain the word `apple` and nothing else. With the example file above the answer is 1.

**Approach:**

Whole-line match plus count. `grep -x 'apple' fruits.txt | wc -l` works too, in two steps. `grep -o … | wc -l` is wrong here: it counts occurrences (4), not lines.

```shell
grep -cx 'apple' fruits.txt
```

Task 10

**Keep only the integers**

From a `numbers.txt`, print only the lines that are whole numbers, and save them in `filtered.txt`. Then extend the pattern to accept negative numbers, and finally an optional `+` sign. Test it on these lines:

numbers.txt

```text
0
12
-5
+10
12.5
abc
10a
```

**Approach:**

Anchor both ends so nothing else fits on the line: `^[0-9]+$`. For the sign, an optional `-` is `-?`, an optional `+` or `-` is `[+-]?`. `12.5`, `abc` and `10a` never match.

```shell
grep -E '^[0-9]+$' numbers.txt > filtered.txt
```

## Summary

- A running program is a process with a PID and a parent (PPID); the processes form a tree rooted at PID 1.
- `ps aux` lists everything, `top`/`htop` watch it live; STAT tells you whether a process runs (R), waits (S, D), is stopped (T) or a zombie (Z).
- `time` separates waiting from computing; `strace` shows the system calls a program makes.
- `&`, Ctrl+Z, `jobs`, `bg` and `fg` move jobs between foreground, stopped and background. Job numbers are per shell, PIDs are system-wide.
- Signals are sent with `kill`: SIGTERM first, SIGKILL last. Ctrl+C is SIGINT; Ctrl+D is end of input, not a signal.
- `trap` runs your command when a signal arrives – use it to delete temporary files named with `$$`.
- Filters read stdin and write stdout; `|` chains them. `sort -n` for numbers, `grep -x` for whole lines, `grep -o` to count occurrences.
