# Process, OS and CLI Tools

Course: [Node.js](https://www.devtutorials.ai/learn/nodejs) · Section: System and Files
Source: https://www.devtutorials.ai/learn/nodejs/system-files/process-os-and-cli-tools

> The process object (argv, env, platform, arch, version, pid, cwd, uptime and process events), environment files with --env-file and process.loadEnvFile, standard input, output and error, TTY detection and shell piping, exit codes, process.exit versus process.exitCode, the exit and beforeExit events, signals such as SIGINT and SIGTERM, the os module, and building command-line tools with a shebang, the bin field, util.parseArgs, help output, util.styleText and npm link.

## Process, OS and CLI Tools

**Process** = one running program. Every time you type `node app.js`, the computer starts a new process.

- The `process` object describes your running program.
- It holds the arguments, the settings and the input and output channels.
- The `os` module describes the computer the program runs on.
- With both, you can build your own command-line tools.

**Simple lesson:** `process` is about your program, and `os` is about the computer.

### A Real-World Example

Think of the school's exam-paper printing room.

- The clerk gets an order slip: "Class 8, Maths, 40 copies". These are the **arguments**.
- A notice on the wall says "Use A4 paper". This is an **environment setting**.
- Papers come out of the printer tray. This is the **output**.
- If the paper runs out, the clerk writes "FAILED" on the slip. This is the **exit code**.
- If the principal shouts "Stop!", the clerk finishes the current page and switches off. This is a **signal**.

## What You Will Learn

In this lesson, you will learn:

1. `process.argv`, `process.env` and other program facts
2. `.env` files with `--env-file` and `process.loadEnvFile()`
3. `stdin`, `stdout`, `stderr` and shell piping
4. Exit codes, `process.exit()` and `process.exitCode`
5. The `exit` and `beforeExit` events
6. Signals such as SIGINT and SIGTERM
7. The `os` module
8. Building a CLI tool: shebang, `bin`, `parseArgs`, help output and colours

## The process Object

- **`process.argv`** = the words of the command, as text.
- **`process.env`** = the environment variables, as text.
- **`process.platform`** and **`process.arch`** = the operating system and the processor type.
- **`process.version`** and **`process.versions`** = the Node.js version, and the versions of V8, libuv and more.
- **`process.pid`** = the process id number given by the operating system.
- **`process.cwd()`** = the folder the program was started from. **`process.chdir(dir)`** changes it.
- **`process.uptime()`** = how many seconds the program has been running.
- **`process.on(event, fn)`** = listen to process events, such as `exit` or `SIGINT`.

### Environment Variables

**Environment variable** = a named setting given to a program from outside, such as `PORT=3000`.

- Read them with `process.env.NAME`.
- They are always text, or `undefined` when missing.
- Give a default with `??`, and turn numbers into numbers with `Number()`.
- Keep secrets, like passwords, in environment variables, never in code.

Add `.env` to `.gitignore` before your first commit. A password in Git history stays there even after you delete the file.

### .env Files

**.env file** = a plain text file of `NAME=value` lines, one setting per line.

- `node --env-file=.env app.js` loads it before the program starts.
- `process.loadEnvFile(".env")` loads it from inside the program.
- Add `.env` to `.gitignore`. Commit a `.env.example` with the names but no real secrets.

**Simple lesson:** read settings from `process.env`, give them defaults, and load local ones from `.env`.

## Standard Streams

Every process has three channels.

- **stdin** (standard input) = text coming in, from the keyboard or from another program.
- **stdout** (standard output) = normal output. `console.log` writes here.
- **stderr** (standard error) = error output. `console.error` writes here.
- **`process.stdout.write(text)`** = write text without adding a new line.

### Piping and Redirecting in the Shell

- `node app.js > report.txt` = send stdout into a file.
- `node app.js 2> errors.txt` = send stderr into a file.
- `node app.js < names.txt` = feed a file into stdin.
- `cat names.txt | node app.js` = feed another program's output into stdin.
- Because errors go to stderr, `> report.txt` keeps them out of the report.

### TTY Detection

**TTY** = a real terminal window that a person types into.

- `process.stdout.isTTY` is `true` when a person sees the output in a terminal.
- It is `undefined` when output goes to a file or a pipe.
- Tools use this to add colours only for people, not for files. In this lab, output is not a TTY.

**Simple lesson:** stdout for results, stderr for problems, stdin for input from other programs.

<figure className="ngc-fig node-fig">
<svg viewBox="0 0 560 170" role="img" ariaLabelledby="fig-node-stdio" style={{ width: "100%", maxWidth: 560, height: "auto" }}>
<title id="fig-node-stdio">Animation: text flows in through stdin, normal output leaves through stdout, and errors leave through stderr</title>
<rect x="20" y="60" width="120" height="50" rx="10"  style={{ fill: "var(--ngc-blue-soft)", stroke: "var(--ngc-blue)" }}/><text x="80" y="82" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>stdin</text><text x="80" y="99" textAnchor="middle" style={{ fill: "var(--ink)", fontFamily: "var(--sans)", fontSize: 11 }}>keyboard or file</text>
<rect x="210" y="50" width="140" height="70" rx="12"  style={{ fill: "var(--ngc-purple-soft)", stroke: "var(--ngc-purple)", strokeWidth: 1.5 }}/><text x="280" y="82" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>node app.js</text><text x="280" y="100" textAnchor="middle" style={{ fill: "var(--ink)", fontFamily: "var(--sans)", fontSize: 11 }}>your program</text>
<rect x="420" y="20" width="120" height="44" rx="10"  style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)" }}/><text x="480" y="40" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>stdout</text><text x="480" y="56" textAnchor="middle" style={{ fill: "var(--ink)", fontFamily: "var(--sans)", fontSize: 11 }}>console.log</text>
<rect x="420" y="106" width="120" height="44" rx="10"  style={{ fill: "var(--ngc-red-soft)", stroke: "var(--ngc-red)" }}/><text x="480" y="126" textAnchor="middle" style={{ fill: "var(--ngc-red)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>stderr</text><text x="480" y="142" textAnchor="middle" style={{ fill: "var(--ink)", fontFamily: "var(--sans)", fontSize: 11 }}>console.error</text>
<g style={{ stroke: "var(--ink)", strokeDasharray: "4 4" }}><line x1="140" y1="85" x2="210" y2="85" /><line x1="350" y1="75" x2="420" y2="42" /><line x1="350" y1="95" x2="420" y2="128" />
</g>
<circle r="6" cx="140" cy="85" style={{ fill: "var(--ngc-blue)" }}><animate attributeName="cx" values="140;210;210" keyTimes="0;0.3;1" dur="6s" repeatCount="indefinite" /></circle>
<circle r="6" cx="350" cy="75" opacity="0" style={{ fill: "var(--ngc-green)" }}><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.35;0.36;0.6;0.61" dur="6s" repeatCount="indefinite" /><animate attributeName="cx" values="350;350;420;420" keyTimes="0;0.35;0.6;1" dur="6s" repeatCount="indefinite" /><animate attributeName="cy" values="75;75;42;42" keyTimes="0;0.35;0.6;1" dur="6s" repeatCount="indefinite" /></circle>
<circle r="6" cx="350" cy="95" opacity="0" style={{ fill: "var(--ngc-red)" }}><animate attributeName="opacity" values="0;0;1;1;0" keyTimes="0;0.65;0.66;0.95;0.96" dur="6s" repeatCount="indefinite" /><animate attributeName="cx" values="350;350;420;420" keyTimes="0;0.65;0.9;1" dur="6s" repeatCount="indefinite" /><animate attributeName="cy" values="95;95;128;128" keyTimes="0;0.65;0.9;1" dur="6s" repeatCount="indefinite" /></circle>
</svg>
<figcaption>Every program has three streams: one in, two out.</figcaption>
</figure>

## Exit Handling

**Exit code** = a number the process gives back when it ends.

- **0** = success.
- **Any other number** = failure. 1 is the usual "something went wrong".
- Shells and build servers read the exit code to decide what to do next.

### process.exit() vs process.exitCode

- **`process.exit(1)`** = stop right now. Waiting timers, writes and network calls are cut off.
- **`process.exitCode = 1`** = remember the code, and let the program finish its work first.
- Prefer `process.exitCode`. Use `process.exit` only when you must stop at once.

Shells read the exit code. `node check.js && node deploy.js` runs the deploy only when the check exits with 0.

### The exit and beforeExit Events

- **`beforeExit`** = the event loop is empty. You may still start more async work here.
- **`exit`** = the process is ending. Only sync code runs here.
- `beforeExit` does not fire after `process.exit()` or a crash.

**Simple lesson:** exit code 0 means success, and `process.exitCode` is safer than `process.exit()`.

## Signals

**Signal** = a short message from the operating system to a process.

- **SIGINT** = "interrupt". Sent when you press **Ctrl + C**.
- **SIGTERM** = "please stop". Sent by process managers and containers when they stop a program.
- **SIGKILL** = "stop now". It cannot be caught.
- Listen with `process.on("SIGINT", fn)`. Then Node.js no longer exits by itself. You must call `process.exit()`.
- **`process.kill(pid, "SIGTERM")`** = send a signal to a process.

<figure className="ngc-fig node-fig">
<svg viewBox="0 0 560 170" role="img" ariaLabelledby="fig-node-signal" style={{ width: "100%", maxWidth: 560, height: "auto" }}>
<title id="fig-node-signal">Animation: pressing Ctrl + C sends SIGINT, the program saves its work and then exits with code 0</title>
<rect x="20" y="60" width="110" height="50" rx="10" opacity="0.12"  style={{ fill: "var(--ngc-slate)", stroke: "var(--ngc-slate)" }}/><text x="75" y="90" textAnchor="middle" style={{ fill: "var(--ink)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>Ctrl + C</text>
<rect x="190" y="60" width="140" height="50" rx="10"  style={{ fill: "var(--ngc-orange-soft)", stroke: "var(--ngc-orange)", strokeWidth: 1.5 }}/><text x="260" y="82" textAnchor="middle" style={{ fill: "var(--ngc-orange)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>SIGINT handler</text><text x="260" y="100" textAnchor="middle" style={{ fill: "var(--ink)", fontFamily: "var(--sans)", fontSize: 11 }}>save attendance</text>
<rect x="390" y="60" width="150" height="50" rx="10"  style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)", strokeWidth: 1.5 }}/><text x="465" y="90" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>exit(0), clean</text>
<line x1="130" y1="85" x2="190" y2="85"  style={{ stroke: "var(--ngc-slate)", strokeWidth: 2 }}/><line x1="330" y1="85" x2="390" y2="85"  style={{ stroke: "var(--ngc-slate)", strokeWidth: 2 }}/>
<circle r="7" cx="130" cy="85" style={{ fill: "var(--ngc-orange)" }}><animate attributeName="cx" values="130;190;260;330;390;465" keyTimes="0;0.15;0.45;0.6;0.75;1" dur="5s" repeatCount="indefinite" /></circle>
<text x="280" y="150" textAnchor="middle" style={{ fill: "var(--muted)", fontFamily: "var(--sans)", fontSize: 12 }}>Without a handler, Node.js stops at once and the work is lost</text>
</svg>
<figcaption>A signal handler gives the program a moment to clean up.</figcaption>
</figure>

Click **▶ Run**, then press **Press Ctrl + C** in the panel on the right.

- **Without the handler,** the program would stop at once with exit code 130.
- **With the handler,** it saves its work and chooses its own exit code.

**Simple lesson:** catch SIGINT and SIGTERM to save work, then exit yourself.

## The os Module

`node:os` tells you about the computer.

- **`os.platform()`**, **`os.arch()`** = the same as `process.platform` and `process.arch`.
- **`os.cpus()`** = one entry per processor core.
- **`os.totalmem()`** and **`os.freemem()`** = memory in bytes.
- **`os.homedir()`**, **`os.tmpdir()`**, **`os.hostname()`** = the home folder, the temp folder and the computer's name.
- **`os.networkInterfaces()`** = the network cards and their addresses.
- **`os.EOL`** = the end-of-line text: `\n` on Mac and Linux, `\r\n` on Windows.

The lab shows values of a pretend computer. On your own computer, the numbers will be yours.

**Simple lesson:** use `os` to adapt your program to the computer it runs on.

## Building CLI Tools

**CLI tool** = a program you run from the terminal by name, such as `npm` or `git`.

1. **Shebang line** = `#!/usr/bin/env node` as the first line. It tells Mac and Linux to run the file with Node.js.
2. **Executable file** = run `chmod +x report.js` on Mac and Linux. Windows uses the `bin` field instead.
3. **The `bin` field** = in `package.json`, maps a command name to your file: `"bin": { "report-card": "./report.js" }`.
4. **`npm link`** = installs your tool on your own computer, so you can type `report-card` anywhere while you build it.

### Parsing Arguments with util.parseArgs()

- **`options`** = the flags your tool knows. `short` gives a one-letter form, such as `-c 8`.
- **`positionals`** = the plain words, such as student names.
- **Help output** = show usage when asked with `--help`, or when input is missing.
- **`styleText("bold", text)`** = colours and styles for the terminal. It skips them when `NO_COLOR` is set or output is not a TTY.
- **Unknown flags** = `parseArgs` throws `ERR_PARSE_ARGS_UNKNOWN_OPTION`, which protects you from typos.

Always print help for `--help`, and when required input is missing. It is the first thing a new user tries.

**Simple lesson:** a shebang, a `bin` entry and `parseArgs` turn a script into a real command.

## Common mistakes

<div className="tscroll">

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| `process.env.PORT + 1` | Environment values are numbers | They are text. Use `Number()` | `"3000" + 1` gives `"30001"` |
| Committing `.env` to Git | It is just a settings file | Commit `.env.example` only | Passwords leak |
| Calling `process.exit()` right after writing output | Exit waits for writes | Set `process.exitCode` instead | The last lines may be lost |
| Printing errors with `console.log` | Output is output | Use `console.error` for problems | Errors end up inside the saved report |
| A SIGINT handler that never exits | Node.js still stops by itself | Call `process.exit()` in the handler | Ctrl + C does nothing |
| Reading `process.argv[0]` as the first word | Your words start at 0 | They start at index 2 | You get the path to Node.js |

</div>

## TL;DR

1. **`process.argv`** = command words. Your own start at index 2.
2. **`process.env`** = settings as text. Load local ones with `--env-file=.env`.
3. **stdin, stdout, stderr** = input, output and error channels.
4. **Exit code** = 0 is success, anything else is failure.
   - prefer `process.exitCode = 1` over `process.exit(1)`
5. **`beforeExit`** = loop is empty. **`exit`** = sync goodbye.
6. **SIGINT** = Ctrl + C. **SIGTERM** = polite stop. Handle them to clean up.
7. **`os`** = facts about the computer.
8. **CLI tool** = shebang + `bin` + `parseArgs` + help output.

## Check Yourself

## Exercises

## What to Remember

- `process` describes your program. `os` describes the computer.
- Arguments and environment variables are always text.
- Keep secrets in `.env` locally, and never commit them.
- Results go to stdout, problems go to stderr.
- Exit code 0 means success. Prefer `process.exitCode` to `process.exit()`.
- Handle SIGINT and SIGTERM to finish cleanly.
- `parseArgs`, a shebang and a `bin` entry make a real CLI tool.

Next: **Path and File System**. You will build file paths safely and read, write and organise files.
