# Peiosutils

> The Peios core utilities — the everyday commands, reworked for the Peios security model.

---

# Files and directories

_Peios / Using Peios / Peiosutils / Files and directories_

> The commands that create, copy, move, remove, and resize files and directories — and how each one handles a file's security descriptor.

This topic covers the commands that *change* the file system: creating files and directories, copying and moving them, removing them, and adjusting their size. If a command brings a file into existence, takes one out of it, or duplicates one, it is here.

This page names every command, and then explains the one theme that runs through the whole topic on Peios: what happens to a file's **security descriptor** when the file is created or copied.

## The commands

| Command | Purpose |
|---|---|
| [`cp`](/peios/using-peios/peiosutils/files-and-directories/cp.md) | Copy files and directories. |
| [`mv`](/peios/using-peios/peiosutils/files-and-directories/mv.md) | Move or rename files and directories. |
| [`rm`](/peios/using-peios/peiosutils/files-and-directories/rm.md) | Remove files, and with `-r`, directories. |
| [`rmdir`](/peios/using-peios/peiosutils/files-and-directories/rmdir.md) | Remove directories, but only empty ones. |
| [`mkdir`](/peios/using-peios/peiosutils/files-and-directories/mkdir.md) | Create directories. |
| [`mkfifo`](/peios/using-peios/peiosutils/files-and-directories/mkfifo.md) | Create named pipes (FIFOs). |
| [`mknod`](/peios/using-peios/peiosutils/files-and-directories/mknod.md) | Create special files — device nodes and FIFOs. |
| [`touch`](/peios/using-peios/peiosutils/files-and-directories/touch.md) | Create empty files, or update a file's timestamps. |
| [`ln`](/peios/using-peios/peiosutils/files-and-directories/ln.md) | Create hard links and symbolic links. |
| [`link` and `unlink`](/peios/using-peios/peiosutils/files-and-directories/link-and-unlink.md) | The single-purpose, low-level versions of "make a hard link" and "remove a file". |
| [`shred`](/peios/using-peios/peiosutils/files-and-directories/shred.md) | Destroy a file's contents by overwriting them, so they cannot be recovered. |
| [`dd`](/peios/using-peios/peiosutils/files-and-directories/dd.md) | Copy data block by block, converting it on the way. |
| [`df`](/peios/using-peios/peiosutils/files-and-directories/df.md) | Report how much space each file system has, and how much is free. |
| [`du`](/peios/using-peios/peiosutils/files-and-directories/du.md) | Report how much space files and directories use. |
| [`truncate`](/peios/using-peios/peiosutils/files-and-directories/truncate.md) | Shrink or extend a file to an exact size. |
| [`mktemp`](/peios/using-peios/peiosutils/files-and-directories/mktemp.md) | Create a temporary file or directory with a safely unique name. |

## Every file has a security descriptor

On Peios, every file and directory carries a **security descriptor** — the record of who owns it and who may do what to it. Access to a file is decided from its descriptor; see [Security descriptors](/peios/security-fundamentals/security-descriptors/overview.md) for the full picture.

That fact shapes two groups of commands in this topic.

### Creating a file gives it a descriptor

When `mkdir`, `mkfifo`, `mknod`, or `touch` creates a new object, that object needs a security descriptor. By default it gets one **inherited from the directory it is created in** — the new object picks up the access rules its parent directory hands down.

All four of these commands share one set of options — the **creation flags** — that let you set the new object's descriptor explicitly instead of taking the inherited default: name an owner, set a different group, lock the object so it does *not* inherit, give it an integrity label, or supply a complete descriptor. The flags are documented in full on the [`mkdir`](/peios/using-peios/peiosutils/files-and-directories/mkdir.md) page; `mkfifo`, `mknod`, and `touch` each link back to it.

### Copying a file makes a fresh descriptor — unless you preserve

`cp` creates new files, and a cross-file-system `mv` does too. A brand-new copy gets a brand-new descriptor inherited from its destination directory — it does **not** carry the source file's security across by default.

When you *do* want the copy to keep the source's owner, access rules, timestamps, or other attributes, that is the job of the `--preserve` family of options. `cp` and `mv` share an identical `--preserve` surface; it is documented on the [`cp`](/peios/using-peios/peiosutils/files-and-directories/cp.md) page.

### Removing a file checks whether you may write it

`rm` and `shred` both make a decision based on whether you can write a file. `rm` prompts before removing a file you cannot write to; `shred`'s `--force` overrides a file's protection so it can be destroyed. Both judge "can you write this?" by a live access check against the file's security descriptor — not by any decorative metadata on the inode. Each command's page explains exactly where that check is made.

## Where to start

For copying — and for the whole `--preserve` model — read [`cp`](/peios/using-peios/peiosutils/files-and-directories/cp.md).

For creating files and directories, and the shared creation flags, read [`mkdir`](/peios/using-peios/peiosutils/files-and-directories/mkdir.md).

For removing things safely, read [`rm`](/peios/using-peios/peiosutils/files-and-directories/rm.md).

---

# cp

_Peios / Using Peios / Peiosutils / Files and directories_

> Copy files and directories — including how a copy gets its security descriptor and the --preserve family that carries attributes from the source.

`cp` copies files and directories.

```
cp [options] source dest
cp [options] source... directory
cp [options] -t directory source...
```

In the first form, `cp` copies one `source` to one `dest`. In the other two, it copies any number of sources *into* a directory, keeping their names.

```
$ cp report.txt report-backup.txt
$ cp report.txt notes.txt /home/jack/archive/
```

## Copying directories

`cp` refuses to copy a directory unless you ask it to descend into one:

| Option | Effect |
|---|---|
| `-r`, `-R`, `--recursive` | Copy directories, and everything inside them, recursively. |
| `-a`, `--archive` | A faithful recursive copy: copy the whole tree, follow no symbolic links, and preserve every attribute. Equivalent to `-d -R --preserve-all`. |

`-a` is the option to use when you want the copy to be as close to the original as possible — same structure, same security, same timestamps. A plain `-r` copies the *contents* but, as the next section explains, gives the copies fresh security of their own.

## What a copy's security descriptor is

This is the part of `cp` that is specific to Peios, so it is worth being precise.

Every file carries a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md) — the record of who owns it and who may do what to it. When `cp` copies a file, it creates a *new* file, and that new file needs a descriptor.

**By default, a copy gets a fresh descriptor — it does not inherit the source's.** Specifically, a plain `cp`:

- gives the copy a descriptor **inherited from the destination directory**, exactly as if you had created a new file there;
- makes **you** the owner of the copy;
- gives the copy **fresh timestamps** — the copy's modification time is "now".

This is usually what you want. A file copied into your home directory should be governed by your home directory's rules and owned by you, regardless of where it came from. But when you need the copy to keep something from the source, you ask for it explicitly — with the `--preserve` family.

### The `--preserve` family

`--preserve` carries chosen attributes from the source onto the copy. The attributes you can preserve:

| Attribute | What it carries from the source |
|---|---|
| `owner` | The owner SID. |
| `dacl` | The whole DACL — every access rule on the file, inherited rules included. |
| `sacl` | The whole SACL — the audit rules and the integrity label. |
| `daclni` | The DACL with **inherited** rules stripped out — only the rules set explicitly on the source. |
| `saclni` | The SACL with inherited rules stripped out. |
| `timestamps` | The access and modification times. |
| `links` | The hard-link structure — files hard-linked together in the source stay hard-linked in the copy. |
| `security` | The `security.peios.*` extended attributes (other than the descriptor itself). |
| `xattrs` | All other extended attributes. |

You rarely name those individually. These options select sensible sets:

| Option | Preserves |
|---|---|
| `-p` | Timestamps only. |
| `--preserve` | Timestamps only — the same as `-p`. |
| `--preserve=LIST` | Exactly the comma-separated attributes you name. |
| `--preserve-all` | Every attribute in the table above. |
| `--sd` | `owner`, `dacl`, `sacl` — the **full security descriptor**, copied verbatim. |
| `--sd-explicit` | `owner`, `daclni`, `saclni` — the source's **explicitly set** rules only. |
| `--no-preserve=LIST` | Turns the named attributes back *off* — useful to subtract from a broader option. |

### `--sd` versus `--sd-explicit`

Both carry the source's security across; they differ in how they treat *inherited* rules.

- **`--sd`** copies the descriptor exactly — inherited rules and all. The copy ends up with precisely the access rules the source had, even if it lands in a directory that would have handed down something different. Use it when the copy must be a security-exact duplicate.
- **`--sd-explicit`** copies only the rules that were set *explicitly* on the source, and lets the destination directory supply inheritance for the rest. Use it when copying a file into a *different* directory and you want the file's own tailored rules to come along while still picking up the new location's inherited rules.

For what "inherited" versus "explicit" means, see [Inheritance](/peios/security-fundamentals/security-descriptors/inheritance.md).

### When a preserve cannot be honoured

A requested preserve is a firm instruction. If `cp` cannot carry an attribute across — for example, writing the copy's SACL requires the `SeSecurityPrivilege` privilege, and the caller does not have it — `cp` treats that as a hard error and the copy fails. It does not quietly produce a copy that is missing what you asked to preserve.

There is one exception, and it is a matter of definition rather than a softening of the rule. `security` and `xattrs` name extended attributes, and some filesystems have no extended attributes **at all** — FAT, 9p, and a squashfs image built without an attribute table are the ones you will meet. Copying *from* such a filesystem carries nothing, because there was nothing there: the preserve is satisfied vacuously, and the copy succeeds.

That is narrow on purpose. It applies only to reading the source. If the source does have extended attributes and the *destination* filesystem cannot hold them, the preserve genuinely failed and `cp` still stops:

```
# cp -a /home/jack/notes /mnt/usb-fat/
cp: failed to set extended attributes on '/mnt/usb-fat/notes': Operation not supported
```

The distinction matters most for `-a`, which requests both attribute classes on every file it touches. Without it, one xattr-less filesystem anywhere in a tree would abort the whole copy partway through.

## Overwriting an existing destination

By default, if the destination already exists, `cp` overwrites it. These options change that.

| Option | Effect |
|---|---|
| `-i`, `--interactive` | Ask before overwriting an existing file. |
| `-n`, `--no-clobber` | Never overwrite an existing file; skip it silently. |
| `-u`, `--update[=WHICH]` | Overwrite only when the source is newer than the destination. `WHICH` can be `all`, `none`, or `older`. |
| `-f`, `--force` | If an existing destination cannot be opened for writing, remove it and try the copy again. |
| `--remove-destination` | Remove the destination *before* opening it, always — contrast `--force`, which only removes on failure. |
| `-b`, `--backup[=CONTROL]` | Before overwriting, make a backup of the destination. |

## Symbolic links in the source

When a source is a symbolic link, `cp` can copy the link itself or the file it points to.

| Option | Effect |
|---|---|
| `-L`, `--dereference` | Always follow symbolic links — copy the file they point to. |
| `-P`, `--no-dereference` | Never follow symbolic links — copy the link itself. |
| `-H` | Follow only the symbolic links named directly on the command line, not links found while recursing. |
| `-d` | Copy links as links, and preserve hard-link structure. Short for `--no-dereference --preserve=links`. |

## Other ways to copy

`cp` can also produce something other than a plain duplicate.

| Option | Effect |
|---|---|
| `-l`, `--link` | Create a hard link to the source instead of copying its data. |
| `-s`, `--symbolic-link` | Create a symbolic link to the source instead of copying. |
| `--reflink[=WHEN]` | Make a lightweight copy-on-write clone where the file system supports it. `WHEN` is `always`, `auto`, or `never`. |
| `--sparse[=WHEN]` | Control whether runs of zero bytes are stored as holes rather than written out. `WHEN` is `always`, `auto`, or `never`. |
| `--attributes-only` | Create the destination and copy its attributes, but not its data. |

## Other options

| Option | Effect |
|---|---|
| `-t`, `--target-directory=DIR` | Copy every source into `DIR`. Useful when the directory is not the last argument. |
| `-T`, `--no-target-directory` | Treat `dest` as a plain file even if it is a directory. |
| `--parents` | Recreate the source's leading directories under the destination. |
| `-x`, `--one-file-system` | Do not cross into a different file system while recursing. |
| `--strip-trailing-slashes` | Strip any trailing slashes from each source argument before using it. |
| `-v`, `--verbose` | Print each file as it is copied. |
| `--debug` | Explain in detail how each file was copied. Implies `-v`. |
| `--progress` | Show a progress bar. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every file was copied successfully. |
| `1` | A file could not be copied — including a requested `--preserve` that could not be honoured. |

---

# mv

_Peios / Using Peios / Peiosutils / Files and directories_

> Move or rename files and directories — including how a move handles a file's security descriptor depending on whether it crosses a file system.

`mv` moves files and directories from one place to another. Moving a file within the same directory, under a new name, is how you *rename* it.

```
mv [options] source dest
mv [options] source... directory
mv [options] -t directory source...
```

```
$ mv draft.txt report.txt                 # rename
$ mv report.txt notes.txt /home/jack/done/ # move into a directory
```

## A move is either a rename or a copy-and-delete

`mv` does its job in one of two ways, and which one decides what happens to the file's security.

**Within one file system, a move is a rename.** Nothing is copied: the file stays exactly where it physically is, and only its name and directory entry change. The file keeps its inode, and so it keeps its **security descriptor unchanged** — every owner, access rule, audit rule, and timestamp is exactly as before. A rename does not re-evaluate inheritance; a file does not pick up new rules just because it landed in a new directory.

**Across file systems, a move is a copy followed by a delete.** When the destination is on a different file system, `mv` cannot just rename — it copies the data to a new file on the destination, then removes the original. That new file is a genuinely new object, so, exactly as with [`cp`](/peios/using-peios/peiosutils/files-and-directories/cp.md), it gets a **fresh security descriptor inherited from the destination directory** unless you ask otherwise.

So: rename a file and its security is untouched; move it to another file system and the copy starts fresh.

## Preserving security on a cross-file-system move

For the copy-and-delete case, `mv` carries the same `--preserve` family that `cp` does — an identical set of options, behaving the same way. They let a cross-file-system move carry the source's owner, access rules, timestamps, and other attributes onto the new file instead of taking the destination directory's inherited default.

The full `--preserve` reference — the attribute list, `--sd`, `--sd-explicit`, `--preserve-all`, `--no-preserve`, and what happens when a preserve cannot be honoured — is on the [`cp`](/peios/using-peios/peiosutils/files-and-directories/cp.md) page. Everything there applies to `mv` unchanged.

On a same-file-system move the `--preserve` options have nothing to do: a rename already keeps every attribute, because it is the same file.

## Overwriting an existing destination

By default `mv` overwrites an existing destination. These options change that. When more than one of `-i`, `-f`, `-n` is given, the last one wins.

| Option | Effect |
|---|---|
| `-i`, `--interactive` | Ask before overwriting an existing file. |
| `-f`, `--force` | Do not prompt before overwriting. |
| `-n`, `--no-clobber` | Never overwrite an existing file. |
| `-u`, `--update[=WHICH]` | Overwrite only when the source is newer. `WHICH` can be `all`, `none`, or `older`. |
| `-b`, `--backup[=CONTROL]` | Make a backup of the destination before overwriting it. |

## Other options

| Option | Effect |
|---|---|
| `-t`, `--target-directory=DIR` | Move every source into `DIR`. |
| `-T`, `--no-target-directory` | Treat `dest` as a plain file even if it is a directory. |
| `--strip-trailing-slashes` | Strip any trailing slashes from each source argument. |
| `-v`, `--verbose` | Print each file as it is moved. |
| `--debug` | Explain in detail how each file was moved. Implies `-v`. |
| `--progress` | Show a progress bar. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every file was moved successfully. |
| `1` | A file could not be moved. |

---

# rm

_Peios / Using Peios / Peiosutils / Files and directories_

> Remove files and directories — including how rm decides a file is write-protected and worth prompting about.

`rm` removes files. With `-r`, it removes directories and everything inside them.

```
rm [options] file...
```

```
$ rm draft.txt old-notes.txt
$ rm -r build/
```

Removal is not reversible — once `rm` removes a file, its directory entry is gone. `rm` has a few safety behaviours, below, but the basic rule stands: think before you confirm.

## Removing directories

`rm` will not remove a directory unless you say so:

| Option | Effect |
|---|---|
| `-r`, `-R`, `--recursive` | Remove directories and all of their contents, recursively. |
| `-d`, `--dir` | Remove a directory, but only if it is already empty. |

## The write-protected prompt

By default — when you have not passed `-f` and `rm` is running interactively — `rm` prompts before removing a file you cannot write to:

```
$ rm policy.db
rm: remove write-protected regular file 'policy.db'?
```

The point of that prompt is to catch mistakes: a file you cannot write is one you most likely did not mean to delete.

**"Write-protected" here means a real access check.** `rm` decides it by asking the kernel whether your token may write the file — a live check against the file's [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md). It is not read off any decorative metadata on the inode; it is the same authority question that any write to the file would face. So the prompt is honest: if `rm` says a file is write-protected, it is a file the access model would genuinely stop you writing.

(The check is about *write* access to the file's contents. Whether you may actually *remove* the file is a separate question, decided by its own access right — so you can be prompted about a write-protected file and still be allowed to delete it. The prompt is a courtesy check, not the authorisation.)

## Controlling the prompts

| Option | Effect |
|---|---|
| `-f`, `--force` | Never prompt. Also ignore files that do not exist instead of reporting them. |
| `-i` | Prompt before *every* removal. |
| `-I` | Prompt just *once* before removing more than three files or before a recursive removal. Less intrusive than `-i`, but still a check against a slip. |
| `--interactive[=WHEN]` | Set the prompting explicitly: `never`, `once`, or `always`. |

## The root failsafe

`rm` refuses to recursively remove `/` — the root of the file system — because doing so by accident would be catastrophic.

| Option | Effect |
|---|---|
| `--preserve-root` | Refuse to recurse on `/`. This is the default; you do not need to ask for it. |
| `--no-preserve-root` | Disable the failsafe. Required, in full and unabbreviated, if you genuinely intend a recursive operation on `/`. |

## Other options

| Option | Effect |
|---|---|
| `-v`, `--verbose` | Print each file as it is removed. |
| `--progress` | Show a progress bar. |

## `rm` and `shred`

`rm` unlinks a file — it removes the name, and the space becomes free. The file's *data* may still be physically present on the device until something else overwrites it. When you need the contents to be genuinely unrecoverable, use [`shred`](/peios/using-peios/peiosutils/files-and-directories/shred.md), which overwrites the data before removing the file.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every requested removal succeeded (or, with `-f`, the file did not exist). |
| `1` | A file could not be removed. |

---

# rmdir

_Peios / Using Peios / Peiosutils / Files and directories_

> Remove directories, but only when they are already empty.

`rmdir` removes directories — but only **empty** ones. If a directory still has anything in it, `rmdir` leaves it alone and reports the failure.

```
rmdir [options] directory...
```

```
$ rmdir old-cache/
```

That refusal to touch a non-empty directory is the whole point of `rmdir`: it is the safe way to remove a directory you *believe* is empty. If it is not empty, you find out instead of losing the contents. To remove a directory together with everything inside it, use [`rm -r`](/peios/using-peios/peiosutils/files-and-directories/rm.md).

## Options

| Option | Effect |
|---|---|
| `-p`, `--parents` | Remove the directory and then its ancestors, as long as each becomes empty in turn. `rmdir -p a/b/c` removes `a/b/c`, then `a/b`, then `a`. |
| `--ignore-fail-on-non-empty` | Do not treat "directory not empty" as a failure. Other failures are still reported. Useful when clearing out whatever directories happen to be empty and leaving the rest. |
| `-v`, `--verbose` | Print a line for each directory as it is removed. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every directory was removed. |
| `1` | A directory could not be removed — it was not empty, did not exist, or removal was refused. |

---

# mkdir

_Peios / Using Peios / Peiosutils / Files and directories_

> Create directories — and the shared creation flags that set a new object's security descriptor, used by mkdir, mkfifo, mknod, and touch.

`mkdir` creates directories.

```
mkdir [options] directory...
```

```
$ mkdir reports
$ mkdir -p projects/2026/q2
```

## Creating parent directories

By default `mkdir` creates exactly the directories you name, and fails if a parent is missing. `-p` removes that restriction:

| Option | Effect |
|---|---|
| `-p`, `--parents` | Create any missing parent directories along the way. Also makes `mkdir` succeed quietly if the directory already exists. |
| `-v`, `--verbose` | Print a line for each directory as it is created. |

## The creation flags

A new directory needs a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md) — the record of who owns it and who may do what to it. By default, **a new directory inherits its descriptor from the directory it is created in**: it picks up the access rules its parent hands down. For most directories that is exactly right, and you pass no extra options at all.

When you need the new directory to have a *different* descriptor, the **creation flags** set it explicitly. These five flags are shared, unchanged, by `mkdir`, [`mkfifo`](/peios/using-peios/peiosutils/files-and-directories/mkfifo.md), [`mknod`](/peios/using-peios/peiosutils/files-and-directories/mknod.md), and [`touch`](/peios/using-peios/peiosutils/files-and-directories/touch.md) — whenever one of those commands creates a new object, it accepts this same set. This page is the full reference for them.

| Flag | Argument | Effect |
|---|---|---|
| `--owner` | SID | Make this principal the owner, instead of you. |
| `--group` | SID | Set the object's group. |
| `--label` | level | Give the object a mandatory integrity label. |
| `--no-inherit` | — | Do **not** inherit rules from the parent directory; lock the object to its owner. |
| `--sddl` | SDDL string | Supply the entire security descriptor directly. |

A SID argument is either a literal `S-1-…` value or a well-known alias — `BA` for the built-in administrators, for example.

`--label` takes one of these levels, lowest to highest: `untrusted`, `low`, `medium`, `medium-plus`, `high`, `system`, `protected`. The label applies the standard rule for files and directories — a principal below the object's level cannot write to it.

### Inherited, or explicit

The default and `--no-inherit` are the two ends of a choice.

- **Inherited (the default)** — the new directory tracks its parent. Rules the parent hands down apply to it, and as the parent's rules change the directory follows along. This keeps a tree of directories consistent without per-directory effort.
- **`--no-inherit`** — the new directory takes nothing from its parent. Its access is locked to its owner, and it does not follow the parent's rules. Use it to carve out a directory whose security stands apart from the tree around it.

See [Inheritance](/peios/security-fundamentals/security-descriptors/inheritance.md) for the full model.

### `--sddl` for a complete descriptor

The four flags above are shortcuts for common adjustments. When you need to specify the *whole* descriptor — a particular set of access rules, audit rules, owner, and group all at once — `--sddl` takes it as a single string in the security-descriptor definition language:

```
$ mkdir --sddl 'O:BAG:BAD:(A;OICI;FA;;;BA)' secure-area
```

`--sddl` is mutually exclusive with `--owner`, `--group`, `--label`, and `--no-inherit` — it already says everything those flags would say, so combining them is rejected.

### How the descriptor is applied

`mkdir` creates the directory first and then applies the descriptor to it. The two steps are not separately visible — `mkdir` either finishes with the directory created and secured as asked, or fails as a whole. If applying the descriptor fails, the half-created directory is not left behind.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every directory was created. |
| `1` | A directory could not be created, or a creation flag could not be applied. |

---

# mkfifo

_Peios / Using Peios / Peiosutils / Files and directories_

> Create named pipes (FIFOs).

`mkfifo` creates **named pipes**, also called FIFOs.

```
mkfifo [options] name...
```

```
$ mkfifo events
```

A named pipe is a file that two programs use to pass a stream of data: one writes into it, the other reads from it, and the data flows through in order — first in, first out, which is what "FIFO" stands for. Unlike an ordinary pipe between two commands, a named pipe has a name on the file system, so the two programs do not have to be started together or be related to each other.

## Setting the new pipe's security

A FIFO is a file, and a new file needs a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md). By default a new FIFO inherits its descriptor from the directory it is created in.

`mkfifo` accepts the shared **creation flags** — `--owner`, `--group`, `--label`, `--no-inherit`, and `--sddl` — to set that descriptor explicitly instead. They behave exactly as they do for `mkdir`; the full reference is on the [`mkdir`](/peios/using-peios/peiosutils/files-and-directories/mkdir.md) page.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every FIFO was created. |
| `1` | A FIFO could not be created — for example, the name already exists. |

---

# mknod

_Peios / Using Peios / Peiosutils / Files and directories_

> Create special files — device nodes and named pipes.

`mknod` creates **special files** — files that are not data but an interface to something else: a device, or a named pipe.

```
mknod [options] name type [major minor]
```

```
$ mknod /dev/mydisk b 8 0
$ mknod backlog p
```

## The type argument

The `type` says what kind of special file to create:

| Type | Creates |
|---|---|
| `b` | A **block device** — a device addressed in fixed-size blocks, such as a disk. |
| `c` or `u` | A **character device** — a device addressed as a stream of bytes, such as a terminal. |
| `p` | A **named pipe** (FIFO). |

For a block or character device (`b`, `c`, `u`) you must also give a **major** and a **minor** number. The major number selects which driver handles the device; the minor number tells that driver which specific device is meant. For a pipe (`p`), the major and minor numbers are omitted — a pipe has no driver behind it.

A major or minor number is read as hexadecimal if it begins with `0x`, as octal if it begins with `0`, and as decimal otherwise.

`mknod NAME p` and [`mkfifo NAME`](/peios/using-peios/peiosutils/files-and-directories/mkfifo.md) do the same thing; `mkfifo` exists as the clearer way to ask for just a pipe.

## Setting the new file's security

A special file needs a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md) like any other file, and by default a new one inherits its descriptor from the directory it is created in.

`mknod` accepts the shared **creation flags** — `--owner`, `--group`, `--label`, `--no-inherit`, and `--sddl` — to set that descriptor explicitly. They behave exactly as they do for `mkdir`; the full reference is on the [`mkdir`](/peios/using-peios/peiosutils/files-and-directories/mkdir.md) page.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The special file was created. |
| `1` | It could not be created — a missing or invalid type, missing device numbers, or a name that already exists. |

---

# touch

_Peios / Using Peios / Peiosutils / Files and directories_

> Create empty files, or update the timestamps of existing ones.

`touch` does one of two things, depending on whether the file already exists:

- if the file **does not exist**, `touch` creates it, empty;
- if the file **does exist**, `touch` updates its timestamps to the current time.

```
touch [options] [file...]
```

```
$ touch notes.txt        # create notes.txt if absent, else bump its times
```

The "create an empty file" behaviour is the everyday use. The "update timestamps" behaviour matters to tools that decide what work to do by comparing file times — `touch` is how you tell such a tool that a file should be considered freshly changed.

## Which timestamps, and to what

By default `touch` sets both the access time and the modification time to the current moment. These options narrow or redirect that.

| Option | Effect |
|---|---|
| `-a` | Change only the access time. |
| `-m` | Change only the modification time. |
| `--time=WORD` | Choose which time to change by name: `access`, `atime`, or `use` for the access time; `modify` or `mtime` for the modification time. |
| `-d`, `--date=STRING` | Use the time described by `STRING` instead of the current time. |
| `-t STAMP` | Use the time given as `[[CC]YY]MMDDhhmm[.ss]` instead of the current time. |
| `-r`, `--reference=FILE` | Use the timestamps of `FILE` instead of the current time. |
| `-h`, `--no-dereference` | If the named file is a symbolic link, change the link's own timestamps rather than those of the file it points to. |

## Not creating a file

To use `touch` purely to update timestamps, and never to create anything:

| Option | Effect |
|---|---|
| `-c`, `--no-create` | Do not create a file that does not exist. A named file that is absent is silently skipped. |

## Setting a created file's security

When `touch` *creates* a file, that new file needs a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md), and by default it inherits one from the directory it is created in.

`touch` accepts the shared **creation flags** — `--owner`, `--group`, `--label`, `--no-inherit`, and `--sddl` — to set that descriptor explicitly. They behave exactly as they do for `mkdir`; the full reference is on the [`mkdir`](/peios/using-peios/peiosutils/files-and-directories/mkdir.md) page.

These flags apply **only on the create path**. If `touch` is updating the timestamps of a file that already exists, there is no new descriptor to set, and the creation flags have nothing to do — they do not alter an existing file's security. Pair them with `-c` and they never take effect at all, since `-c` means nothing is ever created.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every file was created or updated as requested. |
| `1` | A file could not be created or updated. |

---

# ln

_Peios / Using Peios / Peiosutils / Files and directories_

> Create hard links and symbolic links between files.

`ln` creates **links** — extra ways to reach a file.

```
ln [options] target link_name
ln [options] target
ln [options] target... directory
ln [options] -t directory target...
```

```
$ ln -s /opt/app/bin/app /lcl/bin/app   # a symbolic link
$ ln data.csv data-archive.csv                # a hard link
```

## Hard links and symbolic links

There are two kinds of link, and they are genuinely different things.

A **hard link** is another name for the *same file*. The file's data exists once; a hard link is an additional directory entry pointing at it. Every hard link to a file is equal — none is "the original" — and the file's data stays as long as at least one hard link remains. Hard links cannot span file systems, and cannot be made to directories.

A **symbolic link** is a small file that simply *contains a path*. When something opens a symbolic link, it is redirected to whatever that path names. The symbolic link and its target are separate files: if the target is moved or removed, the link still exists but now points at nothing — it "dangles". Symbolic links can point anywhere, across file systems and at directories.

`ln` creates **hard links by default**, and symbolic links with `-s`. When in doubt, `-s` — a symbolic link's behaviour is the easier one to reason about.

The two also differ in how they are secured. A hard link is not a new file, so creating one involves no new [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md) — every name for the file shares the file's existing one. A symbolic link *is* a new file, and like any new file it inherits its own descriptor from the directory it is created in.

## The forms

- `ln target link_name` — create one link named `link_name`.
- `ln target` — create a link to `target` in the current directory, with the same final name.
- `ln target... directory` — create a link to each `target` inside `directory`.
- `ln -t directory target...` — the same, with the directory named first.

## Options

| Option | Effect |
|---|---|
| `-s`, `--symbolic` | Make symbolic links instead of hard links. |
| `-f`, `--force` | Remove an existing destination file so the link can be created. |
| `-i`, `--interactive` | Ask before removing an existing destination. |
| `-r`, `--relative` | For a symbolic link, write the target as a path relative to the link's own location, rather than an absolute path. |
| `-n`, `--no-dereference` | If `link_name` is itself a symbolic link to a directory, treat it as an ordinary file — replace the link rather than create something inside the directory it points to. |
| `-L`, `--logical` | When the target is a symbolic link, link to what it points to. |
| `-P`, `--physical` | Make a hard link to a symbolic link itself, rather than to its target. |
| `-t`, `--target-directory=DIR` | Create all links inside `DIR`. |
| `-T`, `--no-target-directory` | Treat `link_name` as a plain file, never as a directory to create links inside. |
| `-b`, `--backup[=CONTROL]` | Back up an existing destination before replacing it. |
| `-S`, `--suffix=SUFFIX` | Use `SUFFIX` for backup file names. |
| `-v`, `--verbose` | Print the name of each link as it is created. |

## Inspecting links afterwards

[`readlink`](/peios/using-peios/peiosutils/listing-and-paths/readlink.md) prints where a symbolic link points. The low-level [`link`](/peios/using-peios/peiosutils/files-and-directories/link-and-unlink.md) command makes a single hard link with no options, for scripts that want exactly that and nothing else.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every link was created. |
| `1` | A link could not be created. |

---

# link and unlink

_Peios / Using Peios / Peiosutils / Files and directories_

> The single-purpose, low-level commands for creating one hard link and for removing one file.

`link` and `unlink` are the bare, single-purpose versions of two operations that [`ln`](/peios/using-peios/peiosutils/files-and-directories/ln.md) and [`rm`](/peios/using-peios/peiosutils/files-and-directories/rm.md) also perform. Each does exactly one thing, takes exactly its operands, and has no options. They exist for scripts that want one precise operation with no surrounding behaviour — no prompting, no link kinds to choose, no recursion.

## `link`

```
link file1 file2
```

`link` creates one hard link: a new name, `file2`, for the existing file `file1`. `file1` must exist; `file2` must not.

```
$ link data.csv data-archive.csv
```

That is the whole command. It performs the link operation directly and reports whether it succeeded. For anything more — symbolic links, replacing an existing destination, linking into a directory — use [`ln`](/peios/using-peios/peiosutils/files-and-directories/ln.md).

## `unlink`

```
unlink file
```

`unlink` removes one file: it deletes the directory entry `file`. It takes a single name and removes it directly.

```
$ unlink stale.lock
```

`unlink` does not prompt, does not recurse, and does not remove directories. For removing several files, removing directories, or any of the safety prompts, use [`rm`](/peios/using-peios/peiosutils/files-and-directories/rm.md).

## Why they exist

`ln` and `rm` are the commands to use day to day — they are flexible and have the safety behaviours. `link` and `unlink` are deliberately rigid: a script that calls `unlink` will *only ever* remove a single file, and a reviewer can see that at a glance. The narrowness is the feature.

## Exit status

Both commands:

| Code | Meaning |
|---|---|
| `0` | The operation succeeded. |
| `1` | The operation failed — a missing or already-existing operand, or a refused request. |

---

# shred

_Peios / Using Peios / Peiosutils / Files and directories_

> Destroy a file's contents by overwriting them repeatedly, so they cannot be recovered — including how --force overrides a file's protection.

`shred` destroys a file's contents. It does not just unlink the file — it **overwrites the data**, repeatedly, so that what was there cannot be read back even with effort and specialised equipment.

```
shred [options] file...
```

```
$ shred -u -v secret-keys.txt
```

[`rm`](/peios/using-peios/peiosutils/files-and-directories/rm.md) removes a file's *name*; the data may sit on the device, recoverable, until something else happens to overwrite it. `shred` overwrites the data on purpose. Use it when a file held something that must not be recoverable.

By default `shred` overwrites a file three times and then **leaves the file in place** — empty of its old contents but still present. That default exists because `shred` is often pointed at device files, which usually should not be removed. To remove the file after shredding it, pass `-u`.

## When shredding actually works

`shred` relies on one assumption: that writing to the file overwrites the *same physical storage* the old data occupied. On a traditional file system that holds. Several common file system designs break it — anything that writes new data to a fresh location instead of in place, keeps snapshots, journals file data, caches copies elsewhere, or compresses. On those, `shred` cannot guarantee the old data is gone.

Backups and remote mirrors are a separate matter entirely: `shred` cannot reach a copy of the file that lives somewhere else.

Treat `shred` as effective on a plain local file system and uncertain otherwise.

## Options

| Option | Effect |
|---|---|
| `-n`, `--iterations=N` | Overwrite `N` times instead of the default of 3. |
| `-u`, `--remove[=HOW]` | Remove the file after overwriting it. `HOW` controls how thoroughly the *name* itself is obscured before the file is unlinked. |
| `-z`, `--zero` | Add a final pass that writes zeros, so the file does not visibly look shredded afterwards. |
| `-s`, `--size=N` | Shred `N` bytes, rather than the whole file. Accepts size suffixes such as `K`, `M`, `G`. |
| `-x`, `--exact` | Do not round the shredded size up to the next full block. Without it, `shred` rounds up so the block's slack space is overwritten too. |
| `--random-source=FILE` | Take the random bytes for the overwrite passes from `FILE`. |
| `-v`, `--verbose` | Show progress as each pass runs. |
| `-f`, `--force` | Override a file's protection: when a live access check shows you cannot write the file, rewrite its security descriptor so the overwrite can proceed (see below). |

## `--force` and a file's protection

To overwrite a file, `shred` has to be able to write it. If the file's [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md) does not grant you write access, an ordinary `shred` cannot proceed.

`-f` (`--force`) handles that case. When `--force` is given and a live access check shows you cannot currently write the file, `shred` **rewrites the file's security descriptor** to one that grants full access, and then overwrites the file. The reasoning is deliberate: the file is about to be destroyed anyway, and `--force` is you saying "override this file's protection."

`--force` can do this only when you are allowed to change the file's security in the first place — that is, when you own the file or hold the right to rewrite its access rules. If you cannot change the descriptor, `--force` cannot grant itself write access, and the shred still fails. `--force` overrides a file's *protection*; it does not manufacture authority you do not have.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every file was shredded successfully. |
| `1` | A file could not be shredded. |

---

# dd

_Peios / Using Peios / Peiosutils / Files and directories_

> Copy data block by block between files, optionally converting and reshaping it on the way.

`dd` copies data from one place to another, a block at a time, and can transform the data as it goes. It is the tool for low-level copying — writing a disk image to a device, extracting an exact byte range out of a file, copying from or to a raw device.

```
dd [operand]...
```

```
$ dd if=disk.img of=/dev/mydisk bs=4M status=progress
```

## How dd is told what to do

`dd` does not take options in the usual `-x` form. It takes **operands**, each written `name=value`, in any order. With no `if=` operand it reads from standard input; with no `of=` operand it writes to standard output.

## The operands

| Operand | Meaning |
|---|---|
| `if=FILE` | Read input from `FILE`. |
| `of=FILE` | Write output to `FILE`. |
| `bs=BYTES` | Read and write `BYTES` at a time. Sets both the input and output block size at once. |
| `ibs=BYTES` | Input block size (default 512). |
| `obs=BYTES` | Output block size (default 512). |
| `cbs=BYTES` | Conversion block size — used by the `block` and `unblock` conversions. |
| `count=N` | Stop after `N` input blocks, rather than reading to the end. |
| `skip=N` | Skip `N` input blocks before copying. Also spelled `iseek=N`. |
| `seek=N` | Skip `N` output blocks before writing. Also spelled `oseek=N`. |
| `conv=LIST` | Apply the comma-separated conversions below. |
| `iflag=LIST` | Comma-separated input flags — how the input is opened and read. |
| `oflag=LIST` | Comma-separated output flags — how the output is opened and written. |
| `status=LEVEL` | How much to report: `progress` (periodic stats while copying), `noxfer` (final counts only), `none` (silent). |

A size value accepts a unit suffix (`K`, `M`, `G`, …). So `bs=4M` is four mebibytes per block.

## Conversions (`conv=`)

| Value | Effect |
|---|---|
| `ucase` / `lcase` | Convert the data to upper-case / lower-case. |
| `swab` | Swap every adjacent pair of bytes. |
| `sync` | Pad each input block out to its full size — with zeros, or with spaces alongside `block`/`unblock`. |
| `block` / `unblock` | Convert between newline-terminated lines and fixed-size `cbs` records. |
| `sparse` | Where an output block is all zeros, seek past it instead of writing it. |
| `excl` | Fail if the output file already exists. |
| `nocreat` | Fail if the output file does *not* already exist. |
| `notrunc` | Do not truncate the output file when opening it. |
| `noerror` | Continue past read errors instead of stopping. |
| `fdatasync` / `fsync` | Flush the data — or the data and metadata — to storage before finishing. |

When `dd` **creates** the output file — an `of=` file that does not already exist — the new file needs a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md), and it inherits one from the directory it is created in.

## Input and output flags (`iflag=`, `oflag=`)

| Flag | Applies to | Effect |
|---|---|---|
| `count_bytes` | input | Interpret `count=N` as a number of bytes, not blocks. |
| `skip_bytes` | input | Interpret `skip=N` as a number of bytes. |
| `fullblock` | input | Wait for a full `ibs` of data on each read. |
| `seek_bytes` | output | Interpret `seek=N` as a number of bytes. |
| `append` | output | Open the output in append mode. |
| `direct` | both | Use direct I/O, bypassing the cache. |
| `dsync` / `sync` | both | Use synchronised I/O — for data, or for data and metadata. |
| `nonblock` | both | Use non-blocking I/O. |
| `noatime` | both | Do not update the file's access time. |
| `nocache` | both | Ask the system to drop the file's cached pages. |
| `directory` | both | Fail unless the file is a directory. |

## What dd prints

Unless `status=none` is set, `dd` prints a summary when it finishes:

```
16+0 records in
16+0 records out
67108864 bytes (67 MB, 64 MiB) copied, 1.234 s, 54.4 MB/s
```

The `records in` / `records out` counts are written `complete+partial` — full-sized blocks plus any short final block. `status=progress` prints the last line periodically during a long copy.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The copy completed. |
| `1` | The copy failed — a bad operand, an I/O error, or a file that could not be opened. |

---

# df

_Peios / Using Peios / Peiosutils / Files and directories_

> Report how much space each file system has and how much of it is free.

`df` — "disk free" — reports, for each file system, how much space it has and how much is still available.

```
df [options] [file...]
```

With no arguments, `df` reports on every mounted file system. Given a `file`, it reports on the one file system that file lives on.

```
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda2       456G  198G  235G  46% /
/dev/sda1       511M   12M  499M   3% /boot
```

## The default columns

Each line is one file system:

- **Filesystem** — the device or source backing it.
- **Size** — its total capacity.
- **Used** — space in use.
- **Avail** — space still available.
- **Use%** — used space as a percentage.
- **Mounted on** — where it is attached in the directory tree.

## Choosing the units

By default sizes are shown in blocks. These options change that.

| Option | Effect |
|---|---|
| `-h`, `--human-readable` | Show sizes with unit suffixes — `456G`, `12M` — using powers of 1024. |
| `-H`, `--si` | Likewise, but using powers of 1000. |
| `-B`, `--block-size=SIZE` | Show sizes scaled to `SIZE` — `-BM` reports in megabytes. |
| `-k` | Use 1024-byte blocks. |

## Choosing what to report

| Option | Effect |
|---|---|
| `-a`, `--all` | Include dummy and otherwise-hidden file systems that `df` would normally skip. |
| `-l`, `--local` | Report only local file systems, skipping network-mounted ones. |
| `-t`, `--type=TYPE` | Report only file systems of type `TYPE`. |
| `-x`, `--exclude-type=TYPE` | Report everything *except* file systems of type `TYPE`. |
| `--total` | Add a final line giving the grand total across everything reported. |

## Changing the output

| Option | Effect |
|---|---|
| `-i`, `--inodes` | Report inode counts — total, used, and free — instead of block space. A file system can run out of inodes while it still has space. |
| `-T`, `--print-type` | Add a column showing each file system's type. |
| `--output[=LIST]` | Replace the default columns with exactly the fields named in `LIST`. With no list, print every available field. |
| `-P`, `--portability` | Use a fixed, simple column layout that does not wrap a long device name onto its own line. |

## Sync behaviour

| Option | Effect |
|---|---|
| `--sync` | Flush pending writes before reading the usage figures, for the most up-to-date numbers. |
| `--no-sync` | Do not flush first. This is the default and is faster. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The report was produced. |
| `1` | A failure — for example, a named file does not exist, or the table of file systems could not be read. |

---

# du

_Peios / Using Peios / Peiosutils / Files and directories_

> Report how much disk space files and directories use.

`du` — "disk usage" — reports how much space files and directories occupy. Where [`df`](/peios/using-peios/peiosutils/files-and-directories/df.md) tells you about a whole file system, `du` tells you where the space has actually gone.

```
du [options] [file...]
```

```
$ du -sh /home/jack/projects
4.2G    /home/jack/projects
```

With no arguments, `du` works on the current directory. By default it walks the directory tree, prints the cumulative size of **each directory** (a directory's size includes everything under it), and ends with the total for the argument itself. Individual files are not listed unless you ask for them.

## What to count and how deep

| Option | Effect |
|---|---|
| `-a`, `--all` | List every file, not only directories. |
| `-s`, `--summarize` | Print only a single total for each argument — no per-subdirectory breakdown. |
| `-d`, `--max-depth=N` | Show totals only down to `N` levels below each argument. `-d 0` is the same as `-s`. |
| `-S`, `--separate-dirs` | Give each directory's *own* size, excluding its subdirectories. |
| `-c`, `--total` | Add a final grand-total line. |
| `--inodes` | Count inodes used rather than space used. |

## What "size" means

| Option | Effect |
|---|---|
| `-h`, `--human-readable` | Sizes with unit suffixes — `4.2G` — using powers of 1024. |
| `--si` | Likewise, using powers of 1000. |
| `-B`, `--block-size=SIZE` | Scale sizes to `SIZE`. |
| `-k` / `-m` | Use 1024-byte / 1024×1024-byte units. |
| `--apparent-size` | Report the apparent size of files — how much data they contain — rather than the disk space they occupy. The two differ for sparse files and because of block rounding. |
| `-b`, `--bytes` | Report plain byte counts. Equivalent to `--apparent-size --block-size=1`. |

## Following symbolic links

By default `du` does not follow symbolic links.

| Option | Effect |
|---|---|
| `-L`, `--dereference` | Follow all symbolic links and count what they point to. |
| `-D`, `-H`, `--dereference-args` | Follow only the symbolic links named directly on the command line. |
| `-P`, `--no-dereference` | Follow no symbolic links. This is the default. |
| `-x`, `--one-file-system` | Do not cross into a different file system while walking. |
| `-l`, `--count-links` | Count a hard-linked file every time it is encountered, instead of once. |

## Limiting the output

| Option | Effect |
|---|---|
| `--exclude=PATTERN` | Skip files and directories whose name matches `PATTERN`. |
| `--exclude-from=FILE` | Skip everything matching any pattern listed in `FILE`. |
| `-t`, `--threshold=SIZE` | Show only entries at least `SIZE` (or, with a negative `SIZE`, at most that big). |
| `--files0-from=F` | Take the list of files to measure from `F`, NUL-separated. `-` means standard input. |
| `--time[=WORD]` | Also show a timestamp — by default the latest modification time anywhere in the directory. |
| `-0`, `--null` | End each output line with a NUL character instead of a newline. |
| `-v`, `--verbose` | Report extra detail about what is being processed. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The report was produced. |
| `1` | A file or directory could not be accessed. |

---

# truncate

_Peios / Using Peios / Peiosutils / Files and directories_

> Shrink or extend a file to an exact size.

`truncate` sets a file's size to an exact figure — shrinking it, or extending it.

```
truncate [options] file...
```

```
$ truncate -s 0 logfile        # empty the file
$ truncate -s 1G disk.img      # make a 1 GiB file
```

Shrinking a file discards everything past the new size. Extending it adds space that reads back as zero bytes; that added space is a *hole* — it costs no actual storage until something writes real data into it, which is how `truncate -s 1G` can create a "1 GiB file" instantly.

By default, `truncate` creates a file that does not yet exist (as an empty file, then sized as asked). A file created this way needs a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md), and it inherits one from the directory it is created in.

## Specifying the size

`-s` (`--size`) takes the size. A plain number sets the size outright. A unit suffix scales it: `K`, `M`, `G`, `T`, … where the bare letter is a power of 1024 (`K` = 1024) and the letter with `B` is a power of 1000 (`KB` = 1000).

A size may also begin with a prefix, which makes it *relative to the file's current size*:

| Prefix | Meaning |
|---|---|
| `+` | Extend by this much. |
| `-` | Reduce by this much. |
| `<` | Shrink to this size only if the file is currently larger ("at most"). |
| `>` | Extend to this size only if the file is currently smaller ("at least"). |
| `/` | Round the size *down* to a multiple of this number. |
| `%` | Round the size *up* to a multiple of this number. |

```
$ truncate -s +4K notes.txt    # 4 KiB larger than it is now
$ truncate -s '<1M' notes.txt  # cap it at 1 MiB, leave smaller files alone
```

## Options

| Option | Effect |
|---|---|
| `-s`, `--size=SIZE` | Set or adjust the size to `SIZE`, as described above. |
| `-r`, `--reference=RFILE` | Base the size on the current size of `RFILE` instead of giving a number. Combine with a relative `-s` to mean "the size of `RFILE`, adjusted". |
| `-c`, `--no-create` | Do not create a file that does not exist; skip it instead. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every file was resized. |
| `1` | A file could not be resized — a bad size, or a file that could not be opened. |

---

# mktemp

_Peios / Using Peios / Peiosutils / Files and directories_

> Create a temporary file or directory with a safely unique name.

`mktemp` creates a temporary file — or, with `-d`, a temporary directory — gives it a name that is not already taken, and prints that name.

```
mktemp [options] [template]
```

```
$ mktemp
/tmp/tmp.QV8x3kZ1aR
$ mktemp -d
/tmp/tmp.7bNc0wPe2L
```

The reason to use `mktemp` rather than inventing a name yourself is **safety**. A script that picks a fixed name like `/tmp/work` has two problems: two copies of the script collide, and a name an attacker can predict is a name an attacker can interfere with before the script gets to it. `mktemp` chooses an unpredictable name *and* creates the file in the same step, so nothing can slip in between. It prints the name it used, for the script to capture:

```
work=$(mktemp)
# ...use "$work"...
rm "$work"
```

The temporary file or directory `mktemp` creates is a new object, and a new object needs a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md); it inherits one from the directory it is created in.

## Templates

The name comes from a **template** — a name with a run of trailing `X` characters, each of which `mktemp` replaces with a random character. More `X`s means a wider range of possible names.

```
$ mktemp report-XXXXXX.txt
report-a8Kp2Q.txt
```

With no template, `mktemp` uses a built-in default that places the file in the temporary directory. A template needs at least three `X`s.

## Options

| Option | Effect |
|---|---|
| `-d`, `--directory` | Create a directory instead of a file. |
| `-p`, `--tmpdir[=DIR]` | Create the temporary object inside `DIR`. With no `DIR`, use the directory named by the `TMPDIR` environment variable, or the system temporary directory. The template is then just the final name component. |
| `--suffix=SUFFIX` | Append `SUFFIX` after the random part of the name — for giving the file an extension. |
| `-u`, `--dry-run` | Do not create anything; just print a name that *would* be free. This reintroduces the race the command exists to avoid — use it only when you genuinely cannot create the object yet. |
| `-q`, `--quiet` | Print no error message if creation fails; rely on the exit status. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The temporary file or directory was created; its name was printed. |
| `1` | Creation failed — a bad template, or a directory that does not exist. |

---

# sd

_Peios / Using Peios / Peiosutils / Files and directories_

> The sd command reads and changes a file's security descriptor — owner, access rules, audit rules, integrity label, and inheritance.

`sd` is the command-line tool for working with **security descriptors** on files. Everything this topic describes — owners, DACLs, ACEs, the SACL, integrity labels, inheritance — `sd` is how you read it and change it from a shell.

```
sd subcommand path [arguments]
```

```
$ sd show ./report.txt
$ sd allow ./report.txt alice:read
$ sd owner ./report.txt BA
```

Where [`ls -l`](/peios/using-peios/peiosutils/listing-and-paths/ls.md) shows a file's owner and a summary, and [`cp --preserve`](/peios/using-peios/peiosutils/files-and-directories/cp.md) carries a descriptor across, `sd` is the tool that edits the descriptor directly.

## The subcommands

| Group | Subcommand | Does |
|---|---|---|
| Inspect | `show` | Print the descriptor on a path. |
| | `check` | Simulate an access check against the path. |
| DACL | `allow` | Add an allow rule for one or more principals. |
| | `deny` | Add a deny rule for one or more principals. |
| | `remove` | Drop every DACL rule for the named principals. |
| Auditing | `audit` | Add an audit rule to the SACL. |
| | `unaudit` | Drop every SACL rule for the named principals. |
| Ownership | `owner` | Set the descriptor's owner. |
| | `group` | Set the descriptor's group. |
| Integrity | `integrity` | Set the mandatory integrity label. |
| Inheritance | `inherit` | Turn inheritance protection on or off. |
| | `reset` | Drop the file's own rules and re-inherit from the parent. |
| | `propagate` | Push inheritance down to descendants. |
| Wholesale | `set` | Replace the entire descriptor at once. |

## Naming a principal

Wherever a subcommand takes a `PRINCIPAL`, it accepts any of:

| Form | Example | Meaning |
|---|---|---|
| `@self` | `@self` | The user SID of the token running `sd`. |
| `@owner` | `@owner` | A placeholder that the access check substitutes with the file's own owner. |
| A well-known label | `Everyone`, `Administrators`, `LocalSystem` | A named built-in principal. |
| A two-letter alias | `WD`, `BA`, `SY` | The short alias for a well-known principal. |
| A raw SID | `S-1-5-32-544` | Any SID, written out in full. |

See [SIDs](/peios/security-fundamentals/identity/sids.md) for what these are.

## Naming permissions

Wherever a subcommand takes `PERMS`, several notations are accepted, and may be mixed:

| Form | Example | Meaning |
|---|---|---|
| Single letters | `rwx`, `r`, `m` | `r` read, `w` write, `x` execute, `d` delete, `m` modify, `f` full, `c` change-permissions, `o` take-ownership. |
| Words | `read,write`, `modify` | The same set, spelled out. |
| Fine-grained names | `read-data,append,traverse` | Individual low-level rights, for precise rules. |
| Raw hex | `0x1F01FF` | An access mask written directly. |

Run letters together (`rwx`) or separate names with commas (`read,write,execute`).

## Inspecting

### `sd show`

Prints the descriptor on a path — the owner, the group, the DACL, the SACL.

```
$ sd show ./report.txt
```

| Flag | Effect |
|---|---|
| `--sddl` | Render the descriptor as an SDDL string. |
| `--raw` | Render SIDs in raw `S-1-…` form only. |
| `--label` | Render SIDs as their labels where known. |
| `--all` | Verbose — decode every flag and show raw masks alongside. |
| `--json` | Emit JSON. |

### `sd check`

Simulates an [access decision](/peios/security-fundamentals/access-decisions/overview.md): "would this access be allowed?" — without performing it.

```
$ sd check ./report.txt write
$ sd check ./report.txt read --pid 4821 --explain
```

| Argument / flag | Effect |
|---|---|
| `PERMS` | The access to test for. |
| `--pid PID` | Check against process `PID`'s token instead of your own. |
| `--explain` | Show why the decision came out as it did — the rule-by-rule walk. |

`sd check` is the first thing to reach for when an access is denied and you do not know why. `--explain` walks the descriptor the same way the kernel does.

## Changing the DACL

The DACL is the list of allow and deny rules. These three subcommands edit it.

### `sd allow` and `sd deny`

Add an allow (or deny) rule for one or more `PRINCIPAL:PERMS` pairs.

```
$ sd allow ./report.txt alice:read bob:rw
$ sd deny  ./report.txt Everyone:write
```

| Flag | Effect |
|---|---|
| `--flags LIST` | ACE inheritance flags — `CI` container-inherit, `OI` object-inherit, `NP` no-propagate, `IO` inherit-only; `none` clears them. |
| `--if EXPR` | Make it a [conditional rule](/peios/security-fundamentals/security-descriptors/conditional-aces.md), applied only when `EXPR` is true. |
| `--replace` | Drop any existing rules for this principal and kind first, instead of appending. |
| `--recursive`, `-r` | Apply to every descendant of the path. |

Remember that a **deny** rule, when it matches, wins over any allow — see [DACL evaluation](/peios/security-fundamentals/security-descriptors/dacl-evaluation.md).

### `sd remove`

Drops every DACL rule — allow and deny — for the named principals.

```
$ sd remove ./report.txt bob carol
```

| Flag | Effect |
|---|---|
| `--allow-empty` | Permit the result to be a present-but-empty DACL, which denies everyone. Without this, `sd` refuses to produce one. |
| `--recursive`, `-r` | Apply to every descendant. |

## Auditing

### `sd audit` and `sd unaudit`

The SACL holds audit rules — see [The SACL](/peios/security-fundamentals/security-descriptors/the-sacl.md). `sd audit` adds one; `sd unaudit` drops every SACL rule for the named principals.

```
$ sd audit ./secrets.db Everyone:write:failure
```

An audit spec is `PRINCIPAL:PERMS:WHEN`, where `WHEN` is `success`, `failure`, or `both` — which outcomes to log. `sd audit` takes the same `--flags`, `--if`, `--replace`, and `-r` options as `sd allow`.

## Ownership

### `sd owner` and `sd group`

Set the descriptor's owner or group SID.

```
$ sd owner ./report.txt alice
$ sd group ./report.txt Administrators
```

Both take a single `PRINCIPAL` and accept `-r`. Changing an owner is itself an access-controlled act — see [Ownership](/peios/security-fundamentals/security-descriptors/ownership.md).

## Integrity

### `sd integrity`

Sets the file's mandatory integrity label.

```
$ sd integrity ./report.txt high
```

The level is one of `untrusted`, `low`, `medium`, `medium-plus`, `high`, `system`, `protected`. The standard catalog is five levels (`untrusted`, `low`, `medium`, `high`, `system`); `medium-plus` (RID 8448) and `protected` (RID 20480) are non-standard Windows-compatibility levels — the kernel compares any `S-1-16-<rid>` numerically.

| Flag | Effect |
|---|---|
| `--policy BITS` | The label's policy bits, comma-separated: `NW` no write-up, `NR` no read-up, `NX` no execute-up. |
| `--recursive`, `-r` | Apply to every descendant. |

For what the label does, see [Mandatory integrity control](/peios/security-fundamentals/access-decisions/mandatory-integrity-control.md).

## Inheritance

### `sd inherit`

Turns inheritance protection on or off — the `+` mark `ls -l` shows.

```
$ sd inherit off ./report.txt    # lock the file; stop inheriting
$ sd inherit on  ./report.txt    # let it inherit from its parent again
```

`inherit on` lets the file inherit rules from its parent directory. `inherit off` protects the file — it keeps its current rules and stops tracking the parent.

| Flag | Effect |
|---|---|
| `--strip-inherited` | When turning protection off, also drop the inherited rules already on the file. |
| `--recursive`, `-r` | Apply to every descendant. |

### `sd reset`

Drops the file's own explicit rules and rebuilds its DACL purely from what the parent directory hands down — returning the file to "inherits everything".

### `sd propagate`

Pushes this directory's inheritable rules down into its descendants, refreshing what they inherit. Use it after changing a directory's rules so the children pick up the change.

See [Inheritance](/peios/security-fundamentals/security-descriptors/inheritance.md) for the full model.

## Replacing the whole descriptor

### `sd set`

Replaces the entire descriptor in one step.

```
$ sd set ./report.txt 'O:BAG:BAD:P(A;;FA;;;BA)(A;;0x1200a9;;;BU)'
```

| Argument / flag | Effect |
|---|---|
| `SDDL` | The new descriptor as an SDDL string. `-` reads it from standard input. |
| `--binary FILE` | Instead of SDDL, read the raw descriptor bytes from `FILE` (`-` for standard input). |
| `--components LIST` | Override which parts of the descriptor (owner, group, DACL, SACL) the operation writes. |

## Common flags

These apply across the subcommands:

| Flag | Effect |
|---|---|
| `--recursive`, `-r` | Apply the change to every descendant of the path. |
| `--no-follow-symlinks`, `-P` | Operate on a symbolic link itself, not the file it points to. |
| `--json` | Emit JSON instead of human-readable output. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The operation succeeded — or, for `sd check`, the access would be **allowed**. |
| non-zero | The operation failed, the path was unreachable, or — for `sd check` — the access would be **denied**. |

---

# Listing and paths

_Peios / Using Peios / Peiosutils / Listing and paths_

> The commands that show you what a directory contains and that inspect, resolve, and manipulate path names — ls, stat, pwd, readlink, realpath, and the rest.

Before you can do anything with a file you usually have to *find* it — see what a directory holds, learn one file's details, turn a relative name into an absolute one. This topic covers the commands for that: listing directories, inspecting a single file, and resolving path names.

This page names every command in the topic, says in one line what each is for, and points you at the page that covers it in full.

## The commands

| Command | Purpose |
|---|---|
| [`ls`](/peios/using-peios/peiosutils/listing-and-paths/ls.md) | List the contents of a directory. The everyday command for "what's in here?". |
| [`dir` and `vdir`](/peios/using-peios/peiosutils/listing-and-paths/dir-and-vdir.md) | `ls` with a fixed output style. `dir` lists in columns; `vdir` lists in long format. |
| [`stat`](/peios/using-peios/peiosutils/listing-and-paths/stat.md) | Show the detailed status of one file — its type, size, timestamps, and raw inode metadata. |
| [`pwd`](/peios/using-peios/peiosutils/listing-and-paths/pwd.md) | Print the directory you are currently working in. |
| [`readlink`](/peios/using-peios/peiosutils/listing-and-paths/readlink.md) | Print where a symbolic link points. |
| [`realpath`](/peios/using-peios/peiosutils/listing-and-paths/realpath.md) | Resolve a path to its canonical, absolute form — every symlink followed, every `.` and `..` removed. |
| [`basename`](/peios/using-peios/peiosutils/listing-and-paths/basename.md) | Strip the directory part off a path, leaving just the final name. |
| [`dirname`](/peios/using-peios/peiosutils/listing-and-paths/dirname.md) | The opposite of `basename` — strip the final name, leaving the directory. |
| [`pathchk`](/peios/using-peios/peiosutils/listing-and-paths/pathchk.md) | Check whether a path name is valid and portable before you try to create it. |
| [`dircolors`](/peios/using-peios/peiosutils/listing-and-paths/dircolors.md) | Produce the colour settings that `ls` uses when it colours its output. |

## These commands and file security

Most commands in this topic work purely on path strings — they never touch the file the path names. `basename`, `dirname`, `realpath`, and `pathchk` are all string operations.

Two report on real files, and they relate to file security differently:

- **`ls -l` surfaces security-descriptor information directly.** On Peios a file's access is governed by its **security descriptor** — the record of who owns the file and who may do what to it. The long listing shows the owner and the parts of the descriptor that fit in a one-line-per-file format.
- **`stat` is a low-level diagnostic.** It dumps the raw contents of a file's inode. The inode carries some numeric fields — an owner id, a group id, a mode value — that the Peios access model does **not** consult. `stat` reports them because it reports the inode verbatim, but they are not the file's access policy.

Neither command prints the full security descriptor; inspecting that is a job for the dedicated security tooling. If a line of `ls -l` looks unfamiliar, or you want to know why `stat` shows fields that "don't count", the explanation is in [Security descriptors](/peios/security-fundamentals/security-descriptors/overview.md).

## Where to start

If you want the everyday "what's in this directory" command and the meaning of every column it prints, read [`ls`](/peios/using-peios/peiosutils/listing-and-paths/ls.md).

If you need the full detail of one specific file, read [`stat`](/peios/using-peios/peiosutils/listing-and-paths/stat.md).

If you are writing a script and need to take a path apart or canonicalise it, the string commands — [`basename`](/peios/using-peios/peiosutils/listing-and-paths/basename.md), [`dirname`](/peios/using-peios/peiosutils/listing-and-paths/dirname.md), [`realpath`](/peios/using-peios/peiosutils/listing-and-paths/realpath.md) — are what you want.

---

# ls

_Peios / Using Peios / Peiosutils / Listing and paths_

> List the contents of a directory — the default listing, the Peios long format and its security-descriptor columns, and the sorting and formatting options.

`ls` lists the contents of a directory. It is the command you use to answer "what is in here?" — and, with the right options, "who owns these files, how big are they, and when were they last changed?".

```
ls [options] [file...]
```

Each `file` is a directory to list the contents of, or a single file to report on. With no `file` argument, `ls` lists the directory you are currently in.

```
$ ls
build.sh  notes.txt  projects
```

## The default listing

Run on its own, `ls` prints the names of the entries in a directory and nothing else. Three rules shape that default:

- **Hidden entries are skipped.** A name beginning with a dot (`.config`, `.cache`) is hidden. `ls` leaves hidden entries out unless you ask for them with [`-a` or `-A`](#choosing-what-to-list).
- **Entries are sorted by name.** Alphabetical order, case-sensitive. Other [sort orders](#sorting) are available.
- **The layout adapts to where output goes.** When `ls` is writing to a terminal it arranges names into columns sized to the terminal width. When output is redirected to a file or a pipe it prints one name per line, so the result is easy for another program to read.

Everything past this default is opt-in. The rest of this page is the options, grouped by what they do.

## The long format

`-l` (or `--long`) switches `ls` into the **long format**: one entry per line, each with its mode, owner, size, modification time, and name.

```
$ ls -l
total 28
d-- S-1-5-21-9f3a-1c4e-7b20-1001   4096 May 14 09:12 projects
-x- S-1-5-21-9f3a-1c4e-7b20-1001   8344 May 16 17:40 build.sh
--- S-1-5-21-9f3a-1c4e-7b20-1001    219 May 15 11:03 notes.txt
--+ S-1-5-18                      12048 May 10 22:15 policy.db
```

The `total` line at the top reports the combined disk space used by the listed files, measured in blocks.

Each entry line has five columns: **mode**, **owner**, **size**, **modification time**, **name**. There is no permission-bits column and no group column — the reason is below.

### The mode column

The mode column is three characters: `[type][executable][protected]`.

The **first character** is the file's type:

| Character | Type |
|---|---|
| `-` | Regular file |
| `d` | Directory |
| `l` | Symbolic link |
| `p` | Named pipe (FIFO) |
| `s` | Socket |
| `c` | Character device |
| `b` | Block device |

The **second character** is the executable mark. It is `x` when the entry is a regular file that is marked as an executable, and `-` otherwise (it is always `-` for directories, links, and other non-regular entries).

The executable mark describes the *file*, not your relationship to it. `x` means "this file is runnable code." It does **not** mean that you, specifically, are allowed to run it — whether a given principal may execute a file is a separate access decision made against the file's security descriptor. A file can show `x` and still be one you cannot run, and the reverse. See [Access decisions](/peios/security-fundamentals/access-decisions/overview.md) for how execution is actually authorised.

The **third character** is the protected mark. It is `+` when the file's access control is **inheritance-protected**, and `-` otherwise.

A `-` here means the file's access control is inherited from the directory it lives in: it tracks its parent. A `+` means the file's access control has been set deliberately and locked, so it no longer tracks the parent directory. The `+` is the signal that someone has tailored this file's security on purpose. See [Inheritance](/peios/security-fundamentals/security-descriptors/inheritance.md).

So in the example above: `projects` is a directory (`d--`); `build.sh` is an executable file (`-x-`); `notes.txt` is an ordinary file inheriting its security from its directory (`---`); and `policy.db` is an ordinary file whose access control has been protected (`--+`).

### Why there are no permission bits

A long listing does not carry a fixed block of permission characters. On Peios, what a principal may do to a file is decided by the file's **security descriptor** — a record that can contain any number of entries granting or denying specific rights to specific principals. There is no small, fixed set of bits that captures that, so `ls -l` does not pretend there is.

The mode column tells you the file's *type* and two *facts about it* (is it executable, is its security protected). To see *who may do what*, look at the security descriptor itself with [`stat`](/peios/using-peios/peiosutils/listing-and-paths/stat.md) or the dedicated security tooling. The concepts are in [Security descriptors](/peios/security-fundamentals/security-descriptors/overview.md).

### The owner column

The owner column is the **SID** of the file's owner — shown in full, in the `S-1-…` form. `ls` does not translate SIDs into account names; it prints the identifier exactly as the security descriptor stores it. A well-known owner such as the system itself appears as its fixed SID (`S-1-5-18`); an ordinary account appears as a longer domain-style SID.

For what a SID is and how to read one, see [SIDs](/peios/security-fundamentals/identity/sids.md). For what "owner" means and what it grants, see [Ownership](/peios/security-fundamentals/security-descriptors/ownership.md).

If `ls` cannot read a file's descriptor — for a dangling symlink, say — the owner column shows `?` and the mode column shows the type character followed by `??`.

## Choosing what to list

By default `ls` shows non-hidden entries, and for a directory argument it shows the directory's *contents*. These options change that.

| Option | Effect |
|---|---|
| `-a`, `--all` | Show hidden entries too, including `.` (the directory itself) and `..` (its parent). |
| `-A`, `--almost-all` | Show hidden entries, but leave out `.` and `..`. |
| `-d`, `--directory` | List a directory as an entry in its own right, instead of listing its contents. Useful with `-l` to see a directory's own owner and mode. |
| `-R`, `--recursive` | Descend into every subdirectory and list it too. |
| `-B`, `--ignore-backups` | Skip entries whose name ends in `~`. |
| `--ignore=PATTERN` | Skip entries whose name matches the shell pattern. May be given more than once. |
| `--hide=PATTERN` | Like `--ignore`, but overridden by `-a` or `-A` — so a hide rule can be cancelled by asking to see everything. |

## Sorting

`ls` sorts by name by default. These options change the sort key; `-r` reverses whatever order is in effect.

| Option | Sort order |
|---|---|
| `-t` | By modification time, newest first. |
| `-S` | By size, largest first. |
| `-X` | By file extension, alphabetically. |
| `-v` | By version: runs of digits in the name sort numerically, so `f2` comes before `f10`. |
| `-U` | No sorting at all — entries appear in the order the directory stores them. Fast for very large directories. |
| `-r`, `--reverse` | Reverse the current sort. |
| `--sort=WORD` | Choose the sort key by name: `none`, `time`, `size`, `extension`, `version`, or `width`. |
| `--group-directories-first` | List directories before other entries, with each group sorted normally. |

## Output layout

When several entries fit on a line, `ls` has to decide how to arrange them.

| Option | Layout |
|---|---|
| `-C` | Columns, filled top-to-bottom. The default when writing to a terminal. |
| `-x` | Columns, filled left-to-right (across the rows) instead. |
| `-1` | One entry per line. The default when output is not a terminal. |
| `-m` | All entries on as few lines as possible, separated by commas. |
| `-l`, `--long` | The long format described above. |
| `--format=WORD` | Choose the layout by name: `across`, `commas`, `horizontal`, `long`, `single-column`, or `vertical`. |
| `-w`, `--width=COLS` | Assume the terminal is `COLS` columns wide instead of detecting it. `0` means unlimited. |

## File-type indicators and colour

These options make a listing easier to scan by marking entries with a symbol or a colour.

| Option | Effect |
|---|---|
| `-F`, `--classify` | Append a type symbol to each name: `/` for a directory, `@` for a symlink, `\|` for a FIFO, `=` for a socket, and `*` for an executable file. |
| `--file-type` | The same, but without the `*` on executables. |
| `-p` | Append only the `/` on directories. |
| `--indicator-style=WORD` | Choose the indicator set: `none`, `slash`, `file-type`, or `classify`. |
| `--color[=WHEN]` | Colour entries by type. `WHEN` is `auto` (colour only when writing to a terminal — the usual choice), `always`, or `never`. |

The colours `ls` uses are configurable. [`dircolors`](/peios/using-peios/peiosutils/listing-and-paths/dircolors.md) generates the colour settings and explains how to install them.

## File sizes

In the long format, and with `-s`, sizes are printed in bytes by default. These options rescale them.

| Option | Effect |
|---|---|
| `-h`, `--human-readable` | Print sizes with a unit suffix — `4.0K`, `234M`, `56G` — using powers of 1024. |
| `--si` | Like `-h`, but using powers of 1000, so the suffixes are decimal. |
| `-s`, `--size` | Print the disk space each entry occupies, in blocks, before its name. |
| `-k`, `--kibibytes` | Use 1024-byte blocks for the size figures and directory totals. |
| `--block-size=SIZE` | Scale every size by `SIZE` (for example `--block-size=1M` to count in megabytes). |

## Timestamps

The long format shows the modification time by default. These options change which timestamp is shown — and, when sorting by time, which one is sorted on.

| Option | Timestamp |
|---|---|
| `-u` | Access time — when the file was last read. |
| `-c` | Status-change time — when the file's metadata last changed. |
| `--time=WORD` | Choose the timestamp by name: `atime`/`access`, `ctime`/`status`, `mtime`/`modification`, or `birth`/`creation`. |
| `--time-style=STYLE` | Choose the date format: `full-iso`, `long-iso`, `iso`, `locale`, or `+FORMAT` for a custom layout. |
| `--full-time` | Shorthand for the long format with `--time-style=full-iso` — a complete, unabbreviated timestamp. |

## Symbolic links

By default `ls` reports on a symbolic link itself — its type is `l`, its size is the length of the link text. These options make it report on the link's *target* instead.

| Option | Effect |
|---|---|
| `-L`, `--dereference` | Always report on the file a link points to, not the link. |
| `-H`, `--dereference-command-line` | Dereference only the links named directly on the command line, not links found inside a listed directory. |
| `--dereference-command-line-symlink-to-dir` | Dereference a command-line link only when it points to a directory. |

[`readlink`](/peios/using-peios/peiosutils/listing-and-paths/readlink.md) and [`realpath`](/peios/using-peios/peiosutils/listing-and-paths/realpath.md) are the dedicated tools for inspecting and resolving links.

## Other options

The long-tail options, each in one line.

| Option | Effect |
|---|---|
| `-i`, `--inode` | Print each entry's index number (its inode number). |
| `-Z`, `--context` | Print each entry's security context. Available only when the build enables it. |
| `-b`, `--escape` | Print non-printable characters in names using C-style backslash escapes. |
| `-q`, `--hide-control-chars` | Replace non-printable characters in names with `?`. The default when writing to a terminal. |
| `--show-control-chars` | Print names verbatim, control characters and all. |
| `-N`, `--literal` | Print names exactly, with no quoting. |
| `-Q`, `--quote-name` | Wrap each name in double quotes. |
| `--quoting-style=WORD` | Choose the quoting scheme: `literal`, `shell`, `shell-always`, `shell-escape`, `c`, `escape`, and others. |
| `-T`, `--tabsize=COLS` | Assume tab stops every `COLS` columns when laying out output. |
| `-f` | List everything, unsorted, including hidden entries — equivalent to `-aU`. Also turns colour off unless `--color` is given explicitly. |
| `--hyperlink[=WHEN]` | Emit terminal hyperlinks for file names, so a capable terminal can make them clickable. |
| `-D`, `--dired` | Emit extra position markers designed for the Emacs *dired* editing mode. |
| `--zero` | End each line with a NUL character instead of a newline, and list one entry per line. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success — everything requested was listed. |
| `1` | A minor problem — for example, a named file could not be accessed. The rest of the listing still printed. |
| `2` | A serious problem — for example, a directory could not be read, or an option was invalid. |

---

# dir and vdir

_Peios / Using Peios / Peiosutils / Listing and paths_

> Two commands that list directory contents with a fixed output style — dir always lists in columns, vdir always lists in long format.

`dir` and `vdir` list the contents of a directory. They are [`ls`](/peios/using-peios/peiosutils/listing-and-paths/ls.md) with one decision already made for you: the output style is fixed instead of adapting to where the output is going.

```
dir [options] [file...]
vdir [options] [file...]
```

## What they fix

`ls` chooses its layout based on whether it is writing to a terminal — columns for a terminal, one name per line for a pipe or file. `dir` and `vdir` each pick one layout and always use it:

| Command | Layout | Equivalent `ls` |
|---|---|---|
| `dir` | Columns, regardless of where output goes. | `ls -C` |
| `vdir` | Long format, regardless of where output goes. | `ls -l` |

Both also default to a quoting style that escapes unusual characters in names without wrapping every name in quotes.

That is the *only* difference. The fixed style is just a default — pass an explicit format option (`-1`, `-m`, `-l`, `-C`, …) and it overrides the built-in choice, so `dir -l` produces a long listing and `vdir -C` produces columns.

## Everything else is `ls`

`dir` and `vdir` accept every option `ls` accepts and behave identically in every other respect — sorting, filtering, the long-format columns, colour, indicators, sizes, timestamps. `vdir`'s long format is the same Peios long format documented for `ls`, with the `[type][executable][protected]` mode column and the owner SID.

For the full option set and the meaning of every long-format column, see [`ls`](/peios/using-peios/peiosutils/listing-and-paths/ls.md). This page exists only to explain how `dir` and `vdir` differ from it — and the answer is "they pin the output style."

## Exit status

The same as `ls`:

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A minor problem — a named file could not be accessed. |
| `2` | A serious problem — a directory could not be read, or an option was invalid. |

---

# stat

_Peios / Using Peios / Peiosutils / Listing and paths_

> Display the detailed status of a file or a file system — type, size, timestamps, and raw inode metadata — with an optional custom output format.

`stat` displays the detailed status of a file. Where [`ls`](/peios/using-peios/peiosutils/listing-and-paths/ls.md) gives you a line per file, `stat` gives you everything the system records about *one* file: its type, its size, its timestamps, and the low-level metadata stored in its inode.

```
stat [options] file...
```

`stat` is a diagnostic tool. It dumps what a file's inode physically contains, verbatim. That makes it the right command for "tell me exactly what is recorded about this file" — and it means some of what it prints needs a word of explanation, below.

## The default output

With no options, `stat` prints a labelled, multi-line block per file:

```
$ stat notes.txt
  File: notes.txt
  Size: 219             Blocks: 8          IO Block: 4096   regular file
Device: 8,2     Inode: 1572931     Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/    jack)   Gid: ( 1000/    jack)
Access: 2026-05-15 11:03:42.000000000 +0000
Modify: 2026-05-15 11:03:42.000000000 +0000
Change: 2026-05-15 11:03:18.000000000 +0000
 Birth: 2026-05-15 11:03:18.000000000 +0000
```

Reading it line by line:

- **File** — the name, and for a symbolic link, what it points to.
- **Size / Blocks / IO Block / type** — the size in bytes, the number of allocated disk blocks, the preferred I/O block size, and the file type in words.
- **Device / Inode / Links** — the device the file lives on, its inode number (its unique index within that device), and the number of hard links to it.
- **Access / Uid / Gid** — a numeric owner id, a group id, and a mode value. See the caveat below — these are not the access policy.
- **Access / Modify / Change / Birth** — four timestamps: when the file was last read, last written, last had its metadata changed, and first created.

## The Uid, Gid, and mode fields are decorative

The `Access: (0644/-rw-r--r--)  Uid: (…)  Gid: (…)` line needs care.

A file's inode carries a numeric owner id, a numeric group id, and a mode value, and `stat` reports them because `stat` reports the inode verbatim. But **the Peios access model does not consult these fields.** What a principal may do to a file is decided by the file's **security descriptor** — see [Security descriptors](/peios/security-fundamentals/security-descriptors/overview.md). The numbers on the `Uid`/`Gid`/mode line are inert metadata: they are stored, they are reported, and they have no effect on any access decision.

Do not read that line as a permission summary. A file showing `0644` is not "world-readable" in any meaningful sense — whether anyone can read it depends entirely on its security descriptor. `stat` shows these fields for completeness as a low-level diagnostic; it does not claim they mean anything.

To see the parts of a file's security that *do* count, use [`ls -l`](/peios/using-peios/peiosutils/listing-and-paths/ls.md), which shows the owner SID and the mode column built from the security descriptor.

## Custom output formats

Two options replace the default block with output you control.

| Option | Effect |
|---|---|
| `-c FORMAT`, `--format=FORMAT` | Print `FORMAT` for each file, substituting `%` directives. A newline is added after each file. |
| `--printf=FORMAT` | Like `--format`, but interpret backslash escapes (`\n`, `\t`) in `FORMAT` and add no trailing newline. Include `\n` yourself if you want one. |

```
$ stat --format='%n is %s bytes' notes.txt
notes.txt is 219 bytes
```

### File directives

Used in the format string when stating files (the default mode):

| Directive | Substitutes |
|---|---|
| `%n` | File name. |
| `%N` | Quoted file name; for a symlink, with the target shown. |
| `%F` | File type in words (`regular file`, `directory`, …). |
| `%s` | Total size, in bytes. |
| `%b` | Number of allocated blocks. |
| `%B` | Size in bytes of each block counted by `%b`. |
| `%o` | Preferred I/O transfer block size. |
| `%i` | Inode number. |
| `%h` | Number of hard links. |
| `%d` / `%D` | Device number, in decimal / in hexadecimal. |
| `%t` / `%T` | For a device file, the major / minor device type, in hexadecimal. |
| `%m` | Mount point of the file system the file is on. |
| `%f` | Raw mode value, in hexadecimal. |
| `%x` / `%X` | Last access time — human-readable / seconds since the epoch. |
| `%y` / `%Y` | Last modification time — human-readable / seconds since the epoch. |
| `%z` / `%Z` | Last status-change time — human-readable / seconds since the epoch. |
| `%w` / `%W` | File creation (birth) time — human-readable / seconds since the epoch. `-` or `0` if unknown. |

The directives `%a` and `%A` (the mode in octal and in symbolic form) and `%u`, `%U`, `%g`, `%G` (the owner and group, numeric and by name) report the decorative inode fields described above. They are available for completeness; they are not the file's access policy.

The `%C` directive (security context) is inactive — it produces no meaningful value on a standard Peios system.

### File-system directives

With `-f`, `stat` reports on the *file system* a file lives on rather than the file itself, and the format string uses a different directive set:

| Directive | Substitutes |
|---|---|
| `%n` | File name. |
| `%i` | File-system ID, in hexadecimal. |
| `%t` / `%T` | File-system type — in hexadecimal / in words. |
| `%l` | Maximum length of a file name. |
| `%s` | Block size, for fast transfers. |
| `%S` | Fundamental block size, used for the block counts. |
| `%b` | Total data blocks in the file system. |
| `%f` | Free blocks. |
| `%a` | Free blocks available to an ordinary principal. |
| `%c` | Total inodes (file nodes). |
| `%d` | Free inodes. |

## Options

| Option | Effect |
|---|---|
| `-f`, `--file-system` | Report on the file system containing each file, instead of the file. |
| `-L`, `--dereference` | Follow symbolic links — report on the link's target rather than the link itself. |
| `-t`, `--terse` | Print the information on a single line, as bare values with no labels. Useful for scripts. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every file was stated successfully. |
| `1` | A file could not be stated, or an option or format directive was invalid. |

---

# pwd

_Peios / Using Peios / Peiosutils / Listing and paths_

> Print the absolute path of the directory you are currently working in.

`pwd` — "print working directory" — prints the absolute path of the directory you are currently in.

```
pwd [options]
```

```
$ pwd
/home/jack/projects
```

Every process has a working directory: the directory that relative path names are interpreted against. `pwd` tells you what yours is.

## Physical and logical paths

There are two honest answers to "where am I", and they differ only when symbolic links are involved.

Suppose `/home/jack/work` is a symbolic link pointing at `/data/projects/jack`, and you move into it. The **physical** path — the real location, with every link resolved — is `/data/projects/jack`. The **logical** path — the route you took to get there — is `/home/jack/work`.

| Option | Path printed |
|---|---|
| `-P`, `--physical` | The physical path: every symbolic link resolved to its target. |
| `-L`, `--logical` | The logical path: the route recorded as you navigated, taken from the `PWD` environment variable when it is accurate. |

By default `pwd` prints the **physical** path — `-P` is the default, and naming it explicitly just makes that choice visible. Use `-L` when you want the path as you navigated it, links and all.

If `-L` is requested but the recorded `PWD` value is missing or does not actually match the current directory, `pwd` falls back to the physical path rather than print something wrong.

## A note on shells

Many command shells provide their own built-in `pwd`, and the shell's version is what runs when you type `pwd` at a prompt. The two behave the same for everyday use. To be certain you are running this command rather than the shell built-in, invoke it by its full path.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The working directory was printed. |
| `1` | The working directory could not be determined or could not be printed. |

---

# readlink

_Peios / Using Peios / Peiosutils / Listing and paths_

> Print the target of a symbolic link, or canonicalise a path by resolving every link in it.

`readlink` prints where a symbolic link points.

```
readlink [options] file...
```

A symbolic link is a file whose contents are another path. `readlink` reads that path and prints it:

```
$ readlink /home/jack/work
/data/projects/jack
```

By default `readlink` resolves exactly one level: it prints the link's immediate target, whether or not that target is itself a link, and whether or not it exists. If the named file is not a symbolic link, `readlink` prints nothing and reports failure.

## Canonicalising a whole path

The canonicalise options change `readlink` from "read this one link" into "resolve this entire path to its real, absolute form" — following every symbolic link in every component, collapsing `.` and `..`. The three differ only in how strict they are about components existing:

| Option | Resolves | Existence requirement |
|---|---|---|
| `-f`, `--canonicalize` | Every link in the path. | Every component except the last must exist. |
| `-e`, `--canonicalize-existing` | Every link in the path. | Every component must exist. |
| `-m`, `--canonicalize-missing` | Every link in the path. | No component need exist. |

With any of these, `readlink` succeeds on an ordinary (non-link) file too — it simply returns the canonical path. [`realpath`](/peios/using-peios/peiosutils/listing-and-paths/realpath.md) is the dedicated command for this canonicalising job and has more options for it; the canonicalise flags here exist so `readlink` can do it without a second tool.

## Output and error control

| Option | Effect |
|---|---|
| `-n`, `--no-newline` | Do not print the trailing newline. Ignored, with a warning, when more than one file is given. |
| `-z`, `--zero` | End each output line with a NUL character instead of a newline. |
| `-q`, `--quiet` / `-s`, `--silent` | Suppress most error messages. This is the default. |
| `-v`, `--verbose` | Report error messages that the quiet default would suppress. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every named file was resolved and printed. |
| `1` | A file was not a symbolic link, or a path could not be resolved under the chosen strictness. |

---

# realpath

_Peios / Using Peios / Peiosutils / Listing and paths_

> Resolve a path to its canonical, absolute form — every symbolic link followed and every . and .. component removed.

`realpath` resolves a path to its **canonical** form: absolute, with every symbolic link followed and every `.` and `..` component removed. Given any path, it tells you the one true location it refers to.

```
realpath [options] file...
```

```
$ realpath ../work/./notes.txt
/data/projects/jack/notes.txt
```

A single file can be named many ways — through links, through relative paths, with redundant components. `realpath` reduces all of them to the single canonical path, which makes it the right tool for comparing two paths, or for turning a path a user typed into one a script can rely on.

## How much must exist

By default `realpath` requires every component of the path *except the last* to exist. These options change that requirement:

| Option | Existence requirement |
|---|---|
| (default) | Every component except the last must exist. |
| `-e`, `--canonicalize-existing` | Every component must exist — including the final one. |
| `-m`, `--canonicalize-missing` | No component need exist. The path is canonicalised purely as text where it cannot be walked. |

## How symbolic links are handled

| Option | Effect |
|---|---|
| `-P`, `--physical` | Resolve each symbolic link as it is encountered. This is the default. |
| `-L`, `--logical` | Resolve `..` components *before* resolving the symbolic links they follow. |
| `-s`, `--strip`, `--no-symlinks` | Do not resolve symbolic links at all — only remove `.` and `..` components. The result is canonical as text, but a link in the path is left as-is. |

## Output options

| Option | Effect |
|---|---|
| `--relative-to=DIR` | Print the result as a path relative to `DIR` instead of as an absolute path. |
| `--relative-base=DIR` | Print an absolute path, unless the result lies inside `DIR`, in which case print it relative to `DIR`. |
| `-z`, `--zero` | End each output line with a NUL character instead of a newline. |
| `-q`, `--quiet` | Do not print a warning when a path is invalid. The exit status still reports the failure. |

## `realpath` and `readlink`

[`readlink`](/peios/using-peios/peiosutils/listing-and-paths/readlink.md) with `-f`/`-e`/`-m` does the same canonicalising job. The difference is focus: `readlink` is primarily for reading a single link's target, with canonicalising as an extra; `realpath` is built for canonicalising and carries the richer option set — relative output, the symlink-handling modes, the strip-only mode. Use `realpath` when canonicalising is the actual goal.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every path was resolved and printed. |
| `1` | A path could not be resolved under the chosen options. |

---

# basename

_Peios / Using Peios / Peiosutils / Listing and paths_

> Strip the directory part off a path, leaving just the final name — and optionally remove a trailing suffix.

`basename` takes a path and prints just its final component — the file name, with all the leading directories removed.

```
basename name [suffix]
basename [options] name...
```

```
$ basename /home/jack/projects/notes.txt
notes.txt
```

It is a pure text operation: `basename` never looks at the file system. It works on the string you give it, so the path need not exist.

## Removing a suffix

Give a second argument and `basename` also strips that suffix from the end of the name — handy for turning a file name into a bare stem:

```
$ basename /home/jack/projects/notes.txt .txt
notes
```

The suffix is only removed if it is actually there, and never if it would leave an empty string.

## Options

`basename` takes one name by default. The options let it process several at once.

| Option | Effect |
|---|---|
| `-a`, `--multiple` | Treat every argument as a name to process, instead of treating the second argument as a suffix. Required to pass more than one name. |
| `-s`, `--suffix=SUFFIX` | Remove `SUFFIX` from each name. Implies `-a`, so it applies to every name given. |
| `-z`, `--zero` | End each output line with a NUL character instead of a newline. |

```
$ basename -s .txt notes.txt readme.txt changelog.txt
notes
readme
changelog
```

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A usage error — a missing or extra operand. |

---

# dirname

_Peios / Using Peios / Peiosutils / Listing and paths_

> Strip the final component off a path, leaving the directory part.

`dirname` takes a path and prints everything *except* its final component — the directory the name lives in. It is the counterpart of [`basename`](/peios/using-peios/peiosutils/listing-and-paths/basename.md).

```
dirname [options] name...
```

```
$ dirname /home/jack/projects/notes.txt
/home/jack/projects
```

Like `basename`, it is a pure text operation — `dirname` never touches the file system, so the path need not exist.

## How it handles edge cases

- If the name has no `/` in it at all, there is no directory part, so `dirname` prints `.` — the current directory.
- Trailing slashes on the name are ignored before the final component is removed.

```
$ dirname notes.txt
.
$ dirname /home/jack/projects/
/home
```

## Multiple names

`dirname` accepts any number of names and prints the directory part of each on its own line:

```
$ dirname /etc/hosts /var/log/messages report.txt
/etc
/var/log
.
```

## Options

| Option | Effect |
|---|---|
| `-z`, `--zero` | End each output line with a NUL character instead of a newline. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A usage error — no name was given. |

---

# pathchk

_Peios / Using Peios / Peiosutils / Listing and paths_

> Check whether path names are valid and usable before you try to create the files they name.

`pathchk` checks whether a path name is valid and usable. It tells you, *before* you try to create a file, whether the name would be rejected — because it is too long, contains an unusable component, or could not be reached.

```
pathchk [options] name...
```

```
$ pathchk /home/jack/projects/notes.txt
$ echo $?
0
```

`pathchk` prints nothing when a name is fine; it prints a diagnostic and fails when a name is not. It is meant for scripts that build up path names and want to fail early, with a clear message, instead of discovering the problem halfway through an operation.

Like the other name commands in this topic, `pathchk` does not require the file to exist — it is checking the *name*, not looking for the file.

## What the default check covers

With no options, for each name `pathchk` checks:

- that the name is not empty;
- that no part of the name exceeds the length limits of the file systems the path would actually touch;
- that the leading directories of the name can be searched.

## Stricter checks

The options replace "valid on *this* system" with "valid across a wide range of systems" — useful when a name has to work somewhere other than where you are checking it.

| Option | Adds |
|---|---|
| `-p` | Check against a fixed, conservative set of length limits instead of the local file system's, and flag characters that are not broadly portable. |
| `-P` | Reject an empty name, and reject any component that begins with `-`. |
| `--portability` | Apply both `-p` and `-P` — the strictest check. |

A name with a leading `-` on a component is worth catching: many commands would read it as an option rather than a file name. `-P` flags exactly that class of trouble.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every name passed every requested check. |
| `1` | At least one name failed. A diagnostic naming the problem was printed. |

---

# dircolors

_Peios / Using Peios / Peiosutils / Listing and paths_

> Produce the shell commands that set LS_COLORS — the variable that controls how ls colours its output.

When [`ls`](/peios/using-peios/peiosutils/listing-and-paths/ls.md) colours its output, it reads the colours from an environment variable named `LS_COLORS`. `dircolors` is the command that produces the value of that variable.

```
dircolors [options] [file]
```

`dircolors` does not set the variable itself — a command cannot change its parent shell's environment. Instead it *prints shell commands* that, when run by the shell, set `LS_COLORS`. The usual way to use it is to have the shell evaluate that output:

```
eval "$(dircolors)"
```

Put that line in a shell startup file and every `ls --color` afterwards picks up the colours. With no arguments, `dircolors` uses a built-in default colour scheme.

## Choosing the shell syntax

The commands `dircolors` prints have to match the shell that will run them. By default it guesses the syntax from the `SHELL` environment variable; these options state it outright.

| Option | Output syntax |
|---|---|
| `-b`, `--sh`, `--bourne-shell` | Bourne-shell-family syntax. |
| `-c`, `--csh`, `--c-shell` | C-shell-family syntax. |

If no shell option is given and `SHELL` is not set, `dircolors` cannot guess and reports an error.

## Customising the colours

To change the colours, you start from the default scheme, edit it, and feed it back.

| Option | Effect |
|---|---|
| `-p`, `--print-database` | Print the built-in colour database in its editable source form, instead of shell commands. |
| `--print-ls-colors` | Print the colours fully escaped, one per line, for inspection. |

The workflow is:

```
dircolors -p > ~/.dircolors      # save the default scheme to a file
# ...edit ~/.dircolors to taste...
eval "$(dircolors ~/.dircolors)" # load the edited scheme
```

When `dircolors` is given a `file` argument, it reads the colour definitions from that file instead of using the built-in database. The file maps file types and name extensions to colours; `dircolors -p` shows the format, with each line commented.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A usage error, or a colour-definition file that could not be read or parsed. |

---

# Viewing and joining text

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> The commands that print files to the screen, show parts of them, and combine or split text by line and by column.

Much of the work at a command line is reading text — looking at a file, checking the start or end of one, pulling a column out, stitching two files together. This topic covers the commands for that: viewing files, showing parts of them, and joining or splitting text by line and by column.

This page names every command in the topic. The companion topic, [Transforming text](/peios/using-peios/peiosutils/transforming-text/overview.md), covers the commands that *reshape* text — sorting, de-duplicating, substituting characters.

## The commands

**Viewing a whole file**

| Command | Purpose |
|---|---|
| [`cat`](/peios/using-peios/peiosutils/viewing-and-joining-text/cat.md) | Print one or more files straight through — and join several into one. |
| [`tac`](/peios/using-peios/peiosutils/viewing-and-joining-text/tac.md) | Print a file with its lines in reverse — last line first. |
| [`more`](/peios/using-peios/peiosutils/viewing-and-joining-text/more.md) | Show a file one screen at a time, pausing between screens. |
| [`od`](/peios/using-peios/peiosutils/viewing-and-joining-text/od.md) | Dump a file's raw bytes in octal, hex, decimal, or character form. |

**Viewing part of a file**

| Command | Purpose |
|---|---|
| [`head`](/peios/using-peios/peiosutils/viewing-and-joining-text/head.md) | Print the first lines (or bytes) of a file. |
| [`tail`](/peios/using-peios/peiosutils/viewing-and-joining-text/tail.md) | Print the last lines of a file — and, with `-f`, keep printing as the file grows. |
| [`nl`](/peios/using-peios/peiosutils/viewing-and-joining-text/nl.md) | Print a file with its lines numbered. |

**Cutting and combining**

| Command | Purpose |
|---|---|
| [`cut`](/peios/using-peios/peiosutils/viewing-and-joining-text/cut.md) | Pull selected columns — byte ranges or delimited fields — out of each line. |
| [`paste`](/peios/using-peios/peiosutils/viewing-and-joining-text/paste.md) | Join files side by side, line for line. |
| [`join`](/peios/using-peios/peiosutils/viewing-and-joining-text/join.md) | Join two files on a shared field, like a database join. |
| [`comm`](/peios/using-peios/peiosutils/viewing-and-joining-text/comm.md) | Compare two sorted files and report which lines they share. |

**Splitting a file into files**

| Command | Purpose |
|---|---|
| [`split`](/peios/using-peios/peiosutils/viewing-and-joining-text/split.md) | Break a file into equal-sized pieces. |
| [`csplit`](/peios/using-peios/peiosutils/viewing-and-joining-text/csplit.md) | Break a file into pieces at lines that match a pattern. |

## A note on input

Almost every command here follows the same convention for where its text comes from. Name one or more files and it reads those; name none and it reads from standard input, so it can sit in the middle of a pipeline. Where a command accepts files, the name `-` stands for standard input, so you can mix files and piped input in one invocation.

## Where to start

To simply print a file, or join a few together, read [`cat`](/peios/using-peios/peiosutils/viewing-and-joining-text/cat.md) — the most-used command in the topic.

---

# cat

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Print files to standard output, and concatenate several files into one stream.

`cat` prints the contents of files. Its name is short for "concatenate" — given several files, it prints them one after another, as a single stream.

```
cat [options] [file...]
```

```
$ cat notes.txt
$ cat part1.txt part2.txt part3.txt > whole.txt
```

With no file — or with the name `-` — `cat` reads standard input, which is what makes it useful in a pipeline or for capturing typed input into a file.

## Plain use

Most of the time `cat` is run with no options at all: it copies its input to its output, byte for byte, unchanged. The options exist for the times you want to *see* something about the text that is normally invisible, or to adjust spacing and numbering.

## Numbering lines

| Option | Effect |
|---|---|
| `-n`, `--number` | Number every output line. |
| `-b`, `--number-nonblank` | Number only the non-empty lines. Overrides `-n`. |

## Making invisible characters visible

These options reveal characters that normally print as nothing, or as whitespace — useful when a file is not behaving and you suspect a stray tab or control character.

| Option | Effect |
|---|---|
| `-E`, `--show-ends` | Print a `$` at the end of each line, so trailing spaces become visible. |
| `-T`, `--show-tabs` | Print tab characters as `^I` instead of as whitespace. |
| `-v`, `--show-nonprinting` | Print control and other non-printing characters using `^` and `M-` notation (leaving newline and tab as they are). |
| `-e` | Shorthand for `-vE`. |
| `-t` | Shorthand for `-vT`. |
| `-A`, `--show-all` | Shorthand for `-vET` — show ends, tabs, and all non-printing characters at once. |

## Adjusting spacing

| Option | Effect |
|---|---|
| `-s`, `--squeeze-blank` | Collapse runs of blank lines into a single blank line. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every file was printed. |
| `1` | A file could not be read. |

---

# tac

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Print files with their lines in reverse order — last line first.

`tac` prints a file with its lines in reverse — the last line first, the first line last. The name is `cat` spelled backwards, which is exactly what it does.

```
tac [options] [file...]
```

```
$ tac events.log
```

Reversing a log file so the newest entries come first is the everyday use. With no file, `tac` reads standard input.

## Reversing on something other than lines

`tac` does not have to split on newlines. You can tell it to treat some other string as the separator, and it will reverse the order of the pieces between those separators.

| Option | Effect |
|---|---|
| `-s`, `--separator=STRING` | Use `STRING` as the separator between records, instead of a newline. |
| `-r`, `--regex` | Treat the separator as a regular expression rather than a literal string. |
| `-b`, `--before` | Expect the separator *before* each record rather than after it. This matters for how the separator is reattached when the records are reordered. |

```
$ tac -s ', ' -r names.csv
```

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every file was printed. |
| `1` | A file could not be read, or the separator regular expression was invalid. |

---

# head

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Print the first lines or bytes of a file.

`head` prints the **beginning** of a file — by default, the first 10 lines.

```
head [options] [file...]
```

```
$ head config.toml
```

It is the quick way to glance at the top of a file without printing the whole thing — a header, the first few records, the start of a log. With no file, `head` reads standard input.

## Choosing how much

| Option | Effect |
|---|---|
| `-n`, `--lines=NUM` | Print the first `NUM` lines instead of 10. |
| `-c`, `--bytes=NUM` | Print the first `NUM` bytes instead of counting lines. |

`NUM` accepts a unit suffix (`K`, `M`, …) when counting bytes.

### Counting from the end

If `NUM` is given a leading `-`, the meaning flips: `head` prints **everything except the last `NUM`**.

```
$ head -n -5 report.txt    # all but the final 5 lines
```

## Headers for multiple files

When given more than one file, `head` prints a header line before each one so you can tell them apart. Two options override that:

| Option | Effect |
|---|---|
| `-q`, `--quiet` | Never print the file-name headers. |
| `-v`, `--verbose` | Always print the header, even for a single file. |

## Other options

| Option | Effect |
|---|---|
| `-z`, `--zero-terminated` | Treat the NUL character as the line delimiter instead of newline. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every file was printed. |
| `1` | A file could not be read. |

---

# tail

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Print the last lines of a file — and keep printing as the file grows.

`tail` prints the **end** of a file — by default, the last 10 lines.

```
tail [options] [file...]
```

```
$ tail server.log
```

It is the counterpart of [`head`](/peios/using-peios/peiosutils/viewing-and-joining-text/head.md): the quick way to see the most recent lines of a file. With no file, `tail` reads standard input.

## Choosing how much

| Option | Effect |
|---|---|
| `-n`, `--lines=NUM` | Print the last `NUM` lines instead of 10. |
| `-c`, `--bytes=NUM` | Print the last `NUM` bytes instead of counting lines. |

If `NUM` is given a leading `+`, the meaning flips: `tail` prints **everything from line `NUM` onward**.

```
$ tail -n +20 report.txt    # from line 20 to the end
```

## Following a growing file

`tail`'s most useful trick is `-f`. Instead of printing the end and exiting, it stays running and prints new lines as they are appended — the standard way to watch a log file live.

| Option | Effect |
|---|---|
| `-f`, `--follow` | Keep the file open and print new data as it is added. |
| `-F` | Follow the file *by name*, and keep retrying if it is missing. Equivalent to `--follow=name --retry`. This survives log rotation — when the file is replaced, `-F` picks up the new one. |
| `--retry` | Keep trying to open a file that is not yet accessible. |
| `--pid=PID` | While following, stop once process `PID` exits. |
| `-s`, `--sleep-interval=N` | Wait `N` seconds between checks of the file. |

Press Ctrl-C to stop a `tail -f`.

## Headers for multiple files

As with `head`, when given more than one file `tail` prints a header before each.

| Option | Effect |
|---|---|
| `-q`, `--quiet` | Never print the file-name headers. |
| `-v`, `--verbose` | Always print the header, even for a single file. |

## Other options

| Option | Effect |
|---|---|
| `-z`, `--zero-terminated` | Treat the NUL character as the line delimiter instead of newline. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read. |

---

# more

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> View a file one screen at a time, pausing between screens.

`more` displays a file one screenful at a time. Where [`cat`](/peios/using-peios/peiosutils/viewing-and-joining-text/cat.md) prints a whole file at once — and a long file scrolls straight past — `more` shows the first screen, then waits for you before showing the next.

```
more [options] file...
```

```
$ more long-report.txt
```

## Moving through the file

While `more` is paused, it is waiting for a keypress:

| Key | Action |
|---|---|
| Space | Show the next screenful. |
| Return | Show the next line. |
| `q` | Quit. |
| `/` | Search forward for a string. |
| `h` | Show the built-in help. |

`more` moves *forward* through a file. It is the simple, always-available pager — enough for reading something through once.

## Options

| Option | Effect |
|---|---|
| `-n`, `--lines=NUM` | Show `NUM` lines per screenful instead of filling the terminal. `--number=NUM` is the same. |
| `-F`, `--from-line=NUM` | Begin displaying at line `NUM`. |
| `-P`, `--pattern=STRING` | Search for `STRING` and begin displaying at the first match. |
| `-e`, `--exit-on-eof` | Exit automatically at the end of the file, rather than waiting. |
| `-s`, `--squeeze` | Collapse runs of blank lines into one. |
| `-u`, `--plain` | Suppress underlining in the displayed text. |
| `-p`, `--print-over` | Clear the screen and print the next page, instead of scrolling. |
| `-c`, `--clean-print` | Redraw each page in place, cleaning line ends, instead of scrolling. |
| `-d`, `--silent` | When an unrecognised key is pressed, show a short hint instead of ringing the terminal bell. |
| `-l`, `--logical` | Do not pause when a line contains a form-feed character. |
| `-f`, `--no-pause` | Count logical lines rather than screen lines when deciding where to pause. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The file was displayed. |
| `1` | A file could not be opened. |

---

# nl

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Print files with their lines numbered, with control over which lines are counted and how the number is formatted.

`nl` prints a file with its lines numbered. [`cat -n`](/peios/using-peios/peiosutils/viewing-and-joining-text/cat.md) also numbers lines; `nl` is the command for when you need *control* over the numbering — which lines count, how the number looks, where it restarts.

```
nl [options] [file...]
```

```
$ nl chapter.txt
     1  Once the system is installed,
     2  the first boot brings up
       
     3  the configuration service.
```

## Which lines get a number

By default `nl` numbers only the non-empty lines. `-b` sets the rule:

| `-b STYLE` | Numbers… |
|---|---|
| `-b a` | every line. |
| `-b t` | only non-empty lines. This is the default. |
| `-b n` | no lines. |
| `-b pBRE` | only lines that match the basic regular expression `BRE`. |

| Option | Effect |
|---|---|
| `-b`, `--body-numbering=STYLE` | The numbering rule, as above. |
| `-l`, `--join-blank-lines=N` | Count a run of `N` blank lines as one numbered line. |

## How the number looks

| Option | Effect |
|---|---|
| `-n`, `--number-format=FORMAT` | `ln` = left-justified; `rn` = right-justified; `rz` = right-justified with leading zeros. |
| `-w`, `--number-width=N` | Use `N` columns for the number. |
| `-s`, `--number-separator=STRING` | Put `STRING` between the number and the line text. |
| `-v`, `--starting-line-number=N` | Start counting from `N`. |
| `-i`, `--line-increment=N` | Increase the number by `N` at each counted line. |

## Logical pages

`nl` can treat one file as a sequence of **logical pages**, each with a header, a body, and a footer, and number the three parts by different rules. Pages are separated by special delimiter lines in the input.

| Option | Effect |
|---|---|
| `-h`, `--header-numbering=STYLE` | Numbering style for header sections. |
| `-f`, `--footer-numbering=STYLE` | Numbering style for footer sections. |
| `-d`, `--section-delimiter=CC` | The characters that mark a section boundary in the input. |
| `-p`, `--no-renumber` | Do not reset the line number at the start of each logical page. |

The styles for `-h` and `-f` are the same `a` / `t` / `n` / `pBRE` set as `-b`. For an ordinary file with no delimiter lines, the whole file is one body and only `-b` matters.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read, or an option value was invalid. |

---

# cut

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Pull selected columns — byte ranges or delimited fields — out of each line of input.

`cut` extracts **columns** from text. For each line of input it prints only the parts you select, dropping the rest.

```
cut option... [file...]
```

```
$ cut -d , -f 1,3 people.csv     # the 1st and 3rd comma-separated field
$ cut -c 1-8 log.txt             # the first 8 characters of each line
```

Every run of `cut` needs two decisions: **what counts as a column**, and **which columns to keep**.

## What counts as a column

Choose exactly one mode:

| Option | A column is… |
|---|---|
| `-b`, `--bytes` | a single byte. |
| `-c`, `--characters` | a single character. |
| `-f`, `--fields` | a field — a stretch of text between delimiters. |

`-b` and `-c` cut by position. `-f` cuts by *field*, which is what you want for tabular data — a CSV, a table, the columns of a report.

## Which columns to keep

The mode option takes a **sequence**: numbers and inclusive ranges, separated by commas.

| Sequence | Selects |
|---|---|
| `3` | column 3 only. |
| `2,5,9` | columns 2, 5, and 9. |
| `5-7` | columns 5 through 7. |
| `3-` | column 3 to the end of the line. |
| `-4` | the start of the line through column 4. |
| `1,4-6,9` | any mixture of the above. |

| Option | Effect |
|---|---|
| `--complement` | Invert the selection — keep every column *except* those named. |

## Field mode: the delimiter

In field mode, `cut` needs to know what separates the fields.

| Option | Effect |
|---|---|
| `-d`, `--delimiter=CHAR` | The character that separates fields. The default is a tab. |
| `-w` | Separate fields on runs of whitespace (spaces and tabs) instead of a single character. Cannot be combined with `-d`. |
| `-s`, `--only-delimited` | Print only lines that actually contain the delimiter; drop lines that have no fields to cut. |
| `--output-delimiter=STRING` | Put `STRING` between the kept fields in the output, instead of repeating the input delimiter — handy for converting one delimited format to another. |

## Other options

| Option | Effect |
|---|---|
| `-z`, `--zero-terminated` | Treat the NUL character as the line delimiter, so a "line" may itself contain newlines. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A mode was missing or given twice, an option was misused, or a file could not be read. |

---

# paste

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Join files side by side, line for line.

`paste` joins files **side by side**. It takes the first line of each file and prints them on one line, then the second line of each, and so on — merging files into columns.

```
paste [options] [file...]
```

```
$ paste names.txt ages.txt
Ada      36
Grace    41
Linus    29
```

By default the merged pieces are separated by a tab. `paste` is the column-wise counterpart of [`cat`](/peios/using-peios/peiosutils/viewing-and-joining-text/cat.md), which joins files end to end.

## Pasting one file at a time

| Option | Effect |
|---|---|
| `-s`, `--serial` | Paste each file's lines onto a *single line*, one file at a time, instead of merging files in parallel. A file's lines become a row. |

```
$ paste -s -d , names.txt
Ada,Grace,Linus
```

## Choosing the separator

| Option | Effect |
|---|---|
| `-d`, `--delimiters=LIST` | Use the characters in `LIST` as separators instead of a tab. When `LIST` has more than one character, `paste` cycles through them. |
| `-z`, `--zero-terminated` | Treat the NUL character as the line delimiter instead of newline. |

## `paste` and `join`

`paste` merges by *position* — line 1 with line 1, line 2 with line 2, blind to content. When you want to merge by a *matching value* — pairing rows that share a key — that is [`join`](/peios/using-peios/peiosutils/viewing-and-joining-text/join.md).

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read, or the delimiter list was malformed. |

---

# join

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Join two files on a shared field, pairing lines that have the same key.

`join` merges two files on a **shared field**. For every pair of lines — one from each file — that have the same value in their join field, it prints a combined line. It is the command-line form of a database join.

```
join [options] file1 file2
```

```
$ join employees.txt salaries.txt
```

If `employees.txt` has `101 Ada` and `salaries.txt` has `101 80000`, `join` pairs them on the shared `101` and prints `101 Ada 80000`.

## The input must be sorted

`join` works by stepping through both files together, so **both files must be sorted on the join field**. If they are not, `join` will miss matches. Sort them first with [`sort`](/peios/using-peios/peiosutils/transforming-text/sort.md), on the same field `join` will use.

`join` checks the ordering as it goes and reports a file that is out of order; `--nocheck-order` turns that check off, and `--check-order` forces it on.

## Choosing the join field

By default `join` joins on the **first** field of each file, and fields are separated by whitespace.

| Option | Effect |
|---|---|
| `-1 FIELD` | Join on `FIELD` of file 1. |
| `-2 FIELD` | Join on `FIELD` of file 2. |
| `-j FIELD` | Join on `FIELD` of *both* files — shorthand for `-1 FIELD -2 FIELD`. |
| `-t CHAR` | Use `CHAR` as the field separator for input and output. |

## Unmatched lines

By default `join` prints only the lines that *paired*. A line with no match on the other side is dropped. These options bring unmatched lines back:

| Option | Effect |
|---|---|
| `-a FILENUM` | Also print unpaired lines from file `FILENUM` (`1` or `2`). |
| `-v FILENUM` | Print *only* the unpaired lines from file `FILENUM`, and suppress the joined output. |
| `-e EMPTY` | Fill in any missing field with the string `EMPTY`. |

## Shaping the output

| Option | Effect |
|---|---|
| `-o FORMAT` | Build each output line from the field list `FORMAT`, rather than the default layout. |
| `-i`, `--ignore-case` | Ignore letter case when comparing join fields. |
| `--header` | Treat the first line of each file as column headers — print them, paired, without trying to match them. |
| `-z`, `--zero-terminated` | Treat the NUL character as the line delimiter. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read, was not sorted, or an option was invalid. |

---

# comm

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Compare two sorted files line by line and report which lines they share.

`comm` compares two sorted files and tells you which lines are **unique to each** and which are **common to both**.

```
comm [options] file1 file2
```

By default it prints three columns:

```
$ comm list-a.txt list-b.txt
            apple
banana
                        cherry
date
```

- **Column 1** — lines only in `file1`.
- **Column 2** — lines only in `file2`.
- **Column 3** — lines in both.

Each column is indented past the one before, so a line's column position tells you where it was found.

## The input must be sorted

`comm` steps through both files together, so **both must be sorted**. On unsorted input the comparison is meaningless. Sort the files first with [`sort`](/peios/using-peios/peiosutils/transforming-text/sort.md).

`comm` checks the ordering and reports a file that is out of order. `--check-order` forces the check; `--nocheck-order` disables it.

## Showing only the columns you want

Each column can be switched off. The remaining columns close up to fill the gap.

| Option | Effect |
|---|---|
| `-1` | Suppress column 1 — hide lines unique to `file1`. |
| `-2` | Suppress column 2 — hide lines unique to `file2`. |
| `-3` | Suppress column 3 — hide lines common to both. |

These combine to answer specific questions. `comm -12` shows *only* the common lines — the intersection of the two files. `comm -23` shows lines in `file1` that are not in `file2` — the difference.

## Other options

| Option | Effect |
|---|---|
| `--output-delimiter=STR` | Separate the columns with `STR` instead of spaces. |
| `--total` | Print a final summary line with the count in each column. |
| `-z`, `--zero-terminated` | Treat the NUL character as the line delimiter. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read, or was not sorted. |

---

# split

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Break a file into equal-sized pieces, each written to its own output file.

`split` breaks one file into several smaller files of roughly equal size.

```
split [options] [input [prefix]]
```

```
$ split -l 1000 big.log
$ ls
xaa  xab  xac  xad
```

By default `split` writes 1000 lines per piece, names the pieces `xaa`, `xab`, `xac`, … — a `prefix` of `x` followed by a two-letter suffix — and reads its input from a named file or from standard input.

To put the file back together, concatenate the pieces in order: [`cat`](/peios/using-peios/peiosutils/viewing-and-joining-text/cat.md)` xaa xab xac … > original`.

Each piece is a newly created file, and a new file needs a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md); each piece inherits one from the directory it is created in.

## How big each piece is

Choose one of these to set the piece size:

| Option | Each piece holds… |
|---|---|
| `-l`, `--lines=N` | `N` lines. This is the default behaviour, with `N` = 1000. |
| `-b`, `--bytes=SIZE` | `SIZE` bytes. |
| `-C`, `--line-bytes=SIZE` | up to `SIZE` bytes, but only whole lines — never a line split across two pieces. |
| `-n`, `--number=CHUNKS` | the input divided into a fixed number of pieces (see below). |

A `SIZE` accepts a unit suffix: `K`, `M`, `G`, … as powers of 1024, or `KB`, `MB`, … as powers of 1000.

### Fixed number of chunks (`-n`)

`-n` divides the input into a set number of pieces rather than fixing each piece's size. `CHUNKS` may be:

| Form | Effect |
|---|---|
| `N` | Split into `N` pieces by size. |
| `l/N` | Split into `N` pieces without splitting any line across pieces. |
| `r/N` | Like `l/N`, but distribute lines round-robin across the pieces. |
| `K/N` | Write only the `K`th of `N` pieces, to standard output. |

## Naming the pieces

| Option | Effect |
|---|---|
| `prefix` (operand) | The leading part of each output name. Default `x`. |
| `-a`, `--suffix-length=N` | Use `N` characters for the suffix. Default 2. |
| `-d`, `--numeric-suffixes[=START]` | Use numeric suffixes (`00`, `01`, …) instead of letters, optionally from `START`. |
| `-x`, `--hex-suffixes[=START]` | Use hexadecimal suffixes. |
| `--additional-suffix=SUFFIX` | Append a fixed `SUFFIX` to every output name — for giving the pieces an extension. |

## Other options

| Option | Effect |
|---|---|
| `-e`, `--elide-empty-files` | With `-n`, do not write out pieces that would be empty. |
| `-t`, `--separator=SEP` | Use `SEP` as the line separator instead of newline. |
| `--verbose` | Print a line as each output file is opened. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The file was split. |
| `1` | A failure — the input could not be read, or the suffixes were exhausted. |

---

# csplit

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Break a file into pieces at lines chosen by line number or by a matching pattern.

`csplit` — "context split" — breaks a file into pieces at points *you* choose: a line number, or a line that matches a pattern. Where [`split`](/peios/using-peios/peiosutils/viewing-and-joining-text/split.md) cuts a file into equal sizes, `csplit` cuts it at meaningful boundaries.

```
csplit [options] file pattern...
```

```
$ csplit report.txt '/^Chapter/' '{*}'
```

That splits `report.txt` into one piece per chapter, cutting at every line beginning with `Chapter`. The pieces are written to `xx00`, `xx01`, `xx02`, … and `csplit` prints the byte size of each.

Each piece is a newly created file, and a new file needs a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md); each piece inherits one from the directory it is created in.

## Patterns

Each `pattern` argument marks a cut point. `csplit` copies everything up to that point into the next output file, then continues.

| Pattern | Cut where… |
|---|---|
| `N` (a number) | line `N` is reached — the piece is lines up to `N-1`. |
| `/REGEX/` | a line matches `REGEX`; that line starts the *next* piece. |
| `%REGEX%` | a line matches `REGEX` — but **skip** the text up to it instead of writing it to a piece. |
| `/REGEX/+N` or `/REGEX/-N` | an offset of `N` lines from the matching line. |
| `{N}` | repeat the *previous* pattern `N` more times. |
| `{*}` | repeat the previous pattern as many times as the file allows. |

A pattern that finds no match is an error, and by default `csplit` removes the pieces it had already written. `-k` keeps them.

## Naming the pieces

| Option | Effect |
|---|---|
| `-f`, `--prefix=PREFIX` | Use `PREFIX` instead of `xx` at the start of each output name. |
| `-n`, `--digits=N` | Use `N` digits in the numeric part of the name instead of 2. |
| `-b`, `--suffix-format=FORMAT` | Use a custom `printf`-style `FORMAT` for the numeric suffix. |

## Other options

| Option | Effect |
|---|---|
| `-k`, `--keep-files` | Do not delete the output files if `csplit` fails partway. |
| `-z`, `--elide-empty-files` | Do not write out pieces that would be empty. |
| `--suppress-matched` | Drop the lines that the patterns matched, rather than keeping them in a piece. |
| `-s`, `--quiet` | Do not print the byte size of each piece. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The file was split. |
| `1` | A pattern did not match, a line number was out of range, or the input could not be read. |

---

# od

_Peios / Using Peios / Peiosutils / Viewing and joining text_

> Dump the raw bytes of a file in octal, hexadecimal, decimal, floating-point, or character form — a low-level view of exactly what a file contains.

`od` — short for "octal dump" — prints the raw bytes of a file. Where [`cat`](/peios/using-peios/peiosutils/viewing-and-joining-text/cat.md) shows you a file as text, `od` shows you the actual bytes underneath: each one rendered as a number, a character, or a floating-point value, in whatever base you ask for. It is the tool for looking at binary files, checking exactly which bytes a text file contains, or finding a stray control character that isn't visible on screen.

```
od [options] [file...]
```

```
$ od hello.txt
0000000 062510 066154 020157 067527 066162 020144 000012
0000015
```

With no file — or with the name `-` — `od` reads standard input, so it can sit at the end of a pipeline. Given several files, it reads them one after another as a single continuous stream, and the byte offsets run straight through from one file into the next.

## How the output is laid out

Every line of output has two parts:

- An **offset** on the left: the position, counted in bytes from the start of the input, of the first byte on that line. By default it is printed in octal (see [`-A`](#the-offset-column) to change the base).
- One or more **format columns** on the right: the bytes of that line rendered in the format(s) you chose.

With no format option, `od` prints the input as **octal, two bytes at a time** — the traditional default. The very last line of output is a lone offset marking the total length of the input.

If you ask for more than one format at once, each format is printed on its own line, stacked under a single shared offset. Only the first line of the group carries the offset; the rest are indented to line up beneath it.

## Choosing what the bytes look like

There are two ways to say how bytes should be rendered: a set of single-letter **named shortcuts** for the common cases, and the general **`-t` / `--format`** option for full control. You can give several at once, and they are applied in the order they appear on the command line.

### Named format shortcuts

Each of these selects one output format. Combine them freely (`od -cx` prints characters and hex together).

| Option | Renders each unit as |
|---|---|
| `-a` | Named characters, ignoring the high-order bit (control characters shown by name, e.g. `nul`, `sp`, `del`). |
| `-b` | Octal, one byte at a time. |
| `-c` | Printable ASCII / UTF-8 characters, with backslash escapes (`\n`, `\t`, …) for the rest. |
| `-d` | Unsigned decimal, 2-byte units. |
| `-D` | Unsigned decimal, 4-byte units. |
| `-o` | Octal, 2-byte units. |
| `-O` | Octal, 4-byte units. |
| `-s` | Signed decimal, 2-byte units. |
| `-i` | Signed decimal, 4-byte units. |
| `-l` | Signed decimal, 8-byte units. |
| `-I`, `-L` | Signed decimal, 8-byte units. |
| `-x`, `-h` | Hexadecimal, 2-byte units. |
| `-X`, `-H` | Hexadecimal, 4-byte units. |
| `-f` | Floating point, single precision (32-bit). |
| `-e`, `-F` | Floating point, double precision (64-bit). |

### Type specifications (`-t`, `--format`)

`-t` takes a **type specification** and gives you every combination the named shortcuts cover and more. Repeat `-t` (or list several specs in one argument) to print multiple formats.

A type specification is a **type letter**, an optional **size**, and an optional **`z`** suffix:

| Type letter | Meaning |
|---|---|
| `a` | Printable 7-bit ASCII, named (like `-a`). |
| `c` | UTF-8 characters, with octal escapes for undefined bytes (like `-c`). |
| `d` | Signed decimal. |
| `u` | Unsigned decimal. |
| `o` | Octal. |
| `x` | Hexadecimal. |
| `f` | Floating point. |

For the numeric types (`d`, `u`, `o`, `x`, `f`) a **size** says how many bytes make up one unit. It can be a number, or a letter:

| Size | Applies to | Bytes per unit |
|---|---|---|
| `1` / `C` | integer types | 1 |
| `2` / `S` | integer types | 2 |
| `4` / `I` | integer types | 4 |
| `8` / `L` | integer types | 8 |
| `4` / `F` | floating point | 4 (single precision) |
| `8` / `D` | floating point | 8 (double precision) |
| `16` / `L` | floating point | 16 (extended / long double) |
| `2` / `H` | floating point | 2 (IEEE half precision) |
| `2` / `B` | floating point | 2 (bfloat16) |

If you leave the size off, integer types default to **4 bytes** and floating point defaults to **8 bytes**. For the character types `a` and `c`, a size is not allowed.

Add a **`z`** at the end of a spec to append an ASCII dump — the bytes of that line shown as text, `.` for anything non-printable — to the right of the numbers. For example, `od -t x1z` gives the familiar hex-plus-text layout:

```
$ od -A x -t x1z file.bin
000000 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 0a           >Hello, world!.<
```

Examples: `od -t d2` is signed decimal in 2-byte units; `od -t x1` is hex byte-by-byte; `od -t fD` is double-precision floats; `od -t c` is characters. `od -t x1 -t c` prints hex and characters as two stacked lines.

### Byte order (`--endian`)

For multi-byte numeric formats, `--endian` sets the byte order used to assemble each unit. Without it, `od` uses the machine's native byte order. Use `--endian=big` or `--endian=little` to force one regardless of the host.

## The offset column

| Option | Effect |
|---|---|
| `-A RADIX`, `--address-radix=RADIX` | Print offsets in the given base. `RADIX` is one of `o` (octal, the default), `d` (decimal), `x` (hexadecimal), or `n` (none — omit the offset column entirely). |

Only the first character of the value is examined, so `od -A n` and `od -Anone` mean the same thing.

## Which bytes to read

By default `od` dumps the whole input. Two options narrow that to a window:

| Option | Effect |
|---|---|
| `-j BYTES`, `--skip-bytes=BYTES` | Skip `BYTES` bytes of input before starting to dump. When several files are given, the skip runs across the concatenated stream. |
| `-N BYTES`, `--read-bytes=BYTES` | Dump at most `BYTES` bytes, then stop. |

In both, `BYTES` is decimal by default, octal if it starts with `0`, or hexadecimal if it starts with `0x`. A suffix scales it: `b` = ×512, `KB`/`K` = ×1000 / ×1024, `MB`/`M` = ×1000² / ×1024², `GB`/`G` = ×1000³ / ×1024³.

## Collapsing repeated lines

When several output lines in a row would be identical, `od` prints the first one, then a single line containing just `*` to stand for the run, and resumes at the next line that differs. This keeps a dump of a large block of identical bytes (a file full of zeros, say) short.

| Option | Effect |
|---|---|
| `-v`, `--output-duplicates` | Turn the collapsing off — print every line, including repeats, with no `*` markers. |

## Output width

| Option | Effect |
|---|---|
| `-w[BYTES]`, `--width[=BYTES]` | Print `BYTES` input bytes per output line. The default is 16; giving `-w` with no value uses 32. |

The width must be a whole multiple of the size of the largest format unit in play; if it isn't, `od` warns and rounds it to a usable value.

## Finding printable strings

| Option | Effect |
|---|---|
| `-S[BYTES]`, `--strings[=BYTES]` | Instead of dumping bytes, scan the input and print only runs of at least `BYTES` printable characters, one per line, each preceded by its offset. `BYTES` defaults to 3 when omitted. |

This turns `od` into a quick way to pull the human-readable text out of a binary file. The offset in front of each string honours `-A` (and is dropped entirely under `-A n`). `-j` and `-N` still apply, so you can restrict the scan to a window.

## The traditional offset form

`od` also accepts the historical calling convention, where an **offset** (and, in `--traditional` mode, a **label**) is given as a bare operand rather than an option:

```
od [named-format-options] [file] [[+]offset[.][b]]
od --traditional [options] [file] [[+]offset[.][b] [[+]label[.][b]]]
```

The **offset** says where in the input to begin, exactly like `-j`. It is read as **octal by default**, hexadecimal if prefixed with `0x`, or decimal if it carries a trailing `.`; a trailing `b` multiplies it by 512. The optional leading `+` is allowed everywhere and is required when there is no filename (so `od +20` reads standard input starting at octal offset 20).

The **label** (only in `--traditional` mode) is a second such number: the dump still begins at `offset`, but the offset column is *displayed* as if it started at `label` — useful for making a partial dump's offsets match the original file.

`od` only reads an operand as an offset in the plain form when none of `-A`, `-j`, `-N`, `-t`, `-v`, or `-w` are present. So if a real filename happens to look like a number, add a harmless option such as `-j0` to force it to be treated as a filename.

## A note on reading files

`od` has no access rules of its own — it simply opens a file and reads its bytes. That read is subject to the same check as every read on Peios: whether you may open the file for reading is decided by the file's **security descriptor** (see [Security descriptors](/peios/security-fundamentals/security-descriptors/overview.md)). `od` never reveals bytes you could not already read another way; it only presents the bytes you can read in a different form.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The input was dumped successfully. |
| `1` | A file could not be read, or an option, format, or offset was invalid. |

---

# Transforming text

_Peios / Using Peios / Peiosutils / Transforming text_

> The commands that reshape text — reordering lines, removing duplicates, substituting characters, reflowing paragraphs, and counting.

This topic covers the commands that **reshape** text: putting lines in order, dropping duplicates, swapping characters, reflowing paragraphs to a width, counting what is there. Its companion, [Viewing and joining text](/peios/using-peios/peiosutils/viewing-and-joining-text/overview.md), covers printing and combining files; this topic is about changing the text itself.

## The commands

**Ordering lines**

| Command | Purpose |
|---|---|
| [`sort`](/peios/using-peios/peiosutils/transforming-text/sort.md) | Sort lines — alphabetically, numerically, and many other ways. |
| [`shuf`](/peios/using-peios/peiosutils/transforming-text/shuf.md) | The opposite of sorting: put lines into a random order. |
| [`tsort`](/peios/using-peios/peiosutils/transforming-text/tsort.md) | Topological sort — order items so that each comes after the things it depends on. |

**Filtering lines**

| Command | Purpose |
|---|---|
| [`uniq`](/peios/using-peios/peiosutils/transforming-text/uniq.md) | Collapse or report adjacent repeated lines. |

**Substituting characters**

| Command | Purpose |
|---|---|
| [`tr`](/peios/using-peios/peiosutils/transforming-text/tr.md) | Translate, squeeze, or delete individual characters. |

**Whitespace**

| Command | Purpose |
|---|---|
| [`expand`](/peios/using-peios/peiosutils/transforming-text/expand.md) | Convert tabs into spaces. |
| [`unexpand`](/peios/using-peios/peiosutils/transforming-text/unexpand.md) | Convert runs of spaces back into tabs. |

**Reflowing and paginating**

| Command | Purpose |
|---|---|
| [`fmt`](/peios/using-peios/peiosutils/transforming-text/fmt.md) | Reflow paragraphs to a target width. |
| [`fold`](/peios/using-peios/peiosutils/transforming-text/fold.md) | Hard-wrap long lines at a fixed width. |
| [`pr`](/peios/using-peios/peiosutils/transforming-text/pr.md) | Paginate and columnate text for printing. |

**Indexing and counting**

| Command | Purpose |
|---|---|
| [`ptx`](/peios/using-peios/peiosutils/transforming-text/ptx.md) | Produce a permuted index of the words in a file. |
| [`wc`](/peios/using-peios/peiosutils/transforming-text/wc.md) | Count the lines, words, and bytes in a file. |

## Two commands that need sorted input

A theme worth knowing before you start: [`uniq`](/peios/using-peios/peiosutils/transforming-text/uniq.md) only collapses *adjacent* duplicate lines, so duplicates scattered through a file are not caught unless the file is sorted first. `sort` and `uniq` are almost always used together — and `sort -u` does both jobs in one step.

## Where to start

[`sort`](/peios/using-peios/peiosutils/transforming-text/sort.md) is the workhorse of the topic and the one with the most depth — start there.

---

# sort

_Peios / Using Peios / Peiosutils / Transforming text_

> Sort the lines of files — alphabetically, numerically, by field, and many other ways.

`sort` reads lines and writes them out in order.

```
sort [options] [file...]
```

```
$ sort names.txt
```

With no file, `sort` reads standard input. Given several files, it sorts all of their lines together as one stream. By default it compares lines as plain text, character by character.

## How lines are compared

The default text comparison is often not the order you want — `10` sorts before `9`, and case matters. These options change the comparison:

| Option | Compares lines as… |
|---|---|
| `-n`, `--numeric-sort` | numbers. `9` sorts before `10`. |
| `-g`, `--general-numeric-sort` | numbers, including scientific notation. Slower than `-n`; use it only when `-n` is not enough. |
| `-h`, `--human-numeric-sort` | human-readable sizes — `2K` before `1M` before `3G`. |
| `-M`, `--month-sort` | month names — `JAN` before `FEB`. |
| `-V`, `--version-sort` | version numbers — `1.2.10` after `1.2.9`. |
| `-R`, `--random-sort` | a random order (a shuffle that groups equal lines together). |

And these adjust *what* is compared:

| Option | Effect |
|---|---|
| `-f`, `--ignore-case` | Treat lower and upper case as the same. |
| `-d`, `--dictionary-order` | Consider only letters, digits, and blanks. |
| `-i`, `--ignore-nonprinting` | Ignore non-printing characters. |
| `-b`, `--ignore-leading-blanks` | Ignore blanks at the start of a line (or field). |

## Sort order options

| Option | Effect |
|---|---|
| `-r`, `--reverse` | Reverse the result. |
| `-u`, `--unique` | Output only the first line of each run of equal lines — sort and de-duplicate in one step. |
| `-s`, `--stable` | Keep equal lines in their original relative order. |

## Sorting by a field

By default `sort` compares whole lines. `-k` sorts on a **key** — a chosen part of each line — which is what you need for tabular data.

A line is split into fields (by default at whitespace). A key is written `FIELD[.CHAR][OPTIONS][,FIELD[.CHAR]]` — a start field, an optional start character within it, and an optional end. The single-letter comparison options above can be appended to a key to apply only to it.

```
$ sort -k 2 -n scores.txt        # sort on the 2nd field, numerically
$ sort -t , -k 3 people.csv      # sort a CSV on the 3rd field
```

| Option | Effect |
|---|---|
| `-k`, `--key=KEYDEF` | Sort on the key `KEYDEF`. May be given more than once; keys are tried in order. |
| `-t`, `--field-separator=SEP` | Use `SEP` to split fields, instead of the whitespace default. |

## Checking and merging

| Option | Effect |
|---|---|
| `-c`, `--check` | Do not sort — just check whether the input is already sorted, and report the first line that is out of order. |
| `-C`, `--check=silent` | Check, but report only through the exit status. |
| `-m`, `--merge` | Treat the inputs as already-sorted files and merge them, which is faster than a full sort. |

## Output and resources

| Option | Effect |
|---|---|
| `-o`, `--output=FILE` | Write to `FILE` instead of standard output. `FILE` may be one of the inputs. |
| `-z`, `--zero-terminated` | Treat the NUL character as the line delimiter. |
| `--parallel=N` | Use `N` threads. |
| `-S`, `--buffer-size=SIZE` | Set the size of the in-memory sort buffer. |
| `-T`, `--temporary-directory=DIR` | Put temporary files in `DIR` rather than the default location. |
| `--files0-from=F` | Take the list of input files from `F`, NUL-separated. |
| `--debug` | Underline the part of each line that was actually used as the sort key — invaluable when a `-k` key is not behaving. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The lines were sorted — or, with `-c`/`-C`, the input was already sorted. |
| `1` | With `-c`/`-C`, the input was *not* sorted. |
| `2` | An error — a file could not be read, or an option was invalid. |

---

# uniq

_Peios / Using Peios / Peiosutils / Transforming text_

> Collapse or report adjacent repeated lines in a file.

`uniq` deals with repeated lines: it can collapse a run of identical lines into one, count them, or print only the ones that repeated.

```
uniq [options] [input [output]]
```

```
$ uniq events.txt
```

`input` and `output` may be given as operands; with neither, `uniq` reads standard input and writes standard output.

## uniq only sees adjacent duplicates

This is the one thing to know about `uniq`: it only compares **each line with the one before it**. It collapses *adjacent* repeats. Duplicate lines scattered through a file, with other lines between them, are not caught.

So `uniq` is almost always paired with [`sort`](/peios/using-peios/peiosutils/transforming-text/sort.md), which brings every copy of a line together first:

```
$ sort events.txt | uniq
```

If collapsing duplicates is all you need, `sort -u` does both jobs in one command. Use `uniq` itself when you want what `sort -u` cannot do — counting repeats, or printing only the duplicated lines.

## What to output

By default `uniq` prints every line, with each run of adjacent duplicates reduced to a single copy. These options change that:

| Option | Effect |
|---|---|
| `-c`, `--count` | Prefix each line with the number of times it occurred. |
| `-d`, `--repeated` | Print only lines that *were* repeated — one copy of each. |
| `-D` | Print *all* copies of every repeated line, not just one. |
| `-u`, `--unique` | Print only lines that were *not* repeated. |
| `--group[=WHICH]` | Print every line, with runs separated by a blank line. `WHICH` is `separate`, `prepend`, `append`, or `both`. |

## What counts as "the same"

| Option | Effect |
|---|---|
| `-i`, `--ignore-case` | Treat lines differing only in letter case as the same. |
| `-f`, `--skip-fields=N` | Ignore the first `N` fields when comparing. |
| `-s`, `--skip-chars=N` | Ignore the first `N` characters when comparing. |
| `-w`, `--check-chars=N` | Compare at most `N` characters of each line. |

## Other options

| Option | Effect |
|---|---|
| `-z`, `--zero-terminated` | Treat the NUL character as the line delimiter. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read or written. |

---

# tr

_Peios / Using Peios / Peiosutils / Transforming text_

> Translate, squeeze, or delete individual characters in a stream of text.

`tr` — "translate" — works on text one **character** at a time. It can replace characters with other characters, remove characters, or collapse runs of a character into one.

```
tr [options] set1 [set2]
```

`tr` reads standard input and writes standard output — it has no file arguments, so it always sits in a pipeline.

```
$ tr 'a-z' 'A-Z' < notes.txt        # lower-case to upper-case
$ tr -d ' ' < spaced.txt            # delete every space
$ tr -s ' ' < padded.txt            # collapse runs of spaces to one
```

## The three jobs

What `tr` does depends on the options and how many sets you give it.

- **Translate** — give two sets. Each character in `set1` is replaced by the character at the same position in `set2`. `tr 'abc' 'xyz'` turns every `a` into `x`, `b` into `y`, `c` into `z`.
- **Delete** (`-d`) — give one set. Every character in `set1` is removed.
- **Squeeze** (`-s`) — each run of a repeated character that appears in the relevant set is collapsed to a single occurrence.

`-d` and `-s` can be combined: delete one set of characters, then squeeze another.

## Writing a set

A set is a string of characters, with a few shorthands:

| Notation | Means |
|---|---|
| `a-z` | A range — every character from `a` to `z`. |
| `[:alpha:]`, `[:digit:]`, `[:space:]`, … | A named character class. |
| `[=c=]` | An equivalence class — every character that sorts the same as `c`. |
| `[c*n]` | The character `c` repeated `n` times — for padding a set to a length. |
| `\n`, `\t`, `\\`, `\NNN` | Escapes — newline, tab, backslash, an octal byte. |

## Options

| Option | Effect |
|---|---|
| `-d`, `--delete` | Delete the characters in `set1` instead of translating. |
| `-s`, `--squeeze-repeats` | Collapse each run of a repeated character listed in the last set into one. |
| `-c`, `-C`, `--complement` | Operate on the *complement* of `set1` — every character it does **not** list. |
| `-t`, `--truncate-set1` | Before translating, shorten `set1` to the length of `set2`, rather than reusing `set2`'s last character to cover the surplus. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A usage error — the wrong number of sets, or a malformed set. |

---

# expand

_Peios / Using Peios / Peiosutils / Transforming text_

> Convert tab characters in a file into spaces.

`expand` converts tab characters into spaces, so that text lines up the same way no matter what tab width something is viewed with.

```
expand [options] [file...]
```

```
$ expand source.txt > source-spaces.txt
```

A tab is not a fixed number of spaces — it jumps to the next tab stop, and where those stops are is a display setting. `expand` removes the ambiguity: it replaces each tab with exactly the number of spaces needed to reach the same column, baking the layout in. With no file, it reads standard input.

[`unexpand`](/peios/using-peios/peiosutils/transforming-text/unexpand.md) is the reverse operation.

## Options

| Option | Effect |
|---|---|
| `-t`, `--tabs=N` | Set tab stops every `N` columns instead of the default 8. |
| `-t`, `--tabs=LIST` | Given a comma-separated list, place tab stops at those explicit column positions. |
| `-i`, `--initial` | Convert only the tabs that come before the first non-blank character on each line — leave tabs *within* the text alone. Useful for re-indenting code without disturbing aligned tabs inside it. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read, or a tab specification was invalid. |

---

# unexpand

_Peios / Using Peios / Peiosutils / Transforming text_

> Convert runs of spaces in a file back into tab characters.

`unexpand` is the reverse of [`expand`](/peios/using-peios/peiosutils/transforming-text/expand.md): it converts runs of spaces back into tab characters.

```
unexpand [options] [file...]
```

```
$ unexpand spaced.txt > tabbed.txt
```

Where a run of spaces reaches a tab stop, `unexpand` replaces it with a tab. By default it only converts the **leading** spaces on each line — the indentation — and leaves spacing within the text alone. With no file, it reads standard input.

## Options

| Option | Effect |
|---|---|
| `-t`, `--tabs=N` | Set tab stops every `N` columns instead of the default 8. A comma-separated list places stops at explicit column positions. Using `-t` also enables `-a`. |
| `-a`, `--all` | Convert *all* runs of spaces that reach a tab stop, not only the leading indentation. |
| `--first-only` | Convert only the leading run of blanks on each line. Overrides `-a`. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read, or a tab specification was invalid. |

---

# fmt

_Peios / Using Peios / Peiosutils / Transforming text_

> Reflow paragraphs of text to a target line width.

`fmt` reflows paragraphs: it rejoins the lines of a paragraph and re-breaks them so each comes out close to a target width. Words move freely between lines to make the result fit.

```
fmt [options] [file...]
```

```
$ fmt -w 72 draft.txt
```

`fmt` works on **paragraphs** — runs of non-blank lines separated by blank lines — and treats word boundaries as places it may break. The result reads naturally. This is what distinguishes it from [`fold`](/peios/using-peios/peiosutils/transforming-text/fold.md), which simply cuts every line at a fixed column without regard to words.

With no file, `fmt` reads standard input.

## Setting the width

| Option | Effect |
|---|---|
| `-w`, `--width=WIDTH` | Fill lines up to at most `WIDTH` columns. The default is 75. |
| `-g`, `--goal=WIDTH` | Aim for `WIDTH` columns — the width `fmt` targets, while `-w` is the hard maximum. Defaults to about 93% of the maximum. |

## Controlling the reflow

| Option | Effect |
|---|---|
| `-s`, `--split-only` | Only split lines that are too long; never join short lines together. |
| `-u`, `--uniform-spacing` | Put exactly one space between words and two between sentences. |
| `-c`, `--crown-margin` | Preserve the indentation of a paragraph's first two lines; indent the rest to match the second. |
| `-t`, `--tagged-paragraph` | Like `-c`, but the first line *must* be indented differently from the second, or the two are treated as separate paragraphs. |
| `-q`, `--quick` | Break lines faster, accepting a more ragged right edge. |

## Selecting which lines to format

| Option | Effect |
|---|---|
| `-p`, `--prefix=PREFIX` | Reformat only lines that begin with `PREFIX`; reattach `PREFIX` afterwards. Useful for reflowing comment blocks in source code. |
| `-m`, `--preserve-headers` | Detect and preserve mail-style header lines rather than reflowing them. |
| `--tab-width=N` | Treat a tab as `N` columns when measuring line length. Tabs are kept in the output; this affects measurement only. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read, or an option value was invalid. |

---

# fold

_Peios / Using Peios / Peiosutils / Transforming text_

> Hard-wrap long lines at a fixed width.

`fold` breaks long lines so that none exceeds a fixed width. Every line longer than the limit is cut into pieces.

```
fold [options] [file...]
```

```
$ fold -w 80 wide.txt
```

By default `fold` wraps at 80 columns, and it cuts at exactly that column — even in the middle of a word. That hard cut is the difference between `fold` and [`fmt`](/peios/using-peios/peiosutils/transforming-text/fmt.md): `fmt` reflows paragraphs and breaks between words; `fold` just enforces a width, mechanically. Use `fold` when you need a guarantee that no line is wider than `N`; use `fmt` when you want the result to read well.

With no file, `fold` reads standard input.

## Options

| Option | Effect |
|---|---|
| `-w`, `--width=WIDTH` | Wrap at `WIDTH` columns instead of 80. |
| `-s`, `--spaces` | Break at a space within the width where one exists, rather than mid-word — a gentler wrap. |
| `-b`, `--bytes` | Count bytes rather than display columns. Control characters such as tab and newline then count as ordinary bytes. |
| `-c`, `--characters` | Count character positions rather than display columns. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read, or the width was invalid. |

---

# pr

_Peios / Using Peios / Peiosutils / Transforming text_

> Paginate and columnate text for printing.

`pr` prepares text for printing on paper. It breaks a file into **pages**, puts a header and a trailer on each, and can arrange the text into columns.

```
pr [options] [file...]
```

```
$ pr report.txt
```

Run plainly, `pr` divides the input into 66-line pages, each beginning with a five-line header — the date, the file name, and a page number — and ending with a trailing margin. With no file, it reads standard input.

## Columns

`pr` can lay text out in several columns per page.

| Option | Effect |
|---|---|
| `-COLUMN` | Produce `COLUMN` columns per page (e.g. `-3` for three). Text fills down the first column, then the next. |
| `-a`, `--across` | Fill the columns *across* — line 1 in column 1, line 2 in column 2, and so on — rather than down. |
| `-m`, `--merge` | Print several files side by side, one per column. |
| `-w`, `--width=WIDTH` | Set the page width, for multi-column output. |

## The header and the page

| Option | Effect |
|---|---|
| `-h`, `--header=TEXT` | Use `TEXT` in the header instead of the file name. |
| `-t`, `--omit-header` | Print neither the header nor the trailer. |
| `-T`, `--omit-pagination` | Drop the header, the trailer, and all pagination — just the text. |
| `-l`, `--length=LINES` | Set the page length to `LINES` instead of 66. |
| `-o`, `--indent=N` | Indent every line by `N` spaces. |
| `-D`, `--date-format=FORMAT` | Format the header's date with `FORMAT`. |
| `-F`, `-f`, `--form-feed` | Separate pages with a form-feed character rather than blank lines. |

## Selecting and numbering

| Option | Effect |
|---|---|
| `--pages=FIRST[:LAST]` | Print only pages `FIRST` through `LAST`. |
| `-n`, `--number-lines[=SEP[WIDTH]]` | Number every line. |
| `-N`, `--first-line-number=N` | Begin line numbering at `N`. |
| `-d`, `--double-space` | Double-space the output. |

## Other options

| Option | Effect |
|---|---|
| `-s`, `--separator-char[=CHAR]` | Separate columns with a single `CHAR` (default tab) rather than padding with spaces. |
| `-S`, `--sep-string[=STRING]` | Separate columns with `STRING`. |
| `-e`, `--expand-tabs[=CHAR[WIDTH]]` | Expand input tabs to spaces. |
| `-J`, `--join-lines` | Merge full lines, turning off width truncation. |
| `-r`, `--no-file-warnings` | Do not warn when a file cannot be opened. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read, or an option was invalid. |

---

# ptx

_Peios / Using Peios / Peiosutils / Transforming text_

> Produce a permuted index of the words in a file.

`ptx` produces a **permuted index** — a concordance. For each significant word in the input, it prints a line showing that word together with the text around it, so the same passage appears once per keyword it contains.

```
ptx [options] [input...]
```

```
$ ptx manual.txt
```

A permuted index is the kind of index found at the back of a reference book: every keyword, in alphabetical order, each shown in its context. `ptx` builds one from plain text. With no file, it reads standard input.

## Choosing the keywords

| Option | Effect |
|---|---|
| `-W`, `--word-regexp=REGEXP` | Treat anything matching `REGEXP` as a keyword. |
| `-b`, `--break-file=FILE` | Take the set of word-break characters from `FILE`. |
| `-i`, `--ignore-file=FILE` | Read a list of words to *exclude* from `FILE`. |
| `-o`, `--only-file=FILE` | Index *only* the words listed in `FILE`. |
| `-f`, `--ignore-case` | Fold case together when sorting. |

## Output format

| Option | Effect |
|---|---|
| `-w`, `--width=N` | Set the output width, in columns. |
| `-g`, `--gap-size=N` | Set the gap, in columns, between the output fields. |
| `-A`, `--auto-reference` | Generate a reference (file name and line number) for each entry automatically. |
| `-r`, `--references` | Treat the first field of each input line as a reference for that line. |
| `-R`, `--right-side-refs` | Put references on the right, and exclude them from the width count. |
| `-F`, `--flag-truncation=STRING` | Use `STRING` to mark where a line was truncated. |
| `-O`, `--format=roff` | Emit the index as `roff` typesetting directives. |
| `-T`, `--format=tex` | Emit the index as TeX typesetting directives. |
| `-M`, `--macro-name=NAME` | Use `NAME` as the macro name in `roff`/TeX output. |
| `-G`, `--traditional` | Behave like the older System V `ptx`, without the extended features. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read, or an option value was invalid. |

---

# tsort

_Peios / Using Peios / Peiosutils / Transforming text_

> Topologically sort items so that each comes after the things it depends on.

`tsort` performs a **topological sort**. Given a set of "this must come before that" rules, it produces an order in which everything appears after the things it depends on.

```
tsort [file]
```

```
$ tsort dependencies.txt
```

With no file, `tsort` reads standard input.

## The input

`tsort` reads its input as a flat sequence of whitespace-separated tokens, taken **in pairs**. Each pair `A B` means "A must come before B" — an edge in a dependency graph.

```
compiler  linker
linker    installer
compiler  installer
```

That input says the linker depends on the compiler, the installer on the linker, and the installer on the compiler. `tsort` reads the pairs and prints the items in a valid order:

```
compiler
linker
installer
```

A token paired with itself (`A A`) simply introduces `A` with no dependency — a way to list an item that nothing else mentions.

## Cycles

A topological order only exists if the dependencies form no **cycle**. If A must come before B and B must come before A, there is no valid order. `tsort` detects this, reports the loop it found, and exits with a failure status — it still prints an order, but the cycle means that order cannot satisfy every rule.

## Where it is used

`tsort` answers "what order do I do these in?" — building components in dependency order, scheduling tasks, sequencing anything where some steps must precede others. It is the dependency-aware counterpart to [`sort`](/peios/using-peios/peiosutils/transforming-text/sort.md), which orders by value rather than by dependency.

## Exit status

| Code | Meaning |
|---|---|
| `0` | A valid order was produced. |
| `1` | The input contained a cycle, had an odd number of tokens, or could not be read. |

---

# wc

_Peios / Using Peios / Peiosutils / Transforming text_

> Count the lines, words, and bytes in files.

`wc` — "word count" — counts what is in a file: its lines, its words, and its bytes.

```
wc [options] [file...]
```

```
$ wc report.txt
  214  1832 12044 report.txt
```

By default `wc` prints three numbers per file — **lines, words, bytes** — followed by the file name. Given several files, it adds a `total` line. With no file, it reads standard input.

## Choosing what to count

Pass any of these and `wc` prints only the counts you ask for, in the fixed order lines, words, characters/bytes, longest-line:

| Option | Counts |
|---|---|
| `-l`, `--lines` | The number of lines. |
| `-w`, `--words` | The number of words — runs of non-whitespace. |
| `-c`, `--bytes` | The number of bytes. |
| `-m`, `--chars` | The number of characters. This differs from `-c` for multi-byte text, where one character is several bytes. |
| `-L`, `--max-line-length` | The length of the longest line. |

```
$ wc -l report.txt        # just the line count
214 report.txt
```

## Other options

| Option | Effect |
|---|---|
| `--files0-from=F` | Take the list of files to count from `F`, as NUL-terminated names. `-` reads the list from standard input. |
| `--total=WHEN` | Control the `total` line: `auto` (the default — shown for more than one file), `always`, `only` (just the total, no per-file lines), or `never`. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every file was counted. |
| `1` | A file could not be read. |

---

# shuf

_Peios / Using Peios / Peiosutils / Transforming text_

> Output the input lines in a random order.

`shuf` shuffles: it outputs its input lines in a random order. Every possible ordering is equally likely.

```
shuf [options] [file]
shuf -e [options] arg...
shuf -i lo-hi [options]
```

```
$ shuf playlist.txt
```

`shuf` is the opposite of [`sort`](/peios/using-peios/peiosutils/transforming-text/sort.md) — instead of imposing an order, it removes one. With no file, it reads standard input.

## Where the lines come from

`shuf` has three ways of getting its input, one per usage form:

| Option | Input is… |
|---|---|
| (a file, or stdin) | the lines of the file. |
| `-e`, `--echo` | the command-line arguments, each treated as one line. |
| `-i`, `--input-range=LO-HI` | the integers from `LO` to `HI`, each treated as one line. |

```
$ shuf -e red green blue          # shuffle three given words
$ shuf -i 1-100                   # the numbers 1..100 in random order
```

## Shaping the output

| Option | Effect |
|---|---|
| `-n`, `--head-count=COUNT` | Output at most `COUNT` lines — a random *sample* rather than the whole shuffle. |
| `-r`, `--repeat` | Allow lines to be repeated, so output can be longer than the input. Pair with `-n` to set how many. |
| `-o`, `--output=FILE` | Write to `FILE` instead of standard output. |
| `-z`, `--zero-terminated` | Treat the NUL character as the line delimiter. |

`shuf -n 1` is a common idiom — pick one random line.

## Reproducible shuffles

A shuffle is random by default. To get the *same* shuffle every time — for a repeatable test, say — fix the randomness:

| Option | Effect |
|---|---|
| `--random-seed=STRING` | Seed the shuffle with `STRING`. The same seed gives the same shuffle. |
| `--random-source=FILE` | Take the random bytes from `FILE`. The same file gives the same shuffle. Cannot be combined with `--random-seed`. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A failure — the input could not be read, or `-r` was used with no lines to repeat. |

---

# Output and evaluation

_Peios / Using Peios / Peiosutils / Output and evaluation_

> The commands that produce text, work with the environment, convert numbers, and evaluate expressions and conditions.

This topic gathers the small, sharp commands that scripts are built from: the ones that **print** something, that read or set the **environment**, that **evaluate** an expression or a condition, and that simply **succeed or fail**.

## The commands

**Producing text**

| Command | Purpose |
|---|---|
| [`echo`](/peios/using-peios/peiosutils/output-and-evaluation/echo.md) | Print its arguments as a line of text. |
| [`printf`](/peios/using-peios/peiosutils/output-and-evaluation/printf.md) | Print text from a format string, with precise control over layout. |
| [`yes`](/peios/using-peios/peiosutils/output-and-evaluation/yes.md) | Print a line over and over, without stopping. |
| [`seq`](/peios/using-peios/peiosutils/output-and-evaluation/seq.md) | Print a sequence of numbers. |
| [`tee`](/peios/using-peios/peiosutils/output-and-evaluation/tee.md) | Copy standard input to standard output and to each named file. |

**The environment**

| Command | Purpose |
|---|---|
| [`env`](/peios/using-peios/peiosutils/output-and-evaluation/env.md) | Run a command with a modified environment — or print the current one. |
| [`printenv`](/peios/using-peios/peiosutils/output-and-evaluation/printenv.md) | Print environment variables. |

**Numbers**

| Command | Purpose |
|---|---|
| [`numfmt`](/peios/using-peios/peiosutils/output-and-evaluation/numfmt.md) | Convert numbers to and from human-readable forms (`1.5G`, `1000000`). |
| [`factor`](/peios/using-peios/peiosutils/output-and-evaluation/factor.md) | Print the prime factors of a number. |

**Evaluating**

| Command | Purpose |
|---|---|
| [`expr`](/peios/using-peios/peiosutils/output-and-evaluation/expr.md) | Evaluate an arithmetic, string, or comparison expression. |
| [`test`](/peios/using-peios/peiosutils/output-and-evaluation/test.md) | Evaluate a condition — a file check or a comparison — as a true/false result. |

**Exit status**

| Command | Purpose |
|---|---|
| [`true` and `false`](/peios/using-peios/peiosutils/output-and-evaluation/true-and-false.md) | Do nothing, and succeed — or do nothing, and fail. |

## A note on exit status

Several commands here exist to be used for their **exit status** rather than their output. Every command, when it finishes, leaves behind a number: `0` means success, anything else means a kind of failure. A shell's `if`, `&&`, and `||` all act on that number.

[`test`](/peios/using-peios/peiosutils/output-and-evaluation/test.md) is the clearest case — it prints nothing and exists *only* to set an exit status from a condition. [`true` and `false`](/peios/using-peios/peiosutils/output-and-evaluation/true-and-false.md) are the most minimal: fixed success and fixed failure. Where this topic's pages give an exit-status table, that number is often the whole point of the command.

## Where to start

For everyday printing, [`echo`](/peios/using-peios/peiosutils/output-and-evaluation/echo.md); for anything that needs columns, padding, or precise formatting, [`printf`](/peios/using-peios/peiosutils/output-and-evaluation/printf.md).

---

# echo

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Print arguments as a line of text to standard output.

`echo` prints its arguments, separated by single spaces, followed by a newline.

```
echo [options] [string...]
```

```
$ echo Hello, Peios
Hello, Peios
```

It is the simplest way to put a line of text on standard output — printing a message, or feeding a fixed string into a pipe.

## Options

| Option | Effect |
|---|---|
| `-n` | Do not print the trailing newline. |
| `-e` | Interpret backslash escape sequences in the arguments (see below). |
| `-E` | Do **not** interpret escape sequences. This is the default. |

## Escape sequences

With `-e`, `echo` recognises these backslash sequences and prints the character they stand for:

| Sequence | Character |
|---|---|
| `\\` | A literal backslash. |
| `\n` | Newline. |
| `\t` | Horizontal tab. |
| `\r` | Carriage return. |
| `\b` | Backspace. |
| `\f` | Form feed. |
| `\v` | Vertical tab. |
| `\a` | Alert (the terminal bell). |
| `\e` | Escape. |
| `\0NNN` | The byte with octal value `NNN`. |
| `\xHH` | The byte with hexadecimal value `HH`. |
| `\c` | Stop — produce no further output. |

## `echo` and `printf`

`echo` is fixed: arguments, spaces, a newline. The moment you need *control* over the output — columns, padding, a number formatted a particular way, output with no spaces between pieces — use [`printf`](/peios/using-peios/peiosutils/output-and-evaluation/printf.md) instead. `printf` is also more predictable across environments, since `echo`'s handling of escapes and `-n` varies.

## A note on shells

Many command shells provide their own built-in `echo`, and the shell's version is what runs when you type `echo` at a prompt. Built-in versions vary — especially in how they treat `-n`, `-e`, and escape sequences — so their behaviour may differ from what is described here. To be certain you are running this command rather than the shell built-in, invoke it by its full path.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The output was written. |
| `1` | The output could not be written. |

---

# printf

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Print formatted text from a format string and a list of arguments.

`printf` prints text built from a **format string**. The format string is printed literally, except that escape sequences become characters and substitution fields are replaced by the arguments that follow.

```
printf format [argument...]
```

```
$ printf 'the letter %X comes before %X\n' 10 11
the letter A comes before B
```

Where [`echo`](/peios/using-peios/peiosutils/output-and-evaluation/echo.md) prints arguments as-is, `printf` gives you control: padding, column widths, number bases, fixed decimal places. It is the command for output that has to *line up* or *be exact*.

`printf` writes only what the format string says — there is no automatic trailing newline. Include `\n` yourself where you want one.

## Escape sequences

The format string interprets these backslash sequences:

| Sequence | Character |
|---|---|
| `\\` | Backslash. |
| `\n` `\t` `\r` | Newline, tab, carriage return. |
| `\b` `\f` `\v` `\a` | Backspace, form feed, vertical tab, alert. |
| `\e` | Escape. |
| `\NNN` | The byte with octal value `NNN`. |
| `\xHH` | The byte with hexadecimal value `HH`. |
| `\uHHHH`, `\UHHHHHHHH` | A Unicode character by hexadecimal code point. |
| `%%` | A literal `%`. |

## Substitution fields

A field begins with `%` and is replaced by the next argument, formatted accordingly.

| Field | Formats the argument as… |
|---|---|
| `%s` | a string. |
| `%b` | a string, with backslash escapes in it interpreted. |
| `%q` | a string, quoted so it is safe to reuse as shell input. |
| `%c` | a single character. |
| `%d`, `%i` | a signed integer. |
| `%u` | an unsigned integer. |
| `%x`, `%X` | an unsigned integer in hexadecimal (lower- or upper-case). |
| `%o` | an unsigned integer in octal. |
| `%f`, `%F` | a decimal floating-point number. |
| `%e`, `%E` | a number in scientific notation. |
| `%g`, `%G` | whichever of decimal or scientific notation is shorter. |

## Width and precision

Between the `%` and the field letter you may put two numbers — `%[width][.precision]field`:

- **width** — the minimum number of columns. Output narrower than this is padded with leading spaces; a negative width pads on the right instead.
- **precision** — after a `.`; its meaning depends on the field. For a string it is a *maximum length*; for an integer, a minimum digit count (zero-padded); for a float, the number of decimal places.

```
$ printf '%-10s %5.2f\n' apples 3.1
apples         3.10
```

## Reusing the format string

If more arguments are supplied than the format string has fields, `printf` **repeats the format string** until the arguments run out. If there are too few, the leftover fields default to an empty string (or `0` for a number).

```
$ printf '%s: %d\n' alpha 1 beta 2 gamma 3
alpha: 1
beta: 2
gamma: 3
```

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A missing operand, a bad format string, or a write failure. |

---

# yes

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Print a line repeatedly until stopped.

`yes` prints a line over and over, without stopping.

```
yes [string...]
```

```
$ yes
y
y
y
...
```

With no argument it prints `y` forever. Given arguments, it prints them — joined by spaces — as the repeated line instead.

```
$ yes retry
retry
retry
...
```

`yes` never stops on its own. It ends when something downstream closes the pipe, or when you interrupt it with Ctrl-C.

## What it is for

`yes` exists to **answer prompts**. An older interactive command that asks "are you sure? [y/n]" again and again can be fed a stream of confirmations:

```
$ yes | slow-interactive-command
```

Most modern commands have a proper option for this — `rm -f`, and similar — and that is the better way when it exists. `yes` is the fallback for a command that offers no such option. It is also a quick way to generate an endless stream of identical lines for a test.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The output was closed normally. |
| `1` | A write error occurred. |

---

# seq

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Print a sequence of numbers from a start to an end.

`seq` prints a sequence of numbers, one per line.

```
seq [options] last
seq [options] first last
seq [options] first increment last
```

```
$ seq 3
1
2
3
$ seq 2 2 10
2
4
6
8
10
```

The three forms control where the sequence starts and how it steps:

- `seq last` — count from 1 up to `last`.
- `seq first last` — count from `first` up to `last`.
- `seq first increment last` — count from `first` to `last`, stepping by `increment`.

The increment may be negative, to count down, and the numbers may be fractional. `seq` is most often used to drive a loop a fixed number of times.

## Options

| Option | Effect |
|---|---|
| `-s`, `--separator=STRING` | Separate the numbers with `STRING` instead of a newline. |
| `-t`, `--terminator=STRING` | End the output with `STRING` instead of a newline. |
| `-w`, `--equal-width` | Pad the numbers with leading zeros so they all have the same width. |
| `-f`, `--format=FORMAT` | Format each number with a `printf`-style floating-point `FORMAT`. |

```
$ seq -s , 1 5
1,2,3,4,5
$ seq -w 8 10
08
09
10
```

`-f` takes a single floating-point conversion — see [`printf`](/peios/using-peios/peiosutils/output-and-evaluation/printf.md) for the format syntax — and cannot be combined with `-w`.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The sequence was printed. |
| `1` | An argument was not a valid number, the increment was zero, or no argument was given. |

---

# env

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Run a command with a modified environment, or print the current environment.

`env` runs a command with a changed **environment** — the set of `NAME=VALUE` variables a process inherits. Run with no command, it prints the current environment instead.

```
env [options] [NAME=VALUE]... [command [arg]...]
```

```
$ env LANG=C TZ=UTC my-program
$ env
PATH=/bin
HOME=/home/jack
...
```

## How it works

You give `env` a list of `NAME=VALUE` assignments, then a command. `env` applies the assignments to its own environment and then runs the command, which inherits the result. The assignments affect only that one run — your shell's environment is untouched.

This is the clean way to run something with a particular variable set, without changing it for everything else, and the standard way to run a command with one variable temporarily different.

With **no command**, `env` simply prints the environment it would have used — which, with no options, is the current one.

## Building the environment

| Option | Effect |
|---|---|
| `-i`, `--ignore-environment` | Start from an *empty* environment, not the inherited one — so the command sees only what you assign. A bare `-` argument means the same. |
| `-u`, `--unset=NAME` | Remove `NAME` from the environment. |
| `--file=FILE` | Read `NAME=VALUE` lines from a `.env`-style `FILE` and apply them. |

The order is: start (inherited, or empty with `-i`), apply `--file`, apply `--unset`, apply the `NAME=VALUE` arguments.

## Running the command

| Option | Effect |
|---|---|
| `-C`, `--chdir=DIR` | Change to `DIR` before running the command. |
| `--argv0=NAME` | Run the command but present `NAME` as its zeroth argument. |
| `-S`, `--split-string=S` | Split `S` into separate arguments. This exists for script interpreter lines, where everything after the interpreter arrives as one string. |

## Signal handling

`env` can adjust how the command treats signals before launching it:

| Option | Effect |
|---|---|
| `--ignore-signal[=SIG]` | Set the named signals to be ignored. |
| `--default-signal[=SIG]` | Reset the named signals to their default handling. |
| `--block-signal[=SIG]` | Block delivery of the named signals while the command runs. |
| `--list-signal-handling` | List the signal-handling changes the other options requested. |

## Other options

| Option | Effect |
|---|---|
| `-0`, `--null` | When printing the environment, end each line with a NUL character instead of a newline. Not valid together with a command. |
| `-v`, `--debug` | Print each processing step `env` performs. |

## Exit status

When `env` runs a command, it exits with **that command's** status. The exception is a failure in `env` itself:

| Code | Meaning |
|---|---|
| (command's own) | The command ran; this is its exit status. |
| `125` | `env` itself failed — for example, a bad option. |
| `126` | The command was found but could not be run. |
| `127` | The command was not found. |

---

# printenv

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Print the values of environment variables.

`printenv` prints environment variables.

```
printenv [options] [variable...]
```

Name one or more variables and `printenv` prints the value of each. Name none and it prints the whole environment, one `NAME=VALUE` pair per line.

```
$ printenv HOME
/home/jack
$ printenv
PATH=/bin
HOME=/home/jack
...
```

## Options

| Option | Effect |
|---|---|
| `-0`, `--null` | End each output line with a NUL character instead of a newline — so values that themselves contain newlines can still be told apart. |

## `printenv` and `env`

Both can print the environment. `printenv` is the one built for *reading* it — it can pick out a single variable by name. [`env`](/peios/using-peios/peiosutils/output-and-evaluation/env.md) prints the environment too, but its real job is *running a command* with a modified one. Use `printenv` to look something up; use `env` to change something for a command.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every named variable was found and printed. |
| `1` | A named variable was not set. |
| `2` | A usage error. |

---

# numfmt

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Convert numbers between plain digits and human-readable forms.

`numfmt` converts numbers between plain digits and **human-readable** forms — turning `1500000` into `1.5M`, or `1.5M` back into `1500000`.

```
numfmt [options] [number...]
```

```
$ numfmt --to=si 1500000
1.5M
$ numfmt --from=si 1.5M
1500000
```

`numfmt` takes numbers as arguments, or — with none — reads them from standard input, which lets it rescale a column of numbers flowing through a pipe.

## The direction of conversion

| Option | Effect |
|---|---|
| `--from=UNIT` | Parse input numbers that carry a `UNIT` suffix, scaling them up to plain digits. |
| `--to=UNIT` | Scale output numbers down and add a `UNIT` suffix. |

A `UNIT` is one of:

| Unit | Suffixes mean |
|---|---|
| `none` | No suffixes — a suffix is an error. The default. |
| `si` | Powers of 1000: `1K` = 1000, `1M` = 1000000. |
| `iec` | Powers of 1024: `1K` = 1024, `1M` = 1048576. |
| `iec-i` | Powers of 1024 with a two-letter suffix: `1Ki` = 1024, `1Mi` = 1048576. |
| `auto` | Accept any of the above on input, reading `Ki`/`Mi` as powers of 1024 and bare `K`/`M` as powers of 1000. |

## Working on fields

By default `numfmt` converts the whole input line. To convert numbers sitting in a *column* of wider text, name the field:

| Option | Effect |
|---|---|
| `--field=FIELDS` | Convert only the numbers in these fields. `FIELDS` uses the same range syntax as `cut` — `3`, `2-5`, `2-`. |
| `-d`, `--delimiter=X` | Use `X` to separate fields instead of whitespace. |
| `--header[=N]` | Pass the first `N` lines through unconverted, as a header. |

## Shaping the output

| Option | Effect |
|---|---|
| `--format=FORMAT` | Format the number with a `printf`-style floating-point `FORMAT`, controlling width, padding, and precision. |
| `--padding=N` | Pad the output to `N` columns — positive right-aligns, negative left-aligns. |
| `--grouping` | Group digits according to the locale (for example, `1,000,000`). |
| `--round=METHOD` | Choose the rounding method used when scaling. |
| `--suffix=SUFFIX` | Append `SUFFIX` to each result, and accept it on input. |
| `--from-unit=N` / `--to-unit=N` | Set the unit size the input or output is counted in. |

## Other options

| Option | Effect |
|---|---|
| `--invalid=MODE` | Choose what to do with input that is not a valid number: `abort`, `fail`, `warn`, or `ignore`. |
| `-z`, `--zero-terminated` | Treat the NUL character as the line delimiter. |
| `--debug` | Print warnings about questionable input. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every number was converted. |
| `2` | An input was not a valid number, or an option was misused. |

---

# expr

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Evaluate an arithmetic, string, or comparison expression and print the result.

`expr` evaluates a single expression and prints its value.

```
expr expression
```

```
$ expr 6 + 7
13
$ expr length "Peios"
5
```

Each part of the expression — every number, operator, and keyword — is a separate argument. That is why the spaces matter: `expr 6+7` is one argument and not an expression, while `expr 6 + 7` is three.

## Arithmetic

| Operator | Result |
|---|---|
| `ARG1 + ARG2` | Sum. |
| `ARG1 - ARG2` | Difference. |
| `ARG1 * ARG2` | Product. |
| `ARG1 / ARG2` | Integer quotient. |
| `ARG1 % ARG2` | Remainder. |

## Comparison

A comparison yields `1` for true and `0` for false. It is arithmetic when both sides are numbers, and lexical otherwise.

| Operator | True when… |
|---|---|
| `ARG1 = ARG2` | the two are equal. |
| `ARG1 != ARG2` | they are unequal. |
| `ARG1 < ARG2` | `ARG1` is less. |
| `ARG1 <= ARG2` | `ARG1` is less or equal. |
| `ARG1 > ARG2` | `ARG1` is greater. |
| `ARG1 >= ARG2` | `ARG1` is greater or equal. |

## Logic

| Operator | Result |
|---|---|
| `ARG1 \| ARG2` | `ARG1` if it is neither null nor `0`; otherwise `ARG2`. |
| `ARG1 & ARG2` | `ARG1` if neither argument is null or `0`; otherwise `0`. |

## String operations

| Form | Result |
|---|---|
| `length STRING` | The number of characters in `STRING`. |
| `substr STRING POS LENGTH` | `LENGTH` characters of `STRING`, counting `POS` from 1. |
| `index STRING CHARS` | The position of the first of any of `CHARS` in `STRING`, or `0`. |
| `STRING : REGEXP` | Match `REGEXP` anchored at the start of `STRING`. Returns the matched length, or the text captured by `\(…\)`. |
| `match STRING REGEXP` | The same as `STRING : REGEXP`. |

Many of these operators — `*`, `<`, `|`, `(` — are also meaningful to a shell, so quote or escape them so they reach `expr` intact.

## Exit status

`expr`'s exit status reports on the *value* it computed:

| Code | Meaning |
|---|---|
| `0` | The result was neither null nor `0`. |
| `1` | The result was null or `0`. |
| `2` | The expression was syntactically invalid. |
| `3` | An error occurred during evaluation — such as division by zero. |

---

# factor

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Print the prime factors of numbers.

`factor` prints the prime factors of a number.

```
factor [options] [number...]
```

```
$ factor 360
360: 2 2 2 3 3 5
```

Each line of output is the original number, a colon, and its prime factors in increasing order, repeated as often as they divide in. Give several numbers and `factor` factors each; give none and it reads numbers from standard input.

## Options

| Option | Effect |
|---|---|
| `-h`, `--exponents` | Write a repeated factor in exponent form — `p^e` — instead of repeating it. |

```
$ factor -h 360
360: 2^3 3^2 5
```

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every number was factored. |
| `1` | An input was not a valid positive integer. |

---

# test

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Evaluate a condition — a file check or a comparison — and report the result as an exit status.

`test` evaluates a single condition and reports whether it is **true** or **false**. It prints nothing — the answer is its exit status: `0` for true, `1` for false. That makes it the command a shell's `if`, `while`, `&&`, and `||` are built on.

```
test expression
[ expression ]
```

The two forms are the same command. `[` is `test` under another name; when invoked as `[`, it requires a closing `]` as its last argument. `[ -f notes.txt ]` and `test -f notes.txt` are identical.

```
$ test -f notes.txt && echo "the file exists"
```

## File tests

### Existence and type

| Test | True when the file… |
|---|---|
| `-e FILE` | exists. |
| `-f FILE` | exists and is a regular file. |
| `-d FILE` | exists and is a directory. |
| `-L FILE`, `-h FILE` | exists and is a symbolic link. |
| `-p FILE` | exists and is a named pipe (FIFO). |
| `-S FILE` | exists and is a socket. |
| `-b FILE` | exists and is a block device. |
| `-c FILE` | exists and is a character device. |
| `-s FILE` | exists and is not empty. |

### Access

These three tests ask whether you may actually *use* the file:

| Test | True when… |
|---|---|
| `-r FILE` | you may read the file. |
| `-w FILE` | you may write the file. |
| `-x FILE` | you may execute the file, or search the directory. |

On Peios these are **real access checks**. `test` asks the kernel whether your token is granted the right in question — a live check against the file's [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md), the same decision any actual read, write, or execute would face. The answer is therefore honest: `test -w FILE` is true exactly when a write would be permitted.

`-x` on a regular file means "you are permitted to execute it" — which is a different question from whether the file *is* an executable. A file can be runnable code and still fail `-x` for you, and the two are decided separately; see [Access decisions](/peios/security-fundamentals/access-decisions/overview.md).

### Comparing two files

| Test | True when… |
|---|---|
| `FILE1 -nt FILE2` | `FILE1` is newer than `FILE2`. |
| `FILE1 -ot FILE2` | `FILE1` is older than `FILE2`. |
| `FILE1 -ef FILE2` | both name the same file (same device and inode). |

### Other file tests

| Test | True when the file… |
|---|---|
| `-t FD` | file descriptor `FD` is open on a terminal. |
| `-N FILE` | has been modified since it was last read. |
| `-O FILE`, `-G FILE` | carries an owner-id / group-id field matching the caller's. |
| `-g FILE`, `-u FILE`, `-k FILE` | has its set-group-id, set-user-id, or sticky inode bit set. |

The last two rows probe **decorative inode fields**. The Peios access model does not consult them — they are stored on the inode and reported for completeness, but they carry no authority. For a true picture of what may be done to a file, use the access tests `-r`, `-w`, `-x` above, not `-O`, `-g`, or `-u`.

## String tests

| Test | True when… |
|---|---|
| `-n STRING` | `STRING` is not empty. A bare `STRING` means the same. |
| `-z STRING` | `STRING` is empty. |
| `STRING1 = STRING2` | the strings are equal. |
| `STRING1 != STRING2` | the strings are unequal. |
| `STRING1 < STRING2` | `STRING1` sorts before `STRING2`. |
| `STRING1 > STRING2` | `STRING1` sorts after `STRING2`. |

## Integer comparisons

| Test | True when… |
|---|---|
| `A -eq B` | `A` equals `B`. |
| `A -ne B` | `A` does not equal `B`. |
| `A -lt B` | `A` is less than `B`. |
| `A -le B` | `A` is less than or equal to `B`. |
| `A -gt B` | `A` is greater than `B`. |
| `A -ge B` | `A` is greater than or equal to `B`. |

## Combining conditions

| Form | Result |
|---|---|
| `! EXPRESSION` | The negation. |
| `( EXPRESSION )` | Grouping — escape the parentheses so the shell does not take them. |
| `EXPR1 -a EXPR2` | True when both are true. |
| `EXPR1 -o EXPR2` | True when either is true. |

`-a` and `-o` are genuinely ambiguous to parse and are best avoided. Combine separate `test` calls with the shell's own `&&` and `||` instead:

```
test -f notes.txt && test -r notes.txt
```

## A note on shells

Many command shells provide their own built-in `test` and `[`, and the shell's version is what runs when you type `test` at a prompt or in a script. A built-in may not behave as described here — in particular, the access tests `-r`, `-w`, and `-x` are the real access checks described above only when *this* command runs. To be certain you are running this command rather than the shell built-in, invoke it by its full path.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The expression was true. |
| `1` | The expression was false. |
| `2` | The expression was malformed. |

---

# true and false

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Two commands that do nothing — one always succeeds, the other always fails.

`true` and `false` do nothing at all. Their only product is an **exit status**: `true` always succeeds, `false` always fails.

```
true
false
```

```
$ true ; echo $?
0
$ false ; echo $?
1
```

`true` exits `0`. `false` exits `1`. They take no arguments that matter, produce no output, and have no effect on anything.

## What they are for

A command that is *only* an exit status is useful as a fixed value in places that expect a command:

- **An always-true or always-false loop.** `while true` is the standard way to write a loop that runs until something inside it breaks out.
- **A deliberate placeholder.** Setting a configurable command to `true` makes that step do nothing and "succeed"; `false` makes it a step that always fails.
- **A known result in a test.** When a script or a condition needs a guaranteed pass or fail to check against, `true` and `false` provide it.

They are the simplest commands there are — and the [overview](/peios/using-peios/peiosutils/output-and-evaluation/overview.md)'s note on exit status is the whole idea behind them.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Returned by `true`, always. |
| `1` | Returned by `false`, always. |

---

# tee

_Peios / Using Peios / Peiosutils / Output and evaluation_

> Copy standard input to standard output and to each named file at once.

`tee` reads standard input and writes it, unchanged, to standard output **and** to each file you name. It is the T-junction of a pipeline: the data keeps flowing down the pipe while a copy is also captured on disk.

```
tee [options] [file...]
```

```
$ echo saved | tee note.txt
saved
$ cat note.txt
saved
```

The name is the shape of the letter T — one stream in, the same stream out two ways. That makes `tee` the tool for the moment you want to both *see* (or keep piping) some data and *keep* it at the same time.

## Writing to several places at once

Every byte that arrives on standard input is written to standard output and to each named file. Name more than one file and each one receives a full, identical copy:

```
$ producer | tee a.log b.log c.log | consumer
```

Here `producer`'s output reaches `consumer` down the pipe exactly as if `tee` were not there, and `a.log`, `b.log`, and `c.log` each end up holding the same complete copy of it. With no files named, `tee` copies input straight to standard output and nothing else — a plain pass-through.

`tee` does not buffer between reads: it writes each chunk out as it is read, so a file being written by `tee` fills up as the data flows, rather than all at once when the input ends.

## Overwrite or append

By default, each named file is **truncated** — opened fresh, so any existing contents are discarded before the new data is written. A file that does not exist is created.

With `-a` (`--append`), `tee` **appends** to each named file instead: existing contents are kept and the new data is added to the end. A file that does not exist is still created.

```
$ echo first  | tee log.txt        # log.txt now holds "first"
$ echo second | tee -a log.txt     # log.txt now holds "first" then "second"
```

## Creating a file inherits a security descriptor

When `tee` **creates** a new output file — a named file that did not already exist — that file needs a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md), and it inherits one from the directory it is created in. This is the same creation-inherits-a-descriptor rule that governs every file-creating tool on Peios; see [Files and directories](/peios/using-peios/peiosutils/files-and-directories/overview.md) for the shared model.

With `-a`, when the named file already exists, `tee` opens that existing file to append and creates nothing, so no new descriptor is involved. Whether opening a file to write succeeds — whether creating a new one or writing an existing one — is decided by the normal access check against the relevant directory or file. `tee` reports a file it cannot open and, by default, carries on with the outputs it *can* write.

## Files named `-`

`tee` gives no special meaning to `-`. A file argument of `-` refers to a file literally named `-` in the current directory; it is opened, created, and written exactly like any other named file. It is **not** a stand-in for standard output — standard output is always written and needs no naming.

## Ignoring interrupts

With `-i` (`--ignore-interrupts`), `tee` ignores the interrupt signal (the one a terminal sends on Ctrl-C). This lets `tee` keep copying to its files even as an interrupt tears down the rest of a pipeline, so the capture is not cut short.

## Behaviour on write errors

By default, if a write to one output fails, `tee` reports it and stops writing to *that* output, but keeps writing to the others; a broken-pipe error on standard output is treated quietly. Two options change this policy.

`-p` sets write-error behaviour so that errors on a pipe (a downstream reader that has gone away) are ignored while other write errors are still reported.

`--output-error[=MODE]` sets the policy explicitly. Given on its own, with no `=MODE`, it selects `warn-nopipe`. The modes are:

| Mode | Behaviour |
|---|---|
| `warn` | Warn on a write error to **any** output, including a broken pipe, and continue with the remaining outputs. |
| `warn-nopipe` | Warn on a write error that is **not** a pipe error, and continue. Broken-pipe errors are ignored. This is the mode selected by a bare `--output-error`. |
| `exit` | **Exit** on a write error to any output, including a broken pipe. |
| `exit-nopipe` | Exit on a write error that is **not** a pipe error. Broken-pipe errors are ignored. |

## Options

| Option | Effect |
|---|---|
| `-a`, `--append` | Append to each named file rather than truncating it. |
| `-i`, `--ignore-interrupts` | Ignore the interrupt signal. |
| `-p` | Ignore errors from writing to a broken pipe, still reporting other write errors. |
| `--output-error[=MODE]` | Set the write-error policy: `warn`, `warn-nopipe`, `exit`, or `exit-nopipe`. A bare `--output-error` means `warn-nopipe`. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The input was copied to standard output and every named file without error. |
| `1` | A file could not be opened, a write failed under a policy that reports or exits on it, or the input could not be read. |

---

# Hashing and encoding

_Peios / Using Peios / Peiosutils / Hashing and encoding_

> The commands that compute checksums and hashes to verify data integrity, and that encode binary data into text and back.

This topic covers two related jobs. **Hashing** reduces a file to a short fixed-size value — a checksum — that can be used to tell whether the file has changed. **Encoding** rewrites binary data as plain text so it can travel safely through channels that only handle text.

## The commands

**Checksums and hashes**

| Command | Purpose |
|---|---|
| [checksum commands](/peios/using-peios/peiosutils/hashing-and-encoding/checksum-commands.md) | `md5sum`, `sha1sum`, `sha256sum`, and the rest — one command per hash algorithm. |
| [`cksum`](/peios/using-peios/peiosutils/hashing-and-encoding/cksum.md) | The general checksum tool — any of the algorithms, selected by an option. |
| [`sum`](/peios/using-peios/peiosutils/hashing-and-encoding/sum.md) | A small, legacy block-checksum. |

**Encoding**

| Command | Purpose |
|---|---|
| [`base32` and `base64`](/peios/using-peios/peiosutils/hashing-and-encoding/base32-and-base64.md) | Encode binary data as text using the base32 or base64 alphabet, and decode it back. |
| [`basenc`](/peios/using-peios/peiosutils/hashing-and-encoding/basenc.md) | The general encoder — base64, base32, base16, and several more, selected by an option. |

## What these commands are not

It is worth being clear up front, because both jobs are easy to mistake for something they are not.

**A hash is not encryption.** Hashing is one-way: a checksum tells you *whether* data has changed, but the original cannot be recovered from it. It protects against corruption and detects tampering — it does not keep anything secret.

**Encoding is not encryption either.** `base64` rewrites data into a text-safe form, but anyone can decode it straight back — it is a change of *representation*, not of secrecy. Encoding something does not protect it.

Neither hashing nor encoding hides data. They are about *integrity* and *transport*, not confidentiality.

## Where to start

To verify a file you downloaded against a published checksum, read the [checksum commands](/peios/using-peios/peiosutils/hashing-and-encoding/checksum-commands.md). To turn binary data into something safe to paste into text, read [`base32` and `base64`](/peios/using-peios/peiosutils/hashing-and-encoding/base32-and-base64.md).

---

# Checksum commands

_Peios / Using Peios / Peiosutils / Hashing and encoding_

> md5sum, sha1sum, sha256sum and the rest — the family of commands that each compute and verify one fixed hash algorithm.

This page covers a family of commands that all work the same way and differ only in **which hash algorithm** they use:

| Command | Algorithm | Digest size |
|---|---|---|
| `md5sum` | MD5 | 128-bit |
| `sha1sum` | SHA-1 | 160-bit |
| `sha224sum` | SHA-224 | 224-bit |
| `sha256sum` | SHA-256 | 256-bit |
| `sha384sum` | SHA-384 | 384-bit |
| `sha512sum` | SHA-512 | 512-bit |
| `b2sum` | BLAKE2b | up to 512-bit |

```
sha256sum [options] [file...]
```

Everything below is written with `sha256sum`, but applies to every command in the table.

## Computing a checksum

Run plainly, the command prints the hash of each file, followed by the file name:

```
$ sha256sum installer.iso
9f86d0818884...b1a5  installer.iso
```

With no file, it reads standard input. The hash is a fingerprint of the file's contents: change a single byte and the hash changes completely.

## Verifying with a checksum

The everyday use is **verification** — confirming a file is exactly what it should be, usually a download.

First, the file's publisher computes a checksum and publishes it, often in a file:

```
$ sha256sum installer.iso > installer.iso.sha256
```

Then anyone with the file and that checksum file can verify:

```
$ sha256sum -c installer.iso.sha256
installer.iso: OK
```

`-c` reads each `hash  filename` line, recomputes the hash, and reports `OK` or `FAILED`. A `FAILED` means the file is not the one the checksum was made from — corrupted in transit, or altered.

| Option | Effect |
|---|---|
| `-c`, `--check` | Read checksums from the given files and verify them. |
| `--ignore-missing` | In check mode, do not fail over files that are listed but absent. |
| `--quiet` | In check mode, print nothing for files that pass — only failures. |
| `--status` | In check mode, print nothing at all; report only through the exit status. |
| `-w`, `--warn` | Warn about improperly formatted lines in the checksum file. |
| `--strict` | In check mode, fail if any checksum line is malformed. |

## Output format

| Option | Effect |
|---|---|
| `--tag` | Produce a tagged line — `SHA256 (file) = hash` — that records which algorithm was used. |
| `--untagged` | Produce the plain `hash  file` line. This is the default. |
| `-z`, `--zero` | End each output line with a NUL character instead of a newline. |
| `-b`, `--binary` | Note the file as read in binary mode. |
| `-t`, `--text` | Note the file as read in text mode. |

`b2sum` additionally accepts `-l`, `--length=BITS` to produce a shorter BLAKE2b digest.

## A note on choosing an algorithm

These commands detect change — but not all of them resist a *deliberate* attempt to forge a match. MD5 and SHA-1 can be defeated by an attacker who wants two different files to share a checksum. They are still fine for catching accidental corruption, but for verifying that a file has not been **tampered with**, use a SHA-2 command (`sha256sum` and up) or `b2sum`.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The checksums were computed, or — in check mode — every file verified. |
| `1` | In check mode, a file failed verification. |
| `2` | An error — a file could not be read, or a checksum file was malformed. |

---

# cksum

_Peios / Using Peios / Peiosutils / Hashing and encoding_

> The general checksum tool — compute or verify a digest with any of the supported algorithms.

`cksum` is the **general** checksum command. Where each of the [checksum commands](/peios/using-peios/peiosutils/hashing-and-encoding/checksum-commands.md) is fixed to one algorithm, `cksum` does any of them — the algorithm is an option.

```
cksum [options] [file...]
```

```
$ cksum installer.iso
3915528286 372736000 installer.iso
$ cksum -a sha256 installer.iso
9f86d0818884...b1a5  installer.iso
```

With no `-a`, `cksum` computes a CRC checksum and prints the CRC value, the file's byte count, and the name. With `-a`, it behaves like the corresponding dedicated command.

## Choosing the algorithm

| Option | Effect |
|---|---|
| `-a`, `--algorithm=NAME` | Use the named algorithm. |

`NAME` may be:

| Name | Algorithm |
|---|---|
| `crc` | The default CRC. |
| `crc32b` | A CRC-32 variant. |
| `md5` | MD5 — as `md5sum`. |
| `sha1` | SHA-1 — as `sha1sum`. |
| `sha224`, `sha256`, `sha384`, `sha512` | The SHA-2 family — as the matching `sha*sum`. |
| `sha3` | SHA-3. |
| `blake2b` | BLAKE2b — as `b2sum`. |
| `sm3` | The SM3 hash. |
| `sysv`, `bsd` | The legacy block checksums — as [`sum`](/peios/using-peios/peiosutils/hashing-and-encoding/sum.md). |

`crc`, `crc32b`, `sha3`, and `sm3` are available *only* through `cksum` — there is no dedicated command for them.

## Verifying

`cksum` checks files the same way the dedicated commands do:

| Option | Effect |
|---|---|
| `-c`, `--check` | Read checksums and verify the files against them. |
| `--ignore-missing` | Do not fail over listed files that are absent. |
| `--quiet` | Print nothing for files that pass. |
| `--status` | Print nothing; report only through the exit status. |
| `-w`, `--warn` | Warn about malformed checksum lines. |
| `--strict` | Fail on a malformed checksum line. |

## Output format

| Option | Effect |
|---|---|
| `--tag` | Produce a tagged `ALGORITHM (file) = hash` line. The default for most algorithms. |
| `--untagged` | Produce a plain `hash  file` line. |
| `--raw` | Output the raw binary digest, with nothing else. |
| `--base64` | Print the digest in base64 rather than hexadecimal. |
| `-l`, `--length=BITS` | For an algorithm that supports it, produce a shorter digest. |
| `-z`, `--zero` | End each output line with a NUL character. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success — or, in check mode, every file verified. |
| `1` | In check mode, a file failed verification. |
| `2` | An error — a file could not be read, or an option was invalid. |

---

# sum

_Peios / Using Peios / Peiosutils / Hashing and encoding_

> Compute a small, legacy block checksum of a file.

`sum` computes a small, old-style checksum of a file and reports it along with the file's size in blocks.

```
sum [options] [file...]
```

```
$ sum archive.tar
12345 84 archive.tar
```

The output is the checksum, the block count, and the file name. With no file, `sum` reads standard input.

## Which algorithm

`sum` predates the modern hashes and offers two historical algorithms:

| Option | Algorithm |
|---|---|
| `-r` | The BSD checksum, counted in 1 KiB blocks. This is the default. |
| `-s`, `--sysv` | The System V checksum, counted in 512-byte blocks. |

## When to use it

`sum` exists for compatibility — for reading checksums produced by old tools, and for scripts that still expect its output. Its checksum is short and weak: it catches some accidental corruption, but it is easily fooled and gives no protection against tampering.

For anything new, do not use `sum`. Use a [checksum command](/peios/using-peios/peiosutils/hashing-and-encoding/checksum-commands.md) such as `sha256sum`, or [`cksum`](/peios/using-peios/peiosutils/hashing-and-encoding/cksum.md) — both of which `cksum` can still produce, via `cksum -a bsd` and `cksum -a sysv`, if you need the legacy values.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The checksum was computed. |
| `1` | A file could not be read. |

---

# base32 and base64

_Peios / Using Peios / Peiosutils / Hashing and encoding_

> Encode binary data as text using the base32 or base64 alphabet, and decode it back.

`base32` and `base64` encode binary data as plain text — and decode it back. They are the same command with one difference: the **alphabet** they use.

```
base32 [options] [file]
base64 [options] [file]
```

```
$ echo Peios | base64
UGVpb3MK
$ echo UGVpb3MK | base64 -d
Peios
```

## What encoding is for

Some channels only carry text safely — they mangle or reject arbitrary bytes. Encoding rewrites binary data using a small, safe set of characters, so it can pass through unharmed; decoding reverses it exactly. `base64` uses 64 characters and is the more compact; `base32` uses 32 and is more robust where case might not survive. With no file, both read standard input.

Encoding is **not** encryption — anyone can decode the result. See the [overview](/peios/using-peios/peiosutils/hashing-and-encoding/overview.md).

## Options

By default these commands *encode*. The options below apply identically to both.

| Option | Effect |
|---|---|
| `-d`, `--decode` | Decode instead of encode. |
| `-i`, `--ignore-garbage` | When decoding, skip any characters that are not part of the alphabet, rather than failing on them. |
| `-w`, `--wrap=COLS` | When encoding, wrap the output to lines of `COLS` characters. The default is 76; `-w 0` disables wrapping and produces one unbroken line. |

When decoding, newlines in the input are always tolerated; `-i` is for *other* stray characters.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | A file could not be read, or — when decoding — the input was not valid for the alphabet. |

---

# basenc

_Peios / Using Peios / Peiosutils / Hashing and encoding_

> The general encoder — encode and decode data in base64, base32, base16, and several other encodings.

`basenc` is the **general** encoding command. Where [`base32` and `base64`](/peios/using-peios/peiosutils/hashing-and-encoding/base32-and-base64.md) each do one encoding, `basenc` does many — the encoding is chosen by an option.

```
basenc encoding [options] [file]
```

```
$ echo Peios | basenc --base16
5065696F730A
```

Every run of `basenc` must name an encoding. With no file, it reads standard input.

## The encodings

| Option | Encoding |
|---|---|
| `--base64` | Standard base64 — the same as the `base64` command. |
| `--base64url` | Base64 with a file- and URL-safe alphabet. |
| `--base32` | Standard base32 — the same as the `base32` command. |
| `--base32hex` | Base32 with the extended-hex alphabet. |
| `--base16` | Hexadecimal. |
| `--base2msbf` | A bit string, most-significant bit first. |
| `--base2lsbf` | A bit string, least-significant bit first. |
| `--z85` | A compact, ASCII85-style encoding. When encoding, the input length must be a multiple of 4; when decoding, a multiple of 5. |
| `--base58` | Base58 — an alphabet chosen so its characters are not visually confusable. |

## Options

By default `basenc` *encodes*. These options apply to whichever encoding you chose:

| Option | Effect |
|---|---|
| `-d`, `--decode` | Decode instead of encode. |
| `-i`, `--ignore-garbage` | When decoding, skip characters that are not part of the alphabet. |
| `-w`, `--wrap=COLS` | When encoding, wrap output to lines of `COLS` characters. `-w 0` disables wrapping. |

## `basenc` and the dedicated commands

For plain base64 or base32, `base64` and `base32` are the shorter way to ask. Use `basenc` when you need an encoding the dedicated commands do not offer — hex, URL-safe base64, the bit-string forms, z85, or base58.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | No encoding was named, a file could not be read, or the input was not valid for the chosen encoding. |

---

# System and processes

_Peios / Using Peios / Peiosutils / System and processes_

> The commands that report on the system, run other commands in a managed way, signal processes, and work with time, terminals, and storage.

This topic gathers the commands that deal with the **running system** rather than with files: reading the time, asking what hardware and system you are on, launching another command under some constraint, signalling a process, working with the terminal.

## The commands

**Time**

| Command | Purpose |
|---|---|
| [`date`](/peios/using-peios/peiosutils/system-and-processes/date.md) | Print — or set — the system date and time. |
| [`sleep`](/peios/using-peios/peiosutils/system-and-processes/sleep.md) | Pause for a given length of time. |
| [`uptime`](/peios/using-peios/peiosutils/system-and-processes/uptime.md) | Show how long the system has been running. |

**Running a command under a constraint**

| Command | Purpose |
|---|---|
| [`timeout`](/peios/using-peios/peiosutils/system-and-processes/timeout.md) | Run a command, and kill it if it runs too long. |
| [`nohup`](/peios/using-peios/peiosutils/system-and-processes/nohup.md) | Run a command so it survives the terminal closing. |
| [`nice`](/peios/using-peios/peiosutils/system-and-processes/nice.md) | Run a command at an adjusted scheduling priority. |
| [`stdbuf`](/peios/using-peios/peiosutils/system-and-processes/stdbuf.md) | Run a command with altered stream buffering. |
| [`chroot`](/peios/using-peios/peiosutils/system-and-processes/chroot.md) | Run a command with a different directory as its root. |

**Signalling processes**

| Command | Purpose |
|---|---|
| [`kill`](/peios/using-peios/peiosutils/system-and-processes/kill.md) | Send a signal to a process. |

**System identity and capacity**

| Command | Purpose |
|---|---|
| [`uname`](/peios/using-peios/peiosutils/system-and-processes/uname.md) | Print system information — the OS, the kernel, the machine. |
| [`arch`](/peios/using-peios/peiosutils/system-and-processes/arch.md) | Print the machine's hardware architecture. |
| [`hostname`](/peios/using-peios/peiosutils/system-and-processes/hostname.md) | Display — or set — the system's host name. |
| [`hostid`](/peios/using-peios/peiosutils/system-and-processes/hostid.md) | Print the host's numeric identifier. |
| [`nproc`](/peios/using-peios/peiosutils/system-and-processes/nproc.md) | Print how many processor cores are available. |

**Who you are**

| Command | Purpose |
|---|---|
| [`whoami`](/peios/using-peios/peiosutils/system-and-processes/whoami.md) | Print the name of the user a process is running as. |
| [`id`](/peios/using-peios/peiosutils/system-and-processes/id.md) | Print the user and group identity of a process, or of a named user. |
| [`groups`](/peios/using-peios/peiosutils/system-and-processes/groups.md) | Print the groups a user belongs to. |
| [`logname`](/peios/using-peios/peiosutils/system-and-processes/logname.md) | Print the login name of the user a process is running as. |

These report the **projection** of a token onto POSIX user and group numbers, which is what Linux programs see. The numbers grant nothing on their own — [`id`](/peios/using-peios/peiosutils/system-and-processes/id.md) explains what they can and cannot say, and [`token`](/peios/security-fundamentals/tokens/token-command.md) shows the identity underneath them.

**Storage and terminal**

| Command | Purpose |
|---|---|
| [`sync`](/peios/using-peios/peiosutils/system-and-processes/sync.md) | Flush cached writes out to persistent storage. |
| [`tty`](/peios/using-peios/peiosutils/system-and-processes/tty.md) | Print the name of the terminal on standard input. |
| [`stty`](/peios/using-peios/peiosutils/system-and-processes/stty.md) | Display or change terminal settings. |

## A note on changing the system

Most commands here only *report*. A few can *change* the running system — `date` can set the clock, `hostname` can set the host name. Changing system-wide state is a privileged operation: it succeeds only for a caller whose token holds the right to do it, and is refused otherwise. The pages for those commands say so where it applies, and [Privileges](/peios/security-fundamentals/privileges/overview.md) is the full picture.

## Where to start

[`uname`](/peios/using-peios/peiosutils/system-and-processes/uname.md) answers "what am I running on?". [`kill`](/peios/using-peios/peiosutils/system-and-processes/kill.md) is the one to read carefully — sending the wrong signal to the wrong process is a quick way to lose work.

---

# date

_Peios / Using Peios / Peiosutils / System and processes_

> Print or set the system date and time, with a flexible output format.

`date` prints the current date and time. With the right option, it can also *set* the system clock, display some other moment, or format a date however you need.

```
date [options] [+format]
```

```
$ date
Sun 17 May 2026 14:32:08 BST
$ date +%Y-%m-%d
2026-05-17
```

## Formatting the output

A `+format` argument controls the output. The format string is printed literally, except for `%` sequences, each of which is replaced by a piece of the date. The common ones:

| Sequence | Is replaced by |
|---|---|
| `%Y` `%m` `%d` | Year, month, day. |
| `%H` `%M` `%S` | Hour, minute, second. |
| `%F` | The full date — same as `%Y-%m-%d`. |
| `%T` | The time — same as `%H:%M:%S`. |
| `%A` `%a` | Weekday name, full and abbreviated. |
| `%B` `%b` | Month name, full and abbreviated. |
| `%j` | Day of the year. |
| `%s` | Seconds since 1970-01-01 UTC. |
| `%z` `%Z` | Numeric and named time zone. |
| `%%` | A literal `%`. |

The full set is long; `date --help` lists every sequence with an example.

## Displaying a different moment

| Option | Effect |
|---|---|
| `-d`, `--date=STRING` | Show the time described by `STRING` — `"yesterday"`, `"next Friday"`, `"@1615432800"` — instead of now. |
| `-r`, `--reference=FILE` | Show `FILE`'s last modification time. |
| `-f`, `--file=DATEFILE` | Like `-d`, but once for each line of `DATEFILE`. |
| `-u`, `--universal` | Work in Coordinated Universal Time (UTC) rather than the local zone. |

## Standard formats

| Option | Output |
|---|---|
| `-I`, `--iso-8601[=FMT]` | ISO 8601 — `FMT` is `date`, `hours`, `minutes`, `seconds`, or `ns`. |
| `-R`, `--rfc-email` | The format used in email headers. |
| `--rfc-3339=FMT` | RFC 3339 — `FMT` is `date`, `seconds`, or `ns`. |

## Setting the clock

| Option | Effect |
|---|---|
| `-s`, `--set=STRING` | Set the system clock to the time described by `STRING`. |

Setting the clock changes state for the whole system, so it is a **privileged** operation — it succeeds only for a caller whose token carries the right to set the time, and is refused otherwise. See [Privileges](/peios/security-fundamentals/privileges/overview.md).

## Exit status

| Code | Meaning |
|---|---|
| `0` | The date was printed or set. |
| `1` | An invalid date or format, or the clock could not be set. |

---

# sleep

_Peios / Using Peios / Peiosutils / System and processes_

> Pause for a given length of time.

`sleep` does nothing for a given length of time, then exits.

```
sleep number[suffix]...
```

```
$ sleep 5          # pause for five seconds
$ sleep 1.5h       # pause for an hour and a half
```

It is used to space things out — a pause between the steps of a script, a delay before a retry.

## Specifying the duration

`number` may be a whole number or a fraction. A `suffix` gives the unit:

| Suffix | Unit |
|---|---|
| `s` | Seconds. This is the default if no suffix is given. |
| `m` | Minutes. |
| `h` | Hours. |
| `d` | Days. |

Given more than one argument, `sleep` waits for the **sum** of them — `sleep 1m 30s` pauses for ninety seconds.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The full time elapsed. |
| `1` | A bad argument, or `sleep` was interrupted before the time was up. |

---

# timeout

_Peios / Using Peios / Peiosutils / System and processes_

> Run a command and kill it if it is still running after a time limit.

`timeout` runs a command with a time limit. If the command finishes in time, nothing special happens. If it is still running when the limit is reached, `timeout` kills it.

```
timeout [options] duration command...
```

```
$ timeout 30s slow-fetch https://example.com
```

It is the guard against a command that might hang — a network fetch, a script that could loop forever.

## The duration

`duration` is a number with an optional unit suffix: `s` for seconds (the default), `m` for minutes, `h` for hours, `d` for days. A duration of `0` disables the limit.

## How the command is stopped

When the limit is reached, `timeout` sends the command a `TERM` signal — a request to shut down cleanly. A well-behaved command stops there. A command that ignores `TERM` keeps running, which is what `-k` is for.

| Option | Effect |
|---|---|
| `-s`, `--signal=SIGNAL` | Send `SIGNAL` instead of `TERM` when the limit is reached. |
| `-k`, `--kill-after=DURATION` | If the command is *still* running `DURATION` after the first signal, send it a `KILL` signal — which cannot be ignored. |
| `--preserve-status` | Exit with the command's own status even when it timed out. |
| `--foreground` | Allow the command to keep reading from the terminal; do not time out the command's own children. |
| `-v`, `--verbose` | Report to standard error whenever a signal is sent. |

## Exit status

`timeout` passes through the command's exit status when the command finishes on its own. Otherwise:

| Code | Meaning |
|---|---|
| (command's own) | The command finished within the limit. |
| `124` | The command timed out. |
| `125` | `timeout` itself failed. |
| `126` | The command was found but could not be run. |
| `127` | The command was not found. |
| `137` | The command was ended by a `KILL` signal. |

---

# nohup

_Peios / Using Peios / Peiosutils / System and processes_

> Run a command so it keeps running after the terminal closes — and how the nohup.out it creates is secured.

`nohup` runs a command so that it **survives the terminal closing**. Normally, when a terminal session ends, the system sends a hangup signal to the commands running under it, and they stop. A command started with `nohup` ignores that signal and keeps running.

```
nohup [options] command [arg]...
```

```
$ nohup long-build &
```

It is how you start something that needs to outlast your session — a long build, a background job — without it being killed the moment you disconnect.

## Where the output goes

A command run with `nohup` is meant to outlive the terminal, so `nohup` cannot leave its streams attached to one. It redirects any stream that is still connected to a terminal:

- **Standard input**, if it is a terminal, is replaced with an empty source — the command reads nothing.
- **Standard output**, if it is a terminal, is appended to a file named `nohup.out` in the current directory. If that file cannot be opened there, `nohup` falls back to `nohup.out` in your home directory.
- **Standard error**, if it is a terminal, is sent to wherever standard output now goes.

Streams that you have already redirected yourself — to a file, or a pipe — are left as they are.

## How nohup.out is secured

When `nohup` has to **create** `nohup.out`, that file is a new file and needs a [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md). `nohup` does not let it inherit one from the directory. It creates `nohup.out` with a deliberate, locked descriptor: **full access for the owner, and no one else**.

The reasoning is that `nohup.out` captures whatever the command writes, which the person running it has not chosen to share. A file that appears as a side effect should not be more open than its creator intended — so `nohup` gives it the safe default of owner-only, rather than whatever the surrounding directory would have handed down.

| Option | Effect |
|---|---|
| `--sddl=SDDL` | Create `nohup.out` with the security descriptor given as an SDDL string, instead of the owner-only default. |

`--sddl` only matters when `nohup` actually creates the file. If `nohup.out` already exists, `nohup` appends to it and leaves its existing security alone.

## Exit status

When `nohup` runs a command, it exits with **that command's** status once it finishes. The exception is a failure in `nohup` itself:

| Code | Meaning |
|---|---|
| (command's own) | The command ran; this is its exit status. |
| `125` | `nohup` could not set things up — it could not detach from the terminal, or could not open `nohup.out` anywhere. The command was not run. |

---

# nice

_Peios / Using Peios / Peiosutils / System and processes_

> Run a command at an adjusted scheduling priority.

`nice` runs a command at an adjusted **scheduling priority** — making it more, or less, willing to give the processor up to other work.

```
nice [options] [command [arg]...]
```

```
$ nice -n 15 big-batch-job
```

With no command, `nice` prints the current niceness.

## Niceness

A process has a *niceness* — a number from **-20** to **19**:

- A **high** niceness (toward 19) means the process is "nicer" to others — it yields the processor readily. Good for background work that should not get in the way.
- A **low** niceness (toward -20) means the process is favoured — it gets the processor more often.

By default `nice` adds **10** to the niceness, making the command run in the background more gracefully.

| Option | Effect |
|---|---|
| `-n`, `--adjustment=N` | Add `N` to the niceness instead of 10. `N` may be negative. |

Raising a command's niceness — running it *lower* priority — is always allowed. Lowering the niceness, to claim a *higher* priority than normal, is a privileged request and may be refused; when it is, `nice` warns and runs the command at the priority it was permitted.

## Exit status

When `nice` runs a command, it exits with **that command's** status. The exception is a failure in `nice` itself:

| Code | Meaning |
|---|---|
| (command's own) | The command ran; this is its exit status. |
| `0` | No command was given; the niceness was printed. |
| `125` | `nice` itself failed — for example, a bad adjustment value. |
| `126` | The command was found but could not be run. |
| `127` | The command was not found. |

---

# kill

_Peios / Using Peios / Peiosutils / System and processes_

> Send a signal to a process.

`kill` sends a **signal** to a process. Despite the name, a signal is not always fatal — it is a message, and some signals ask a process to do something other than stop.

```
kill [options] pid...
```

```
$ kill 4821                 # ask process 4821 to terminate
$ kill -KILL 4821           # force it to stop
```

A `pid` is a process identifier — the number a running process is known by.

## Signals

With no option, `kill` sends `TERM` — a polite request to shut down, which a well-behaved process honours by cleaning up and exiting. The signals you will use most:

| Signal | Meaning |
|---|---|
| `TERM` | Terminate. The default — a request to stop cleanly. |
| `KILL` | Stop immediately. Cannot be caught or ignored, so it cannot be refused — but the process gets no chance to clean up. The last resort. |
| `HUP` | Hang up. Conventionally tells a long-running service to reload its configuration. |
| `INT` | Interrupt — the signal a terminal sends on Ctrl-C. |
| `STOP` / `CONT` | Suspend the process / resume a suspended one. |

| Option | Effect |
|---|---|
| `-s`, `--signal=SIGNAL` | Send `SIGNAL` instead of `TERM`. |
| `-SIGNAL` | A shorthand — `-KILL` or `-9` both send `KILL`. |
| `-l`, `--list` | List the signal names. |
| `-L`, `--table` | List the signals as a table of numbers and names. |

Use `KILL` only when `TERM` has failed. A process killed outright cannot save its work or release what it holds.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Every signal was sent. |
| `1` | A signal could not be sent — a process that does not exist, or one you are not permitted to signal. |

---

# uname

_Peios / Using Peios / Peiosutils / System and processes_

> Print system information — the operating system, the kernel, and the machine.

`uname` prints information about the system you are running on.

```
uname [options]
```

```
$ uname
Peios
$ uname -a
Peios host-01 1.4.0 #1 SMP 2026-05-10 x86_64 Peios
```

With no option, `uname` prints the operating-system name — `Peios`.

## What each option prints

| Option | Prints |
|---|---|
| `-s` | The kernel name. |
| `-o` | The operating-system name — `Peios`. |
| `-n` | The node name — the name this system is known by on the network. |
| `-r` | The kernel release. |
| `-v` | The kernel version. |
| `-m` | The machine's hardware name (its architecture). |
| `-p` | The processor type. |
| `-i` | The hardware platform. |
| `-a` | Everything — equivalent to `-mnrsvo`. |

Give several options and `uname` prints those fields, in a fixed order, on one line.

## The operating system is Peios

`uname -o`, and the operating-system field of `uname -a`, report `Peios`. This is the field a script should check when it wants to confirm what system it is on.

For the machine architecture alone, [`arch`](/peios/using-peios/peiosutils/system-and-processes/arch.md) is the shorter command — it prints exactly what `uname -m` prints.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The requested information was printed. |
| `1` | The system information could not be read. |

---

# arch

_Peios / Using Peios / Peiosutils / System and processes_

> Print the machine's hardware architecture.

`arch` prints the machine's hardware architecture — the kind of processor the system runs on.

```
arch
```

```
$ arch
x86_64
```

That is the whole command. It takes no options and prints a single name.

`arch` prints exactly what [`uname -m`](/peios/using-peios/peiosutils/system-and-processes/uname.md) prints. It exists as a short, obvious name for the one question "what architecture is this?" — convenient in a script that picks an architecture-specific path.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The architecture was printed. |
| `1` | The architecture could not be determined. |

---

# hostname

_Peios / Using Peios / Peiosutils / System and processes_

> Display or set the system's host name.

`hostname` displays the system's **host name** — the name the system is known by.

```
hostname [options] [name]
```

```
$ hostname
host-01.example.com
```

## What is displayed

By default `hostname` prints the fully qualified name. These options narrow or change what is shown:

| Option | Prints |
|---|---|
| `-f`, `--fqdn` | The fully qualified domain name — the full `host.domain` form. This is the default. |
| `-s`, `--short` | The short host name only — the part before the first dot. |
| `-d`, `--domain` | The DNS domain part only. |
| `-i`, `--ip-address` | The network address (or addresses) the host name resolves to. |

## Setting the host name

Given a `name` argument, `hostname` sets the system's host name to it.

```
$ hostname host-02
```

Changing the host name changes state for the whole system, so it is a **privileged** operation — it succeeds only for a caller whose token carries the right to do it, and is refused otherwise. See [Privileges](/peios/security-fundamentals/privileges/overview.md).

## Exit status

| Code | Meaning |
|---|---|
| `0` | The host name was displayed or set. |
| `1` | A failure — the name could not be read, set, or resolved. |

---

# hostid

_Peios / Using Peios / Peiosutils / System and processes_

> Print the host's numeric identifier.

`hostid` prints the host's numeric identifier, in hexadecimal.

```
hostid
```

```
$ hostid
007f0100
```

The host id is a number associated with the system. It takes no options and prints one value.

Where [`hostname`](/peios/using-peios/peiosutils/system-and-processes/hostname.md) gives the system's *name* — meant to be read by people — `hostid` gives a numeric value, used by software that wants a machine identifier in a fixed numeric form.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The host id was printed. |

---

# nproc

_Peios / Using Peios / Peiosutils / System and processes_

> Print how many processor cores are available.

`nproc` prints the number of processor cores available.

```
nproc [options]
```

```
$ nproc
8
```

By default it prints the number of cores available **to the current process** — which can be fewer than the machine has, if the process has been restricted to a subset. It is most often used to decide how many parallel jobs to run.

## Options

| Option | Effect |
|---|---|
| `--all` | Print the number of cores the *whole system* has, ignoring any restriction on the current process. |
| `--ignore=N` | Subtract `N` from the count — for leaving some cores free rather than using every one. |

The environment variables `OMP_NUM_THREADS` and `OMP_THREAD_LIMIT`, if set, also bound the number `nproc` reports.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The count was printed. |
| `1` | An option value was invalid. |

---

# uptime

_Peios / Using Peios / Peiosutils / System and processes_

> Show how long the system has been running.

`uptime` shows how long the system has been running, along with a few related figures.

```
uptime [options]
```

```
$ uptime
 14:32:08  up 6 days,  3:21,  load average: 0.18, 0.24, 0.21
```

The default line packs in three things:

- the **current time**;
- how long the system has been **up**;
- the **load average** — a rough measure of how busy the system has been over the last 1, 5, and 15 minutes.

If you know `uptime` from other systems you may be expecting a count of logged-in users between the uptime and the load average. Peios does not print one. That count comes from a login database (`utmp`) that Peios does not keep — logon sessions are a [KACS](/peios/security-fundamentals/access-decisions/overview.md) concept, and the tooling to report them is not built yet. Rather than print a figure that would always read `0 users`, the field is left out until it can be answered honestly.

## Options

| Option | Effect |
|---|---|
| `-p`, `--pretty` | Show just the uptime, in a readable phrase — `up 6 days, 3 hours, 21 minutes`. |
| `-s`, `--since` | Show the date and time the system started, rather than how long ago that was. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The uptime was printed. |
| `1` | The boot time could not be read. |

---

# sync

_Peios / Using Peios / Peiosutils / System and processes_

> Flush cached writes out to persistent storage.

`sync` flushes cached writes to persistent storage.

```
sync [options] [file...]
```

```
$ sync
```

When a program writes to a file, the data does not always reach the physical device immediately — for speed, the system holds recent writes in memory and writes them out a little later. `sync` forces that pending data out *now*, so that what is on the device matches what programs have written.

It is the command to run before doing something that should not lose recent writes — before powering off by hand, or before removing storage.

## Options

With no argument, `sync` flushes everything. The options narrow it:

| Option | Effect |
|---|---|
| `file...` | Flush only the given files, rather than everything. |
| `-d`, `--data` | For the given files, flush only the file data, not metadata that does not need it. |
| `-f`, `--file-system` | Flush the entire file system that contains each given file. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The flush completed. |
| `1` | A named file could not be reached. |

---

# tty

_Peios / Using Peios / Peiosutils / System and processes_

> Print the name of the terminal connected to standard input.

`tty` prints the name of the terminal connected to standard input.

```
tty [options]
```

```
$ tty
/dev/pts/3
```

If standard input is not a terminal — because it has been redirected from a file or a pipe — `tty` prints `not a tty` instead.

That second behaviour is the useful one: a script can run `tty` to find out whether it is being run interactively or as part of a pipeline, and behave accordingly.

## Options

| Option | Effect |
|---|---|
| `-s`, `--silent` | Print nothing; report the answer only through the exit status. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Standard input is a terminal. |
| `1` | Standard input is not a terminal. |
| `2` | A usage error. |

---

# stty

_Peios / Using Peios / Peiosutils / System and processes_

> Display or change terminal settings.

`stty` displays and changes the settings of a terminal — how it handles input, output, and special keys.

```
stty [options] [setting...]
```

```
$ stty
speed 38400 baud; line = 0;
-brkint -imaxbel iutf8
```

Run with no arguments, `stty` prints the settings that differ from the usual defaults. Given `setting` arguments, it changes them.

## Viewing the settings

| Option | Effect |
|---|---|
| `-a`, `--all` | Print *every* setting, in a readable layout. |
| `-g`, `--save` | Print every setting in a single compact line — a form that can be fed straight back to `stty` to restore them. |

The `-g` form is how you save and restore a terminal's state:

```
$ saved=$(stty -g)      # capture the current state
$ stty -echo            # ...change something...
$ stty "$saved"         # restore exactly what was saved
```

## Changing the settings

A `setting` argument turns something on or off, or assigns a value. A few common ones:

| Setting | Effect |
|---|---|
| `echo` / `-echo` | Show / hide typed characters — `-echo` is how a password prompt hides input. |
| `rows N` / `cols N` | Tell the terminal its size. |
| A number | Set the connection speed (baud rate). |

`stty` has a large catalog of settings; `stty -a` shows them all with their current values.

## Choosing the terminal

| Option | Effect |
|---|---|
| `-F`, `--file=DEVICE` | Operate on `DEVICE` instead of the terminal on standard input. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The settings were printed or changed. |
| `1` | A setting was invalid, or the terminal could not be accessed. |

---

# stdbuf

_Peios / Using Peios / Peiosutils / System and processes_

> Run a command with altered buffering on its standard streams.

`stdbuf` runs a command with **altered buffering** on its standard streams.

```
stdbuf [options] command...
```

```
$ slow-producer | stdbuf -oL grep error
```

## What buffering is, and why change it

A program usually does not write each piece of output the instant it is produced — it collects output in a buffer and writes it in batches, which is faster. The cost is *delay*: when a program's output feeds a pipe, that batching can hold lines back for a long time, which is unhelpful when you are watching a pipeline live.

`stdbuf` lets you override that batching for a command, so its output appears sooner.

## Setting the buffering

| Option | Stream |
|---|---|
| `-i`, `--input=MODE` | Standard input. |
| `-o`, `--output=MODE` | Standard output. |
| `-e`, `--error=MODE` | Standard error. |

`MODE` is one of:

| MODE | Buffering |
|---|---|
| `0` | Unbuffered — every write goes out immediately. |
| `L` | Line-buffered — output is flushed at the end of each line. Not valid for input. |
| a size | Fully buffered with a buffer of that many bytes. Accepts suffixes — `K`, `M`, and so on. |

`-oL` — line-buffered output — is the common case: it makes a command in a pipeline emit each line as it is finished.

## A limitation

`stdbuf` adjusts the *default* buffering. A command that manages its own stream buffering will override what `stdbuf` sets, and some commands do not use buffered streams at all — for those, `stdbuf` has no effect.

## Exit status

When `stdbuf` runs a command, it exits with **that command's** status. The exception is a failure in `stdbuf` itself:

| Code | Meaning |
|---|---|
| (command's own) | The command ran; this is its exit status. |
| (a `stdbuf`-level error code) | The command could not be started. |

---

# chroot

_Peios / Using Peios / Peiosutils / System and processes_

> Run a command with a different directory as its root.

`chroot` runs a command with a **different directory as its root** — its `/`. Inside that command, the chosen directory *is* the top of the file system, and nothing above it can be named or reached.

```
chroot newroot [command [arg]...]
```

```
$ chroot /mnt/system /bin/sh
```

With a `command`, `chroot` changes the root to `newroot` and runs the command there. With no command, it starts an interactive shell.

## What it does

A process normally sees the whole file system, rooted at the real `/`. After `chroot`, the named directory becomes that process's `/`: a path like `/etc/config` inside the command resolves to `newroot/etc/config`, and there is no path that reaches outside `newroot` at all.

It is used to work inside another installed system — repairing or configuring a system image by entering it — and to run a command confined to a known subtree.

For the command to be usable, `newroot` must already contain what the command needs: the command's own executable, and whatever libraries and files it depends on, all present *under* `newroot`.

## Options

| Option | Effect |
|---|---|
| `--skip-chdir` | Do not change the working directory to `/` after changing the root. Permitted only when `newroot` is the current `/`. |

## A privileged operation

Changing a process's root directory is a **privileged** operation. `chroot` succeeds only for a caller whose token carries the right to do it, and is refused otherwise — the ability to redraw a process's view of the file system is not something every principal holds. See [Privileges](/peios/security-fundamentals/privileges/overview.md).

## Exit status

`chroot` exits with the command's own status once it finishes. Otherwise:

| Code | Meaning |
|---|---|
| `125` | `chroot` itself failed — for example, `newroot` is not a directory. |
| `126` | The command was found but could not be run. |
| `127` | The command was not found. |

---

# whoami

_Peios / Using Peios / Peiosutils / System and processes_

> Print the name of the user a process is running as.

`whoami` prints the name of the user the calling process is running as.

```
whoami
```

```
$ whoami
jack
```

It takes no arguments. It is the same answer as `id -un`, which is all it has ever been.

## Where the name comes from

The name is resolved from the process's effective user ID, which is a projection of the [token's](/peios/security-fundamentals/tokens/overview.md) user SID. The lookup goes to the authority — there is no `/etc/passwd` to read — and comes back as the principal's canonical name. [Resolving names](/peios/security-fundamentals/managing-local-principals/resolving-names.md) is the full picture.

The name is the principal's; the *number* it was looked up by is not what grants anything. If you want the identity access is actually decided against, `id -Z` prints the SID.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The name was printed. |
| `1` | The user ID could not be resolved to a name. |

A failure here usually means the authority could not be reached rather than that the account is missing — identity does not resolve before the authority is running, which is the correct answer rather than a gap.

---

# id

_Peios / Using Peios / Peiosutils / System and processes_

> Print the user and group identity of a process, or of a named user.

`id` prints who a process is: its user, its primary group, the groups it belongs to, and its security context.

```
id [options] [USER]...
```

```
$ id
uid=5001000(jack) gid=5000513(Users) groups=5000513(Users),100(Everyone),101(Authenticated Users),1544(Administrators) context=S-1-5-21-1004336348-1177238915-682003330-1000:High
```

With no argument it reports the calling process. Given one or more users, it reports on those instead.

## The numbers are projections

This is the thing to understand before reading `id`'s output on Peios.

The `uid=` and `gid=` numbers are **projections** of the token's [SIDs](/peios/security-fundamentals/identity/sids.md). They are real, stable, and exactly what Linux programs see and what filesystems store — but **nothing is decided by them**. Access is decided against the SID the number was projected from, by the [access check](/peios/security-fundamentals/access-decisions/overview.md). A number here grants nothing.

That distinction is not academic, because the projection cannot carry everything a token holds:

- Groups whose SIDs are deliberately unnumbered — `Interactive`, `Network`, `Batch`, `Service` — **cannot appear at all**. They are what make a rule like "network logons cap at Low" expressible, and they have no gid by design.
- A group's **attributes** have nowhere to go. A deny-only group, which can block access through a deny entry but grant nothing through an allow entry, appears in `groups=` looking exactly like an ordinary membership.
- The **integrity level** and the **logon session** have no number either.

So two tokens can project to identical numbers and mean different things. The clearest case is a [filtered token](/peios/security-fundamentals/tokens/restricted-tokens.md): it carries the same user SID and the same group numbers as the token it came from, and differs only in attributes the projection cannot express.

That is why the default line ends with a **`context=`** field.

## The context field

`context=` is the authoritative half: the user SID access is actually decided against, and the integrity level.

```
context=S-1-5-21-1004336348-1177238915-682003330-1000:High
```

The integrity level is part of it precisely because the SID alone does not separate an elevated token from its filtered counterpart — both carry the same user SID, and the level is what differs.

If you know `id` from another Unix, this is the field that carries the SELinux context there. Peios has no SELinux; its security context is the [token](/peios/security-fundamentals/tokens/overview.md), so that is what the field holds.

`context=` is omitted when a user is named rather than the calling process — a named user has no token here to read — and when `POSIXLY_CORRECT` is set, so a caller that asked for the POSIX shape still gets it.

For the whole token rather than this summary, use [`token`](/peios/security-fundamentals/tokens/token-command.md).

## Options

| Option | Effect |
|---|---|
| `-u`, `--user` | Print only the effective user ID. |
| `-g`, `--group` | Print only the effective group ID. |
| `-G`, `--groups` | Print only the group IDs, space-separated. |
| `-n`, `--name` | With `-u`, `-g` or `-G`, print names instead of numbers. |
| `-r`, `--real` | With `-u`, `-g` or `-G`, print the real ID rather than the effective one. |
| `-z`, `--zero` | Separate entries with NUL rather than whitespace. Not allowed in the default format. |
| `-Z`, `--context` | Print only the security context. |
| `-p` | A readable, multi-line form. |
| `-P` | Print as a password-file entry. |
| `-a` | Ignored; accepted for compatibility. |
| `-A` | Not applicable on Peios — see below. |

`-n` and `-r` need one of `-u`, `-g` or `-G`; they do not change the default format.

### `-r` means something different here

On other Unixes the real/effective split is the setuid boundary. On Peios a setuid bit does not change a token, so that boundary does not exist. What the split tracks instead is [impersonation](/peios/security-fundamentals/impersonation/overview.md): the real IDs come from the process's own token, the effective ones from whatever token is in force.

`id` never impersonates, so in practice it reports the same either way and the `euid=`/`egid=` fields never appear. That is not a fault — there is genuinely nothing differing to report.

### `-A` is not applicable

`-A` reports BSD audit session properties. Peios audits through [its own event system](/peios/security-fundamentals/auditing/overview.md) and keeps no BSM session, so `-A` says so rather than silently succeeding at nothing.

## `id -G` and `id -G <yourself>` differ

Asking about yourself and asking about your *name* are two different questions here, and they give different answers:

- **`id -G`** reads your process's credential — the projection of your **token's** group set, which includes the groups the authority added when the token was minted: `Everyone`, `Authenticated Users`, and any `Administrators`-style aliases.
- **`id -G jack`** looks the name up through the [principal store](/peios/security-fundamentals/managing-local-principals/resolving-names.md), which returns **recorded** memberships only. The stapled groups are not recorded anywhere, because membership in them is a rule rather than a stored fact — so they do not come back.

On other systems these two differ only in the order they print. Here the second list is genuinely shorter, and neither is wrong.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The identity was printed. |
| `1` | A user could not be found, an option combination was rejected, or the context could not be read. |

---

# groups

_Peios / Using Peios / Peiosutils / System and processes_

> Print the groups a user belongs to.

`groups` prints the groups a user belongs to.

```
groups [USERNAME]...
```

```
$ groups
Users Everyone Authenticated Users Administrators

$ groups jack
jack : Users Administrators
```

With no argument it reports the calling process. Given names, it reports on those.

## Those two answers are both correct

The example above is not a mistake, and it is the thing worth knowing about this command on Peios.

**`groups`** reads the calling process's credential — the projection of its [token's](/peios/security-fundamentals/tokens/overview.md) group set. That set includes the groups the authority *stapled on* when the token was minted: `Everyone`, `Authenticated Users`, and the like.

**`groups jack`** looks the name up through the [principal store](/peios/security-fundamentals/managing-local-principals/resolving-names.md), which returns **recorded** memberships — the ones something actually wrote down.

The stapled groups are not recorded anywhere, because membership in them is a **rule**, not a stored fact. Everyone is in `Everyone`; nothing needs to record it, and nothing can enumerate it. So they appear in the first answer and not the second.

On other Unixes these two forms differ only in the order they print.

## What cannot appear

Both forms are lossy in the same way, and unavoidably so — a group can only be listed if it has a group ID, and some deliberately do not:

- `Interactive`, `Network`, `Batch` and `Service` are **unnumbered on purpose**. They describe *how* you signed in rather than who you are, which is what lets a rule like "network logons cap at Low" be written at all. You are in `Interactive` at the console and not over the network, so there is no static answer to record.
- A group's **attributes** are not representable. A deny-only group — one that can block access through a deny entry but grant nothing through an allow entry — is printed exactly like an ordinary membership.

[`token groups`](/peios/security-fundamentals/tokens/token-command.md) is the lossless view, with every SID and its attributes.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The groups were printed. |
| `1` | A named user could not be found, or the group list could not be read. |

A group ID with no name is printed as the bare number and sets the exit status, rather than stopping the listing.

---

# logname

_Peios / Using Peios / Peiosutils / System and processes_

> Print the login name of the user a process is running as.

`logname` prints the login name the calling process runs under.

```
logname
```

```
$ logname
jack
```

It takes no arguments.

## Login name, not current name

`logname` and [`whoami`](/peios/using-peios/peiosutils/system-and-processes/whoami.md) look like the same command and answer slightly different questions.

`whoami` reports the **effective** identity — whoever the process is acting as right now. `logname` reports the **login** identity: the principal the process's own token is for, regardless of any token it has since taken on. When a process is [impersonating](/peios/security-fundamentals/impersonation/overview.md) a client, `whoami` follows the impersonation and `logname` does not.

Most of the time nothing is impersonating and the two agree.

If you know `logname` from another Unix, note that it answers this from the token rather than from a login-record file. There is no `utmp` on Peios — nothing writes one — so the traditional source does not exist. The token is the record of who a process is, and it is a better one: it cannot drift from the identity access is actually checked against, because it *is* that identity.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The login name was printed. |
| `1` | No login name could be determined. |

---

# token

_Peios / Using Peios / Peiosutils / System and processes_

> The token command inspects and manipulates tokens — reading a token's contents, adjusting it, duplicating and restricting it, and driving impersonation.

`token` is the command-line tool for working with **tokens** directly. It reads a token's contents, adjusts it, produces derived tokens, and drives impersonation — the low-level operations this topic describes, exposed at a shell.

```
token subcommand [target] [arguments]
```

```
$ token                       # one-line summary of your own token
$ token show --all             # every field of your own token
$ token privs --pid 4821       # the privileges on process 4821's token
```

`token` is a direct, debug-level tool. Day to day you do not inspect tokens by hand — the system does. `token` is for diagnosing an access problem, for understanding what identity a process is really running under, and for building and testing identity setups. Run with no subcommand, it prints a one-line summary of your own token.

## Choosing which token

Almost every subcommand operates on a token, and these flags choose which one. With none, the target is your own.

| Flag | Target |
|---|---|
| `--self` | Your own token. The default. |
| `--real` | Your **primary** token specifically, rather than the effective one — relevant when your thread is impersonating. |
| `--pid PID` | The primary token of process `PID`. |
| `--tid TID` | The impersonation token of thread `TID` (used with `--pid`). |
| `--peer SOCK_FD` | The peer's captured token on a connected socket — see [Peer tokens](/peios/security-fundamentals/impersonation/peer-tokens.md). |

Reading another process's token is itself access-controlled: it succeeds only with the right authority over that process.

## Inspecting a token

### `show`

`token show` prints a token's contents. It is the default — bare `token` is `token show --short`.

| Flag | Effect |
|---|---|
| `--short` | A one-line summary. |
| `--all` | Every query class — the fullest dump. |

### Field accessors

Each of these prints one part of a token, for when you want just that piece:

| Subcommand | Prints |
|---|---|
| `user` | The user SID — who the token is. |
| `owner` | The default owner SID. |
| `group` | The primary group SID. |
| `groups` | The group list. |
| `privs` | The privileges, with their enabled state. |
| `caps` | The capabilities. |
| `claims` | The user and device claims. |
| `integrity` | The integrity level. |
| `logon` | The logon type and logon SID. |
| `source` | What minted the token. |
| `origin` | The originating session for a derived token. |
| `stats` | Token statistics — IDs, timestamps, the modification counter. |
| `default-dacl` | The token's default DACL. |

### `query`

`token query CLASS` performs a raw read of a single named token-info class and prints the result as JSON — the lowest-level inspection route, for tooling.

## Changing a token

### `adjust`

`token adjust` mutates a token in place:

| Form | Changes |
|---|---|
| `adjust privs NAME=STATE …` | Enable, disable, or remove privileges. `STATE` is `enabled`, `disabled`, or `removed`. |
| `adjust groups IDX=STATE …` | Enable or disable groups by their list index. |
| `adjust default --dacl SDDL` | Replace the token's default DACL. Also `--owner-idx` / `--group-idx`. |
| `adjust session ID` | Replace the token's session id. |

### `restrict`

`token restrict` produces a [restricted token](/peios/security-fundamentals/tokens/restricted-tokens.md) — a more limited variant of a token.

| Flag | Effect |
|---|---|
| `--drop-privs MASK\|NAMES` | Privileges to drop. |
| `--deny IDX,…` | Group indices to mark deny-only. |
| `--restrict SID,…` | The restricting SIDs to apply. |

### `duplicate`

`token duplicate` (alias `dup`) copies a token, optionally changing its `--type` (primary or impersonation), its impersonation `--level`, or its `--access` mask.

### `link` and `linked`

`token link` joins two tokens as an elevation pair — a full token and its filtered counterpart — given their file descriptors and a session id. `token linked` shows a token's elevation-linked counterpart, if it has one. See [Elevation](/peios/security-fundamentals/tokens/elevation.md).

## Impersonation

| Subcommand | Effect |
|---|---|
| `impersonate` | Begin impersonating the target token on the calling thread. With a trailing `-- command …`, run that command under the impersonating token. |
| `revert` | Drop any active impersonation on the calling thread. |

See [Impersonation](/peios/security-fundamentals/impersonation/overview.md) for the model these drive.

## Creating tokens

| Subcommand | Effect |
|---|---|
| `create SPEC` | Create a token from a binary token-spec (`SPEC` is a file, or `-` for standard input). |
| `install SPEC` | Create a token from a spec and install it as the caller's primary token. |

Creating and installing tokens is a privileged operation, reserved for the components that legitimately mint identity.

## Output options

| Flag | Effect |
|---|---|
| `--raw` | Render SIDs in raw `S-1-…` form only. |
| `--label` | Render SIDs as their labels where known, falling back to raw. |
| `--json` | Emit JSON instead of human-readable output. |

## `token` and the inspection surfaces

`token` is the convenient front-end. Underneath, it reads the same kernel surfaces and rules described in [Inspecting tokens](/peios/security-fundamentals/inspecting/tokens.md) — that page covers the query mechanism, the access rules for reading another process's token, and what cannot be inspected.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The operation succeeded. |
| `1` | A usage error. |
| non-zero | The operation failed — no such target, an access denial, or a bad spec. |

---

# logonse

_Peios / Using Peios / Peiosutils / System and processes_

> The logonse command lists logon sessions and their processes, creates and destroys them, and sets a process's mitigation flags.

`logonse` is the command-line tool for **logon sessions** — the kernel's records of authentication events that this topic describes. It lists the active sessions, shows which processes belong to one, creates and destroys sessions, and (as a related low-level job) sets a process's mitigation flags.

```
logonse subcommand [arguments]
```

```
$ logonse list
$ logonse show 4711
```

`logonse` is a low-level administrative and debugging tool. It requires a subcommand: `list`, `show`, `create`, `destroy`, or `psb`.

## Listing sessions

### `logonse list`

Enumerates the active logon sessions, and the process IDs in each.

```
$ logonse list
session 0     pids: [1, 2, 14, 22]
session 4711  pids: [820, 844, 901]
```

### `logonse show`

Shows the process IDs that belong to one session.

```
$ logonse show 4711
```

### A caveat on list and show

There is no syscall that enumerates logon sessions, and `logonse` does not read the kernel's sessions file (whose SD restricts it to Administrators and SYSTEM). `logonse list` and `logonse show` work by walking the running processes and reading each one's token to find which session it belongs to. That has two consequences worth knowing:

- It is **best-effort**. A session that has no running process — held alive only by a token file descriptor somewhere — will not appear, because there is no process to find it through.
- It is a **snapshot under change**. Processes start and exit while the walk runs, so the result is a close approximation of the moment, not a locked one.

For routine "who is signed in" use this is fine. For an authoritative listing, the kernel's own sessions surface — described in [Inspecting sessions](/peios/security-fundamentals/inspecting/sessions.md) — is the source of record.

## Creating and destroying sessions

### `logonse create`

Creates a new logon session for a user, described by a logon type, an authentication-package name, and the user's SID.

```
$ logonse create --logon-type interactive --auth-package Negotiate --user-sid S-1-5-21-...-1001
```

| Flag | Meaning |
|---|---|
| `--logon-type TYPE` | The kind of logon: `interactive`, `network`, `batch`, `service`, `network-cleartext`, or `new-credentials`. |
| `--auth-package STR` | The name of the authentication package that vouched for the logon. |
| `--user-sid SID` | The user the session belongs to, as an `S-1-…` SID or an SDDL alias such as `BA`. |

On success `logonse` prints the new session's id. Creating a session is a **privileged** operation — minting authentication records is reserved for the components that legitimately do so.

> [!NOTE]
> The tool builds the kernel's binary session spec from these fields for you. The underlying [wire format](/peios/using-peios/wire-formats-reference/token-and-session-specs.md) is unchanged; only the command-line surface is typed.

### `logonse destroy`

Destroys a session — but only an **empty** one, with no tokens still referencing it.

```
$ logonse destroy 4711
```

A session with live tokens cannot be destroyed this way; its tokens must go first. See [Session lifecycle](/peios/security-fundamentals/logon-sessions/lifecycle.md).

## Setting process mitigation flags

### `logonse psb`

`logonse psb` sets the **mitigation flags** in a process's Process Security Block.

```
$ logonse psb --pid 4821 --mitigations 0x1c0
```

| Flag | Meaning |
|---|---|
| `--pid PID` | The process to act on. |
| `--mitigations MASK` | The mitigation bitmask to apply, in hexadecimal or decimal. |

This subcommand is about process hardening rather than logon sessions — it lives in `logonse` because both deal with low-level per-process kernel state. For what the mitigation flags mean and how they behave, see [Process mitigations](/peios/security-fundamentals/process-mitigations/overview.md).

## Output options

| Flag | Effect |
|---|---|
| `--json` | Emit JSON instead of human-readable output. Accepted by every subcommand. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The operation succeeded. |
| `1` | A usage error, or the operation failed. |

---

# mount

_Peios / Using Peios / Peiosutils / Disks and mounts_

> Attach a filesystem to the Peios mount tree — new mounts, binds, moves, remounts and propagation changes — and apply a KACS mount policy at mount time.

`mount` attaches a filesystem to the Peios mount tree. With no operands it instead *lists* what is currently mounted. It is a faithful reworking of the util-linux `mount(8)` surface for Peios, with two structural differences: Peios has **no `/etc/fstab` and no `/etc/mtab`**, so every mount is described entirely on the command line and live mount state is read from the kernel; and a `mount` can apply a **KACS mount policy** to the new filesystem at attach time (see [Mount policies](/peios/using-peios/mount-policies/overview.md)).

```
mount [-t TYPE] [-o OPTIONS] SOURCE TARGET
mount [-o remount,OPTIONS] TARGET
mount --bind|--rbind|--move SOURCE TARGET
mount --make-shared|--make-private|... TARGET
mount [-l] [-t TYPE]
```

Internally `mount` uses the fd-based mount API (`fsopen` / `fsconfig` / `fsmount` / `move_mount`, plus `open_tree` and `mount_setattr`), never the classic single-shot `mount(2)`. This matters in one place you can see: when a mount fails, the kernel's `fs_context` message log is drained and printed as the reason, even without `-v`.

## Operands and argument shapes

Because there is no fstab to consult, operand resolution is strict:

- **No operands, no verb** — list mode (see [below](#list-mode)).
- **Two operands** — `SOURCE TARGET`.
- **One operand** — an error. In util-linux a lone operand is resolved through fstab; Peios has none, so a single operand cannot be turned into a `SOURCE TARGET` pair. Supply both, or use `--source` / `--target`.
- **A lone target** is valid only for `-o remount` and for a standalone propagation change (`--make-*`), which act on an existing mount point.

`--source SRC` and `--target DIR` name the operands explicitly and may be combined with a single positional. `--target-prefix DIR` prepends `DIR/` to the target after it is chosen. Options may be interspersed with operands (`mount SRC -o ro TGT`), matching util-linux.

Paths, `-o key=value` values, labels and UUIDs are handled as **opaque byte strings**, never assumed to be UTF-8; an embedded NUL is a usage error.

### Canonicalisation

By default source and target paths are canonicalised (made absolute, symlinks resolved). `-c` / `--no-canonicalize` disables that; `X-mount.nocanonicalize[=source|target]` is the `-o` form and can disable it for just one side. The final path handed to the kernel is protected against a TOCTOU symlink swap on its last component.

## Operation modes (verbs)

`mount` performs one of several operations. The **structural verbs** — bind, rbind, move, beneath and remount — are mutually exclusive with one another. A **propagation** change is *not* a structural verb: it may stand alone on a target, or trail another verb or a new mount in the same command (applied afterwards as one or more `mount_setattr` steps), and several may be combined.

| Verb | How to request it | What it does |
|---|---|---|
| **New mount** | `mount [-t T] SRC TGT` | Attach a fresh instance of the filesystem at `SRC` onto `TGT`. |
| **Bind** | `-B` / `--bind`, or `-o bind` | Make an existing subtree visible at a second location. |
| **Recursive bind** | `-R` / `--rbind`, or `-o rbind` | Bind a subtree together with every mount underneath it. |
| **Move** | `-M` / `--move`, or `-o move` | Relocate an existing mount to a new mount point. |
| **Move beneath** | `--beneath SRC TGT` | Attach `SRC` *beneath* the mount currently at `TGT` (the kernel enforces several constraints; violations surface as exit 32). |
| **Remount** | `-o remount[,...] TGT` | Change the options of an existing mount (see [Remounting](#remounting)). |
| **Propagation** | `--make-*` / `--make-r*`, or the `-o` tokens below | Change how mount/unmount events propagate across a subtree. |
| **List** | `mount` / `mount -l` | Print the current mounts. |

### Propagation flags

Each of these sets the propagation type of the mount at the target. The `--make-r*` (and `r`-prefixed `-o`) forms apply recursively to the whole subtree; the recursive form is all-or-nothing (it either applies to the entire subtree or to none of it).

| Flag | `-o` token | Recursive flag | Recursive `-o` token | Meaning |
|---|---|---|---|---|
| `--make-shared` | `shared` | `--make-rshared` | `rshared` | Events propagate to and from peer mounts. |
| `--make-slave` | `slave` | `--make-rslave` | `rslave` | Events propagate *in* from the master but not back out. |
| `--make-private` | `private` | `--make-rprivate` | `rprivate` | No propagation either way. |
| `--make-unbindable` | `unbindable` | `--make-runbindable` | `runbindable` | Private, and cannot be bind-mounted. |

A freshly created mount, bind or move is **private** by default unless its destination's parent is shared, in which case it joins that peer group — the standard kernel rule.

## Source specification

| Form | Resolution |
|---|---|
| Device path (`/dev/sda1`) | Used directly. |
| `-L LABEL` / `LABEL=`, `-U UUID` / `UUID=`, `PARTLABEL=`, `PARTUUID=` | Resolved to a device via libblkid. No match is a mount failure (exit 32). |
| A directory or regular file | Used directly (a bind source or target; a file may be a bind target). |
| A pseudo source (`tmpfs`, `proc`, `sysfs`, `none`, …) | Passed through; `-t` is required because it cannot be probed. |
| An image file | Attached through a loop device (see [Loop devices](#loop-devices)). |

## Filesystem type

`-t TYPE` names the type explicitly. `-t auto`, or omitting `-t` entirely, asks libblkid to **safely probe** the source; an ambiguous or multiply-signed device is refused (exit 32) rather than guessed at. `X-mount.auto-fstypes=LIST` constrains the probe candidates. Type *lists* and `no<type>` negation are not accepted in the mounting path (they remain meaningful only as a [listing filter](#list-mode)).

## The `-o` option language

`-o` takes a comma-separated list of `key` or `key=value` tokens and is repeatable (all occurrences are joined). A `key="..."` double-quoted value protects embedded commas; the `key=value` split is on the **first** `=`. Every token is sorted into one of the following categories.

### Per-mount attributes

Applied to the mount itself. Each accepts its negation; `ro`/`rw` additionally accept a `=vfs` / `=fs` / `=recursive` scope qualifier (bare is `=vfs`, non-recursive; `=fs` targets the superblock read-only flag; `=recursive` applies to the whole subtree).

| Option | Effect |
|---|---|
| `ro` / `rw` | Read-only / read-write. |
| `suid` / `nosuid` | Honour / ignore set-user-ID bits. |
| `dev` / `nodev` | Allow / disallow device nodes. |
| `exec` / `noexec` | Allow / disallow execution. |
| `atime` / `noatime` | Update / never update access times. |
| `relatime` / `norelatime` | Relative-atime updates on or off. |
| `strictatime` / `nostrictatime` | Strict-atime updates on or off. |
| `diratime` / `nodiratime` | Directory access-time updates on or off. |
| `nosymfollow` | Do not follow symlinks on this mount. There is no positive `symfollow` token — the attribute is cleared only via a remount mask. |

The atime tokens share a single mode field; the last one specified wins.

### Superblock flags

| Option | Effect |
|---|---|
| `sync` / `async` | Synchronous / asynchronous writes. |
| `dirsync` | Synchronous directory updates. |
| `lazytime` / `nolazytime` | Lazy on-disk timestamp updates on or off. |
| `iversion` / `noiversion` | Inode version counting on or off. |
| `silent` / `loud` | Suppress or emit certain kernel messages. |
| `mand` / `nomand` | Obsolete (removed from the kernel). Accepted and ignored with a note under `-v`. |

### Filesystem-specific parameters

Any token not recognised above is forwarded verbatim to the filesystem: `key=value` as a string parameter, a bare `key` as a flag. There is no built-in allow-list and no length limit; a rejection by the filesystem is reported with the offending key and the kernel's message.

For example, StrataFS takes its precedence-ordered directory stack through
`strata=`:

```sh
mount -t stratafs none /bin -o 'strata=/lcl/bin+create:/usr/bin+ro'
```

See [StrataFS](/peios/using-peios/disks-and-filesystems/stratafs/overview.md) for the stack flags, merge
and write-routing model, and the `stratafs` inspection command.

### Userspace-only tokens

These never reach the filesystem. They include the meta-verbs `remount`, `bind`, `rbind`, `move`; the loop controls `loop` / `loop=/dev/loopN`, `offset=`, `sizelimit=` (numeric values accept `K`/`M`/`G`/`T` and `KiB`/`MiB`/… suffixes); the propagation tokens listed [above](#propagation-flags); and `defaults`, which expands to `rw,suid,dev,exec,async` (later tokens override it).

### Functional `X-mount.*` options

| Option | Effect |
|---|---|
| `X-mount.mkdir[=mode]` | Create the target directory if missing (default mode `0755`; the alias of `-m`). |
| `X-mount.subdir=DIR` | Attach subdirectory `DIR` of a freshly mounted filesystem at the target. Effective only for a new-instance mount; silently ignored (noted under `-v`) for bind/move/remount/propagation. |
| `X-mount.noloop` | Suppress the implicit loop device for a regular-file source. |
| `X-mount.auto-fstypes=LIST` | Constrain the `-t auto` probe to these types. |
| `X-mount.nocanonicalize[=source\|target]` | The `-o` form of `-c`; with `=source` or `=target` it disables canonicalisation for just that path. |

`X-mount.idmap` and `X-mount.owner` / `group` / `mode` are **not supported** and are rejected with a usage error: Peios ownership is SID/security-descriptor based, so the fix is to add a SID/ACE to the descriptor rather than remap or chown.

### KACS mount policy

This is the genuinely Peios-specific part of `mount`. A mount policy is a per-superblock setting that governs how FACS treats the filesystem; the full model is described in [Mount policies](/peios/using-peios/mount-policies/overview.md) and its detail pages. `mount` can set that policy at attach time:

| Option | Policy class |
|---|---|
| `-o policy=deny-missing` | `facs_deny_missing` — a file with no SD is unreachable. |
| `-o policy=synth-ephemeral` | `facs_synthesize_ephemeral` — a missing SD is synthesised in memory only. |
| `-o policy=synth-persist` | `facs_synthesize_persistent` — a missing SD is synthesised and written back. |
| `--synth-sddl SDDL` | Provide the mount-level SD template used during synthesis (only valid with a `synth-*` policy). |

`policy=unmanaged` is **not** user-settable; only the kernel sets the unmanaged class, for its own pseudo-filesystems. `policy=` is valid only on a **new mount** of a real filesystem — combining it with bind/move/remount/propagation, or with list mode, is a usage error. See [Policy classes](/peios/using-peios/mount-policies/policy-classes.md) for what each class does and [SD storage by filesystem](/peios/using-peios/mount-policies/sd-storage-by-filesystem.md) for how the SD is physically stored.

The policy is applied to the *detached* filesystem before it is attached: if setting it fails, nothing is ever published with an unintended policy (no rollback needed). Because setting a mount policy is a `SeTcbPrivilege`-gated operation (see [Managing mounts](/peios/using-peios/mount-policies/managing-mounts.md)), a caller without that privilege gets a clean `EPERM` (exit 1) and no mount. `--synth-sddl` is validated client-side first — it must be well-formed SDDL and must include an owner.

Peios applies no coarse `uid==0` check anywhere: the `mount` applet is not installed set-user-ID, and every privileged action is authorised per-operation by KACS.

### Remounting

`-o remount` changes the options of an existing mount without detaching it. Peios does **not** perform the util-linux "read the old flags and re-supply them" dance — the fd-based API applies deltas rather than resetting, so each remount touches only what you name. Per-mount attributes (`ro`/`rw`, `nosuid`, atime, …) are changed via `mount_setattr`; superblock flags, filesystem parameters and `ro=fs`/`rw=fs` go through the superblock reconfigure path. `=recursive` remounts the whole subtree, all-or-nothing.

A **bind** mount shares its source's superblock, so any superblock-level option on a bind remount (a Category-B flag, a filesystem parameter, or `ro=fs`/`rw=fs`) is refused as a usage error rather than silently mutating the shared superblock for every mount of that filesystem. Only per-mount VFS attributes are valid on a bind remount.

## Loop devices

A regular-file source is backed by a loop device automatically **when the type is unspecified or the filesystem is recognised by libblkid**; `X-mount.noloop` suppresses this. `-o loop` forces auto-allocation of a free device, `loop=/dev/loopN` names one, and `offset=` / `sizelimit=` select a region of the file (they imply loop and are an error against a real block device). A backing file already attached at the same offset and size is *reused* rather than doubly attached, to avoid corruption. Loops created by `mount` are auto-cleared by the kernel on unmount, so they do not leak; `umount -d` force-clears the rest (see [`umount`](/peios/using-peios/mount-policies/umount.md)).

## Other flags

| Flag | Effect |
|---|---|
| `-t`, `--types TYPE` | Filesystem type, or `auto`. |
| `-o`, `--options LIST` | Mount options (above); repeatable. |
| `-r`, `--ro`, `--read-only` | Mount read-only (`-o ro`). |
| `-w`, `--rw`, `--read-write` | Mount read-write and **forbid** the automatic read-only fallback on a write-protected device (`-o rw`). |
| `--source SRC` / `--target DIR` | Name the operands explicitly. |
| `--target-prefix DIR` | Prepend `DIR/` to the target. |
| `-B`, `--bind` / `-R`, `--rbind` / `-M`, `--move` / `--beneath` | The structural verbs. |
| `--make-*`, `--make-r*` | Propagation changes. |
| `--exclusive` | Force a unique superblock instance (no reuse). Meaningful only for multi-instance filesystems (e.g. `tmpfs`) or read-only block mounts; on an already-mounted writable block device it fails with `EBUSY` (exit 32). |
| `-m`, `--mkdir[=MODE]` | Create the target directory if missing (default mode `0755`). |
| `-L`, `--label LABEL` / `-U`, `--uuid UUID` | Select the source by filesystem label / UUID. |
| `-c`, `--no-canonicalize` | Do not canonicalise paths. |
| `-f`, `--fake` | Dry run: parse, resolve and plan everything but skip the mount syscalls and the policy step. |
| `-v`, `--verbose` | Narrate resolved values and each syscall; drain the kernel `fs_context` log. Repeatable but `-vv` is the same as `-v`. |
| `-l`, `--show-labels` | In list mode, append each filesystem's label. |
| `--onlyonce` | Skip the mount if it is already present (a driver-aware check against live mount state, not a naive string match). |
| `-N`, `--namespace NS` | Operate inside mount namespace `NS` (a PID, an ns file path, or a named namespace). Source resolution happens in the caller's namespace; the mount lands in the target namespace. |
| `-i`, `--internal-only` | Do not invoke a `mount.<type>` helper. |
| `-n`, `--no-mtab` | Accepted and ignored (Peios has no mtab). |
| `--synth-sddl SDDL` | KACS synth-policy template SD (above). |
| `-h`, `--help` / `-V`, `--version` | Standard. |

No external mount helpers ship in this version, so a network or FUSE type fails with "no helper for type" (exit 1) — a clean deferral, not a crash.

## List mode

With no operands, `mount` prints one line per mount, read from `/proc/self/mountinfo`:

```
SOURCE on TARGET type FSTYPE (OPTIONS)
```

`mount -t TYPE` with no operands *filters* the list by filesystem type instead of mounting; here type lists (`-t ext4,xfs`) and `no<type>` negation (`-t nosysfs`) are honoured. `-l` appends ` [LABEL]` to each line when a label is known.

Details of the rendering: the option field is the positional VFS-then-superblock merge with a coalesced `ro`/`rw`, not sorted; mountinfo octal escapes are decoded; control characters in the mount point become `?`; and the source is shown as the kernel recorded it, with `/dev/loopN` resolved to its backing file and `/dev/dm-N` to `/dev/mapper/<name>`. The listing shows only mounts the caller's token may observe; a partial or empty view is correct, not an error, and the paths of filtered-out parents are never reconstructed.

For a device-oriented view of what is available to mount, use [`lsblk`](/peios/using-peios/mount-policies/lsblk.md).

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | Incorrect invocation or a permission denial — bad flags, mutually-exclusive verbs, a lone operand, an invalid `policy=` or `--synth-sddl`, an authorisation failure (`EPERM`/`EACCES`), an embedded NUL, or "no helper for type". |
| `2` | System error (out of memory, no free loop device, cannot fork). |
| `4` | Internal error (an invariant failure). |
| `8` | Interrupted (SIGINT), after signal-safe cleanup. |
| `32` | Mount failure — a syscall in the flow failed, a label/UUID had no match, a probe was undetermined, or a `--beneath`/move/`--exclusive` kernel refusal. |
| `126` | An external `mount.<type>` helper was found but failed to execute (moot until a helper ships). |

Code `16` (mtab) is never produced. Code `64` (some succeeded, some failed) only arises when several source arguments are given and at least one succeeds while another fails.

---

# umount

_Peios / Using Peios / Peiosutils / Disks and mounts_

> Detach a filesystem from the Peios mount tree by mount point or by source, with lazy, forced, recursive and all-targets variants.

`umount` detaches a filesystem from the Peios mount tree. It is the counterpart of [`mount`](/peios/using-peios/mount-policies/mount.md) and, like it, reads live mount state from the kernel (`/proc/self/mountinfo`) rather than any `/etc/mtab` — Peios has none. Each operand is resolved against that live state and unmounted with `umount2(2)`.

```
umount [-lfRA] [-dr] TARGET|SOURCE...
```

## Operands and how they are resolved

Each operand is either a **mount point** or a **source**:

- If the (canonicalised) operand is itself a mount point in the current mount table, it is unmounted directly. This is always unambiguous.
- Otherwise the operand is treated as a **source** and matched against the sources in the mount table. If it is mounted in exactly one place, that place is unmounted. If it is mounted in **several** places, `umount` refuses with an error naming the candidates — resolve it by naming a specific mount point, or use `-A` to unmount all of them.

If an operand matches nothing, it is "not mounted": normally an error (exit 32), but `-g` makes that a success and `-q` suppresses the message.

Several operands may be given in one invocation, and options may be interspersed with them (`umount /a -f /b`). Paths are handled as opaque bytes; an embedded NUL is a usage error. By default each operand is canonicalised; `-c` disables that and additionally selects `UMOUNT_NOFOLLOW` so the kernel does not follow a symlink in the final component.

## Recursive and all-targets unmounts

Two flags expand a single operand into multiple unmounts. Within one operand the expansion **stops on the first failure**.

- **`-R` / `--recursive`** unmounts the target and everything mounted underneath it, including any over-mount stack at a single point, ordered deepest-first.
- **`-A` / `--all-targets`** unmounts *every* mount point of the given source in the current mount namespace. This is the live-mountinfo "unmount everywhere" complement to source resolution; it is not an fstab feature.
- **`-A -R` together** compose: for each mount point of the source, recurse underneath it.

`-R` and `-r` are mutually exclusive (combining them is a usage error, exit 1).

## Flags

| Flag | Effect |
|---|---|
| `-l`, `--lazy` | Detach the filesystem now and clean up references later (`MNT_DETACH`). |
| `-f`, `--force` | Force the unmount (`MNT_FORCE`), e.g. for an unreachable server. |
| `-R`, `--recursive` | Unmount the target and everything under it (see above). |
| `-A`, `--all-targets` | Unmount every mount point of the given source (see above). |
| `-d`, `--detach-loop` | After unmounting, free the backing loop device. Best-effort and verified: it clears the device only if it still backs the mount just removed, so a recycled loop number is never clobbered. Usually redundant, since `mount`-created loops auto-clear on unmount. |
| `-r`, `--read-only` | If the unmount fails, remount the filesystem read-only instead (via `mount_setattr`). Mutually exclusive with `-R`. |
| `-g`, `--graceful` | Exit `0` when the target is absent or not mounted (rather than failing). Applied unconditionally. |
| `-q`, `--quiet` | Suppress "not mounted" messages. |
| `-c`, `--no-canonicalize` | Do not canonicalise paths; selects `UMOUNT_NOFOLLOW`. Applied unconditionally. |
| `-v`, `--verbose` | Say what is being unmounted. Repeatable. |
| `-N`, `--namespace NS` | Enter mount namespace `NS` (a PID, an ns file path, or a named namespace) before reading its mount table and unmounting. All work happens inside that namespace. |
| `-n`, `--no-mtab` | Accepted and ignored (no mtab on Peios). |
| `-i`, `--internal-only` | Do not invoke a `umount.<type>` helper (none ship). |
| `--fake` | Dry run: resolve operands and report, but skip the unmount syscalls. |
| `-h`, `--help` / `-V`, `--version` | Standard. |

The `umount2` flag mapping is direct: `-l` → `MNT_DETACH`, `-f` → `MNT_FORCE`, `-c` (or a symlinked final component) → `UMOUNT_NOFOLLOW`. `MNT_EXPIRE` is deliberately not exposed, matching util-linux.

### On privilege

As with `mount`, there is no coarse `uid==0` check: the applet is not set-user-ID and every unmount is authorised per-operation by KACS. Where util-linux silently suppresses an option for non-root users (`-c`, and `-g`'s effectiveness), Peios applies it unconditionally.

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success (or a `-g` no-op on an absent target). |
| `1` | Incorrect invocation or a permission denial — including `-R` with `-r`, a missing operand, an ambiguous source, an embedded NUL, or an authorisation failure. |
| `2` | System error (e.g. cannot read the mount table). |
| `8` | Interrupted (SIGINT). |
| `32` | Unmount failed, or the target is not mounted (unless `-g`). |
| `64` | Several source/target arguments were given and at least one succeeded while another failed. |
| `126` | An external `umount.<type>` helper was found but failed to execute (moot until a helper ships). |

A single `-R` / `-A` / `-A -R` invocation stops on the first failure and yields `32`; the aggregate `64` only appears across multiple top-level arguments. To see what is currently mounted before unmounting, use [`mount`](/peios/using-peios/mount-policies/mount.md) with no arguments or [`lsblk`](/peios/using-peios/mount-policies/lsblk.md).

---

# lsblk

_Peios / Using Peios / Peiosutils / Disks and mounts_

> List block devices as a tree, flat list, JSON or key=value pairs — with filesystem identity, topology, and SD-derived owner and mode columns.

`lsblk` lists the block devices on the system and their relationships — disks, their partitions, and the device-mapper and loop devices layered on top. It is the device-oriented companion to [`mount`](/peios/using-peios/mount-policies/mount.md): where `mount` with no arguments shows what is *mounted*, `lsblk` shows what is *available to mount* and what filesystem sits on each device.

```
lsblk [options] [DEVICE...]
```

By default it prints a tree, one row per device with children indented beneath their parent. Given one or more `DEVICE` operands (a name like `sda`, a full path like `/dev/sda`, or anything resolving to a device node), it re-roots the output at those devices.

## Where the data comes from

Each column is sourced from one of four places, and any that cannot be read degrades to an empty cell rather than aborting the run:

- **sysfs** (`/sys/block`) — the device tree, sizes, and the topology/hardware columns. Peios leaves `/sys/block` unpatched, so this is the standard no-udev path.
- **libblkid** — filesystem identity: `FSTYPE`, `FSVER`, `UUID`, `LABEL`, and the partition-table columns. libblkid is opened at runtime.
- **The device node's security descriptor** — `OWNER` and `MODE`, read the same way [`ls -l`](/peios/using-peios/peiosutils/listing-and-paths/ls.md) reads them.
- **The `/dev/disk/by-*` symlink farm** — `ID-LINK`. Populated by the device manager once it has run; there is deliberately no `/run/udev/data` parser, so the column reads the links themselves and is empty in the initramfs, where no device manager runs.

## Output modes

The mode selects how the rows are formatted; it does not change which columns are shown.

| Flag | Mode |
|---|---|
| *(default)* | Indented tree. |
| `-l`, `--list` | Flat list — the same columns, no tree glyphs. |
| `-J`, `--json` | JSON. Flag columns render as bare booleans and `MOUNTPOINTS` as an array; children nest under a `children` key. |
| `-P`, `--pairs` | `KEY="value"` pairs, one device per line. |
| `-r`, `--raw` | Raw, space-separated. Values that could contain a space or control character are hex-escaped (`\xNN`) so fields stay parseable. |
| `-T`, `--tree[=COLUMN]` | Force tree output even alongside `-l`, optionally attaching the tree glyphs to `COLUMN` instead of `NAME`. |

## Columns

With no column flag, `lsblk` prints the default set: `NAME`, `MAJ:MIN`, `RM`, `SIZE`, `RO`, `TYPE`, `MOUNTPOINTS`. Three flags swap in a preset, and `-o` names an explicit list (which wins over all of them):

| Flag | Column set |
|---|---|
| `-o`, `--output LIST` | Exactly the comma-separated columns named (case-insensitive; an unknown name is a usage error). |
| `-O`, `--output-all` | Every available column. |
| `-f`, `--fs` | Filesystem view: `NAME`, `FSTYPE`, `FSVER`, `LABEL`, `UUID`, `MOUNTPOINTS`. |
| `-m`, `--perms` | Permissions view: `NAME`, `SIZE`, `OWNER`, `MODE`. |

The available columns are `NAME`, `KNAME`, `PATH`, `MAJ:MIN`, `FSTYPE`, `FSVER`, `LABEL`, `UUID`, `PTUUID`, `PTTYPE`, `PARTTYPE`, `PARTLABEL`, `PARTUUID`, `MOUNTPOINT`, `MOUNTPOINTS`, `SIZE`, `RO`, `RM`, `HOTPLUG`, `TYPE`, `OWNER`, `MODE`, `MODEL`, `VENDOR`, `REV`, `SERIAL`, `TRAN`, `HCTL`, `ALIGNMENT`, `MIN-IO`, `OPT-IO`, `PHY-SEC`, `LOG-SEC`, `STATE`, `ROTA`, `SCHED`, `PKNAME`, and `ID-LINK`.

### The `OWNER` and `MODE` columns

`OWNER` and `MODE` describe the *device node*, not the filesystem on it, and they mirror [`ls -l`](/peios/using-peios/peiosutils/listing-and-paths/ls.md) exactly — there is no `GROUP` column and no POSIX permission bits, because access to a device node is governed by its security descriptor, not by a mode. `OWNER` is the owner SID (`S-1-…`); `MODE` is a three-character `[type][x][+]`: the device-type character (`b` for a block device, `c` for a character device), an `x` slot (never set for a block device), and a `+` when the DACL is inheritance-protected. When the SD cannot be read — for instance on a non-Peios host with no KACS syscalls — `OWNER` shows `?` and `MODE` degrades honestly.

## Filtering, sorting and shaping

| Flag | Effect |
|---|---|
| `-a`, `--all` | Include empty (zero-size) devices, which are hidden by default. |
| `-d`, `--nodeps` | Do not print a device's holders or slaves (drop the children). |
| `-I`, `--include LIST` | Show only devices with these major numbers (comma-separated). |
| `-e`, `--exclude LIST` | Exclude devices by major number. The default is `1` (RAM disks); an explicit `-e` replaces that default, and `-a` clears exclusions entirely. |
| `-s`, `--inverse` | Print dependencies in inverse order (holders above the devices they depend on). |
| `-M`, `--merge` | Collapse a subtree shared by several parents (a multipath device) to a single occurrence. |
| `-E`, `--dedup COLUMN` | Drop rows whose `COLUMN` value duplicates an earlier one. |
| `-x`, `--sort COLUMN` | Sort siblings by `COLUMN` (numeric columns sort by value, not text). |

## Formatting

| Flag | Effect |
|---|---|
| `-b`, `--bytes` | Print `SIZE` as an exact byte count instead of a human-readable value. |
| `-p`, `--paths` | Print full `/dev` paths in the `NAME` column. |
| `-n`, `--noheadings` | Omit the header row. |
| `-i`, `--ascii` | Draw the tree with ASCII characters instead of box-drawing glyphs. |
| `-y`, `--shell` | Render column keys shell-safe (`MAJ:MIN` becomes `MAJ_MIN`). |
| `-w`, `--width NUM` | Truncate each table row to `NUM` columns wide. |
| `--sysroot DIR` | Read sysfs, the mount table and `/dev` from `DIR` instead of `/` (chiefly for testing against a fixture). |
| `-h`, `--help` / `-V`, `--version` | Standard. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | Success. |
| `1` | Incorrect invocation (an unknown column, a malformed major-number or width value) or a failed run (for example, `/sys/block` is unreadable). |

`lsblk` uses this single non-zero code, matching util-linux. A per-device read failure is not fatal: it degrades the affected columns to empty or `?` and the run continues. A broken output pipe (piping into `head`, for instance) is treated as success.

---

# mkirf

_Peios / Using Peios / Peiosutils / Boot images_

> Compile the /boot/initramfs/ source tree into a deterministic cpio initramfs image, resolving hook order and validating the layout. Has a watch mode.

`mkirf` compiles an initramfs *source tree* into an initramfs *image*. The source tree is an ordinary directory — on a running Peios system that directory is [`/boot/initramfs/`](/peios/security-fundamentals/boot-and-trust-establishment/initramfs-stage.md) — whose contents map 1:1 onto `/` inside the initramfs. The image is a single deterministic, gzip-compressed newc cpio archive that the bootloader loads into memory alongside the kernel.

```
mkirf [--watch] [--debounce SECS] [--exclude GLOB]... <src-dir> <out-file>
```

This page is the command reference. For what the initramfs stage *is* — prelude, the `/boot/initramfs/` layout, and the handoff to the real root — see [The initramfs stage](/peios/security-fundamentals/boot-and-trust-establishment/initramfs-stage.md); for how hooks declare their order, see [Boot hooks](/peios/security-fundamentals/boot-and-trust-establishment/boot-hooks.md). `mkirf` reads `<src-dir>` and writes `<out-file>`; it never writes back into the source tree, so the directory you inspect is always exactly what packages and you have put there.

## What a build does

Given a source tree, one build runs a fixed sequence:

1. **Validate the layout.** `<src-dir>` must be a directory, must carry an executable `init` (the initramfs PID 1 — a symlink is fine as long as it resolves within the tree), and must *not* already contain a hook sequence — `hooks.seq` or `system/prelude/hooks.seq.<n>` — which `mkirf` generates. A missing or non-executable `init`, or a stray sequence file, stops the build.
2. **Walk the tree.** Every object beneath `<src-dir>` becomes a cpio entry — regular files (with their executable bit preserved), directories, symlinks (target stored verbatim), character/block device nodes, and FIFOs. Sockets, and any other unsupported type, are rejected. Entries are sorted in `LC_ALL=C` byte order, which places every directory before its descendants — the order the kernel's unpacker requires.
3. **Resolve the hook order.** Hooks are the regular files directly under `<src-dir>/usr/libexec/prelude/hooks.d/` (packaged) or `<src-dir>/lcl/libexec/prelude/hooks.d/` (operator-placed), with the operator's copy winning when both hold the same file name. The legacy `<src-dir>/hooks/` is still read, and each hook found there is reported with the directory it should move to. `mkirf` parses each hook's `# /// hook` metadata block, topologically sorts the resulting capability DAG, and bakes the execution order into generated manifests injected into the image at `/system/prelude/hooks.seq.<n>`. A missing `hooks/` directory simply means no hooks — the manifests are still written, empty, because prelude treats a *missing* sequence as a broken image rather than as "nothing to run".

   The version is in the **file name**, and `mkirf` writes every version it can express faithfully — currently `hooks.seq.1` (the flat resolved order) and `hooks.seq.2` (that order plus each hook's declarations). `mkirf` ships in `peiosutils` and prelude ships in its own package, so an image can pair a newer writer with an older reader; naming the versions separately lets one image satisfy both, which a single file carrying an in-band version cannot. A version is dropped only when a future format carries something it can no longer project honestly.

   They live under `/system` — the tier for *derived* content, alongside `system/retc` — rather than `/usr`, which is the vendor tier no package ships these into, or `/var/state`, which is for mutable state rather than immutable build output. See [Validation and errors](#validation-and-errors) below.
4. **Write atomically.** The archive is written to a sibling temp file and `rename`d into place, so a failed run never leaves a half-written image behind. On success `mkirf` prints the path, entry count, and byte size to stderr.

### Early (pre-decompression) segments

A reserved `<src-dir>/++/` directory, if present, holds *early* initramfs segments — content the kernel consumes **before** it decompresses the main archive, namely CPU microcode and ACPI table overrides. Each immediate child of `++/` is one segment whose own contents map onto the cpio root (`++/microcode/kernel/x86/microcode/GenuineIntel.bin` becomes `kernel/x86/microcode/GenuineIntel.bin`); the segment directory name is a human label and never appears in the archive. Every `++/` entry must itself be a directory. The segments are merged into one **uncompressed** cpio prepended ahead of the gzip-compressed main archive. With no `++/` directory the output is exactly a single gzip member. `mkirf` stays format-agnostic here: it knows "early segments", never "microcode".

## The deterministic-build guarantee

The same source tree always compiles to the same image, byte for byte. This is what makes "did anything actually change?" a meaningful question and underpins later work such as signed boot artifacts. Determinism comes from normalising everything that would otherwise vary between builds:

- **Ownership, inode numbers, and timestamps** are all emitted as zero.
- **Permission bits are normalised.** The source tree is authoritative for file *type* and, for regular files, *executability* — nothing else. Read/write permission bits are not Peios's access mechanism, so they are flattened to a constant: directories `0755`, symlinks `0777`, FIFOs and device nodes `0644`, regular files `0755` if executable and `0644` otherwise.
- **The gzip member** carries mtime 0 and no embedded filename (the `gzip -n` equivalent), at compression level 9.
- **The early region** is byte-stable, with file payloads padded to a 16-byte boundary by widening the preceding entry's name field with NUL bytes.

## Validation and errors

`mkirf` will not produce an image from a source tree that cannot boot. A misconfiguration is caught when the image is built — on a running system where the message is easy to read — rather than as a mystery failure at the next boot. The checks:

- **Layout.** `<src-dir>` is not a directory; no `init`, or `init` is not a regular file, or is not executable; a pre-existing hook sequence; a `++` entry that is not a directory; two `++` segments defining the same file. (Exit 1.)
- **Hooks.** A malformed `# /// hook` metadata block (unclosed, a non-comment line inside it, a duplicate or unknown key, a badly-formed array), a dependency **cycle**, a `requires` that **nothing supplies**, or a capability that is **both provided and contributed to** (a capability is either a set of alternatives or a set of contributors, never both). An `after` naming a capability nothing supplies is *not* an error — that is what distinguishes it from `requires`. (Exit 1.) A hook with no metadata block at all is *not* an error — it is the escape hatch: it is scheduled last, in name order, and `mkirf` prints a warning so a forgotten block is visible.
- **I/O.** Any filesystem read, write, or compression failure. (Exit 1.)
- **Usage.** A semantic invocation error clap cannot express — currently, `--watch` with `<out-file>` inside `<src-dir>` (see below). (Exit 2.)

## Watch mode

With `--watch`, `mkirf` stays resident: it builds once up front (so the watch never begins from a stale archive), then watches `<src-dir>` recursively and rebuilds after every change, debounced. This is what lets `/boot/initramfs/` behave like an ordinary part of the filesystem — edit a hook and the initramfs is current again — rather than a build artifact an administrator has to remember to regenerate.

Watch mode is a foreground loop that runs until killed; supervising and restarting it is a service manager's job, not `mkirf`'s. A rebuild that fails (say, a hook edited into a dependency cycle) is logged but does **not** stop the watch, so fixing the offending file recovers on the next change.

`<out-file>` must not live inside `<src-dir>`: each rebuild would write the image back into the watched tree and retrigger the watch endlessly. `mkirf` refuses this invocation up front (exit 2). The `--debounce` window (default 5 seconds) is the settle time — `mkirf` waits for the tree to be quiet for that long before rebuilding, so a burst of edits produces one rebuild rather than many.

## Options

| Option | Effect |
|---|---|
| `--watch` | Stay resident and rebuild on every change to `<src-dir>`, after an initial build. Runs until killed. |
| `--debounce SECS` | With `--watch`, the settle time before a rebuild. Default `5`. A non-numeric value is a usage error. |
| `--exclude GLOB` | Exclude paths (relative to `<src-dir>`) matching `GLOB` from the image. Repeatable. `*` and `?` stay within a path segment; `**` crosses separators. A matched directory is pruned with its whole subtree. A malformed glob is a usage error. |

The two positionals are both required:

| Positional | Meaning |
|---|---|
| `<src-dir>` | The source tree; its contents map onto `/` in the initramfs. |
| `<out-file>` | The output `cpio.gz` archive. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The image was built (or `--help`/`--version` was printed). |
| `1` | An operational failure — an invalid layout, an unresolvable hook set, or an I/O/compression error. |
| `2` | A usage error — a bad option, a missing positional, or `--watch` with `<out-file>` inside `<src-dir>`. |

## See also

- [The initramfs stage](/peios/security-fundamentals/boot-and-trust-establishment/initramfs-stage.md) — what `mkirf`'s output is for.
- [Boot hooks](/peios/security-fundamentals/boot-and-trust-establishment/boot-hooks.md) — the `# /// hook` metadata block and how order is resolved.
- [mkuki](/peios/security-fundamentals/boot-and-trust-establishment/mkuki.md) — wraps `mkirf`'s image, together with a kernel and command line, into a bootable UEFI unified kernel image.

---

# mkuki

_Peios / Using Peios / Peiosutils / Boot images_

> Build a UEFI unified kernel image — kernel, initramfs, and command line appended as PE sections onto an EFI stub. Has a watch mode.

`mkuki` builds a **UEFI unified kernel image (UKI)**: it takes a PE/COFF EFI stub and appends the kernel, the initramfs, and the kernel command line to it as named PE sections, producing a single EFI binary that UEFI firmware boots directly. Where [`mkirf`](/peios/security-fundamentals/boot-and-trust-establishment/mkirf.md) produces the initramfs image, `mkuki` is the step that wraps that image — together with a kernel and a command line — into one bootable artifact. The two are the two halves of Peios' Dynamic Boot system.

```
mkuki --kernel PATH --initramfs PATH (--cmdline TEXT | --cmdline-file PATH) --out PATH
      [--stub PATH] [--watch] [--debounce SECS]
mkuki --stub-info
```

## The UKI section layout

A UKI is an ordinary PE/COFF EFI executable — the *stub* — with three extra sections appended, which the stub reads at boot to find its payloads:

| Section | Content | Source |
|---|---|---|
| `.cmdline` | The kernel command line, NUL-terminated. | `--cmdline` or `--cmdline-file` |
| `.linux` | The kernel image. | `--kernel` |
| `.initrd` | The initramfs cpio image. | `--initramfs` |

`mkuki` appends them in exactly that order. Each new section is marked as initialised, read-only data. The tool works at the PE level directly: it parses the stub's DOS/PE headers and section table, computes correctly-aligned virtual addresses and file offsets for the new sections, appends their data, updates the section count and the `SizeOfImage`/`SizeOfHeaders` fields, and — if the stub's header has no spare room for three more section entries — grows the header region and relocates the existing sections' file pointers to make space. A stub that is not a valid `MZ`/`PE` image, or whose optional header is malformed, is rejected.

### The command line

The command line is taken either literally from `--cmdline` or read from the file named by `--cmdline-file` (exactly one is required). Either way, `mkuki` trims trailing newlines and carriage returns and appends a single NUL terminator. A command line containing an embedded NUL byte is rejected.

### The stub

`--stub` names the EFI stub to append to. With no `--stub`, `mkuki` uses a stub bundled into the binary (the systemd EFI stub). `mkuki --stub-info` prints that bundled stub's provenance and exits, so you can see exactly what the default is without building anything.

## Output and atomicity

`mkuki` creates any missing parent directories of `--out`, writes the assembled image to a sibling temp file, and `rename`s it into place, so an interrupted or failed build never leaves a half-written UKI behind. On success it prints the output path and byte size to stderr. Each of the three payloads must be non-empty; an empty `.cmdline`, `.linux`, or `.initrd` fails the build.

> [!NOTE]
> `mkuki` does **not** sign the image. Producing the unified binary and signing it for Secure Boot are separate steps; `mkuki` has no signing options. Because a UKI bundles the kernel, initramfs, and command line into one PE image, that image is what a later Secure Boot milestone signs and the firmware verifies as a whole — but the signing itself is out of `mkuki`'s scope.

## Watch mode

With `--watch`, `mkuki` stays resident: it builds once up front, then rebuilds the UKI whenever an input changes — so the boot image tracks a new kernel, a freshly-repacked initramfs (`mkirf`'s half of Dynamic Boot), or an edited command-line file with no manual step. Like [`mkirf`'s watch mode](/peios/security-fundamentals/boot-and-trust-establishment/mkirf.md), it is a foreground loop that runs until killed; supervising it is a service manager's job, and a rebuild that fails (for example, a kernel caught mid-copy) is logged rather than fatal, so fixing the input recovers on the next change.

The watched inputs are `--kernel`, `--initramfs`, and — only if it is a `--cmdline-file`, since a literal `--cmdline` is static — the command-line file. `mkuki` watches each input's **parent directory** (non-recursively), not the file itself: this survives the atomic temp-and-rename writes that `mkirf` and `mkuki` both perform (which appear as a directory event a stale single-file watch would miss) and catches a versioned kernel being swapped in `/boot`. Because the watch is non-recursive, a write to an `--out` path nested deeper under a watched directory does not retrigger it — but an `--out` sitting *directly* in a watched input directory would, so `mkuki` refuses that invocation. The `--debounce` window (default 5 seconds) is the settle time before a rebuild.

## Options

| Option | Effect |
|---|---|
| `--kernel PATH` | The kernel image; becomes the `.linux` section. Required (except with `--stub-info`). |
| `--initramfs PATH` | The initramfs cpio; becomes the `.initrd` section. Required (except with `--stub-info`). |
| `--cmdline TEXT` | The kernel command line, given literally; becomes the `.cmdline` section. Mutually exclusive with `--cmdline-file`. |
| `--cmdline-file PATH` | Read the kernel command line from `PATH` instead. Mutually exclusive with `--cmdline`. |
| `--out PATH` | The output UKI path. Required (except with `--stub-info`). |
| `--stub PATH` | The PE/COFF EFI stub to append sections to. Defaults to the bundled systemd stub. |
| `--stub-info` | Print the bundled stub's provenance and exit. |
| `--watch` | Stay resident and rebuild the UKI whenever `--kernel`, `--initramfs`, or `--cmdline-file` changes. Runs until killed. |
| `--debounce SECS` | With `--watch`, the settle time before a rebuild. Default `5`. |

Exactly one of `--cmdline` or `--cmdline-file` must be given: supplying both, or neither, is a usage error.

## Exit status

| Code | Meaning |
|---|---|
| `0` | The UKI was built (or `--stub-info`, `--help`, or `--version` was printed). |
| `1` | A build or watch failure — a missing or unreadable input, an invalid stub, an empty payload, or an I/O error. |
| `2` | A usage error — a bad or missing option, or an invalid `--cmdline`/`--cmdline-file` combination. |

## See also

- [mkirf](/peios/security-fundamentals/boot-and-trust-establishment/mkirf.md) — builds the `.initrd` payload `mkuki` wraps.
- [The initramfs stage](/peios/security-fundamentals/boot-and-trust-establishment/initramfs-stage.md) — what the bundled initramfs does once firmware boots the UKI.

---

# reg

_Peios / Using Peios / Peiosutils / Registry tools_

> The reg command reads and writes the live registry — values, keys, layers, links, security descriptors, watches, and atomic batches.

`reg` is the command-line interface to the **live** registry — the running [LCS](/peios/using-peios/registry-administration/lcs-and-sources.md) store, reached through the registry system calls. Where [`regman`](/peios/using-peios/registry-administration/regman.md) reads shipped documentation and tells you what a key *means*, `reg` talks to the kernel and reads or writes what a key *is*. It is the everyday tool for scripting configuration: querying effective values, writing to layers, masking and hiding, managing security descriptors, watching for change, and taking backups.

```
reg <command> [options] <key> [value] [data]
```

`reg` is the read/write half of the registry toolset and [`regman`](/peios/using-peios/registry-administration/regman.md) is the lookup half. The division is exact:

| To… | Use… |
|---|---|
| Read or change what a value is *set to*, on this machine, right now | `reg` |
| Look up what a value *means* — its type, default, valid range, when it applies | [`regman`](/peios/using-peios/registry-administration/regman.md) |

Consult `regman` first to learn the legal range for a knob, then use `reg set` to write a value inside it. `reg` never checks a value's meaning and `regman` never touches the live store; they are complementary.

## How reg treats access and privilege

Every `reg` operation goes through the kernel's access check against the single key it opens — the same [access-control](/peios/using-peios/registry-security/access-control.md) path as any other protected object, with **no traversal check** on the parent keys. `reg` performs no identity or membership checks of its own: it does not refuse a privileged operation up front. It attempts the operation and reports faithfully whatever the kernel returns. An operation that needs a privilege you lack (creating a link, a positive-precedence layer, a backup or restore, reading a SACL) simply fails with *access denied* (exit `3`) rather than being blocked by the tool. A single `reg` command may legitimately span privilege tiers.

## Addressing model

Almost every subcommand shares one addressing scheme: a **key path** positional, and an optional **value name** positional. Understanding the split is most of understanding `reg`.

### Key paths

The key is a positional path argument. Both `/` and `\` are accepted as separators — neither character is legal inside an LCS key component, so there is no ambiguity, and you may use whichever your shell quotes most comfortably:

```
reg get Machine/System/KMES
reg get 'Machine\System\KMES'      # identical
```

A leading separator is optional and ignored (`/Machine/X` is the same as `Machine/X`). Paths are compared case-insensitively. The first component is the **hive**:

| Path | Meaning |
|---|---|
| `Machine\…` | The machine hive — system-wide configuration. |
| `Users\<SID>\…` | A specific principal's hive. |
| `CurrentUser\…` | A kernel alias, rewritten to the caller's own `Users\<SID>\` at the syscall boundary. |

On **output**, `reg` displays paths with a backslash by default. `--sep=/` (or `REG_SEP=/`) switches display to forward slash for that invocation; this affects display only, never how a path is parsed.

### The value-name positional

The value name is a **separate** positional argument — it is never folded into the key path. This is deliberate: an LCS value name may itself contain `/` or `\` (which a key component may not), so keeping them apart removes all ambiguity.

```
reg get Machine/System/KMES BufferCapacity
#       └──── key path ─────┘ └── value ──┘
```

The presence or absence of the value argument selects what the command targets:

- **No value argument ⇒ the command targets the key itself** — its metadata, its values as a set, its security descriptor, its children.
- **A value argument ⇒ the command targets that one named value.**
- The **default value** (the empty-name value) is addressed by the literal token `@` in the value-name position, mirroring `.reg` convention: `reg get Machine\App @`.

This split applies uniformly to `get`, `set`, `del`, `mask`, and `unmask`.

### Value literals and types

On `set` (and in a batch), the value's data is a single token whose registry type is either **forced** by a `type:` prefix or **inferred** from its shape. The registry value types and their literal syntax:

| Prefix | Registry type | Data syntax |
|---|---|---|
| `sz:` | `REG_SZ` | UTF-8 string (rest of token, verbatim). |
| `expand:` | `REG_EXPAND_SZ` | UTF-8 string, `%VAR%` left unexpanded on disk. |
| `dword:` | `REG_DWORD` | `42` or `0x2A`; must fit `u32`. |
| `dword-be:` | `REG_DWORD_BIG_ENDIAN` | `42` or `0x2A`; must fit `u32`. |
| `qword:` | `REG_QWORD` | `42` or `0x2A`; must fit `u64`. |
| `multi:` | `REG_MULTI_SZ` | Comma-separated; `\,` escapes a literal comma. |
| `hex:` / `bin:` | `REG_BINARY` | Hex bytes; `:`, `-`, and space separators are ignored. |
| `link:` | `REG_LINK` | An absolute key path (a symlink target). |
| `none:` | `REG_NONE` | No data (token must be empty after the prefix). |

When the substring before the first `:` is **not** a recognised keyword (for example `http://host`), the token is not treated as typed and inference applies:

| Token shape | Inferred type |
|---|---|
| all decimal digits, fits `u32` | `REG_DWORD` |
| all decimal digits, fits `u64` but not `u32` | `REG_QWORD` |
| `0x…` hex, ≤ 8 significant hex digits | `REG_DWORD` |
| `0x…` hex, ≤ 16 significant hex digits | `REG_QWORD` |
| anything else (including empty) | `REG_SZ` |

Inference is intentionally broad, so any all-digit token becomes a number — a PIN, a zip code, or `007` becomes a `REG_DWORD` and loses its textual form. **To force a string, prefix it with `sz:`** (`sz:007` stores the string `007`; `sz:dword:42` stores the literal string `dword:42`). Because the coercion is a known trap, `reg set` **always echoes the resolved type on success**, so a surprising conversion is never silent even under `--quiet`.

## Global options

Every subcommand accepts the following. Each behavioural toggle also has an environment-variable equivalent, so it can be set per-invocation (the flag) or once per shell or script (the variable). The flag wins over the variable, which wins over the built-in default.

| Option | Env var | Effect |
|---|---|---|
| `--json` | `REG_JSON=1` | Emit structured JSON instead of human text. `watch` emits JSON-lines. |
| `-v`, `--verbose` | `REG_VERBOSE=1` | More detailed output. |
| `-q`, `--quiet` | — | Suppress non-essential output. (A surprising type coercion is still reported.) |
| `--sep=CHAR` | `REG_SEP` | Path *display* separator: `\` (default) or `/`. |
| `--help` | — | Print help for the command and exit `0`. |
| `--version` | — | Print the version and exit `0`. |

Mutating subcommands additionally accept:

| Option | Env var | Effect |
|---|---|---|
| `--layer NAME` | `REG_LAYER` | Target layer for the write (default: the base layer). |
| `-y`, `--yes` | `REG_ASSUME_YES=1` | Skip the confirmation prompt on a destructive command. |

Destructive commands (`del -r`, `restore`, `layer del`) prompt for confirmation when standard input is a terminal, and auto-proceed when it is not — so interactive use is guarded while scripts are never blocked. Set `-y`/`--yes` or `REG_ASSUME_YES=1` to skip the prompt explicitly.

---

## Reading

### `reg get <key> [value]`

Read the effective (resolved) value. With a `value`, prints that value's data; with no value, lists the key's effective values (the default `@` value first, then named values sorted case-insensitively) — a shorthand for `ls --values-only`.

| Option | Effect |
|---|---|
| `-L`, `--layers` | Annotate the value with the layer it resolved from and its sequence number. |
| `--raw` | Write the value's raw bytes to standard output verbatim — for piping `REG_BINARY` data. |
| `--no-follow` | Operate on a symlink key itself rather than following it to its target. |

The single-value default form prints data bare (no quotes; one element per line for `REG_MULTI_SZ`) so it pipes cleanly. `-L` shows the winning layer's provenance:

```
$ reg get Machine\System\KMES BufferCapacity
4194304

$ reg get Machine\App Theme -L
Theme = REG_SZ "dark"   (layer: policy, seq 7)
```

Only the *winning* value is shown; the full shadowed stack beneath it is not yet exposed by any client ABI (the `-L` flag will render the whole stack unchanged once that primitive lands). See [Layers](/peios/using-peios/registry-layers/layers.md) for what "effective" resolves against.

### `reg ls <key>`

One-level listing of a key's immediate subkeys and values.

| Option | Effect |
|---|---|
| `-l`, `--long` | Long form: for subkeys, child and value counts; for values, type and byte size. |
| `--keys-only` | List subkeys only. |
| `--values-only` | List values only. |

```
$ reg ls Machine\System\KMES -l
BufferCapacity = REG_QWORD 4194304   (8 bytes)
MaxEventSize   = REG_DWORD 65536   (4 bytes)
```

### `reg tree <key>`

Recursively list the subkey tree rooted at `key`.

| Option | Effect |
|---|---|
| `--depth N` | Limit recursion to `N` levels. |
| `--values` | Include each node's values, not just its keys. |

```
$ reg tree Machine\System --depth 2
```

### `reg info <key>`

Print a key's metadata: leaf name, subkey and value counts, last-write time, hive generation, security-descriptor size, and the volatile and symlink flags.

| Option | Effect |
|---|---|
| `--no-follow` | Inspect a symlink key itself rather than its target. |

```
$ reg info Machine\System\KMES
path             Machine\System\KMES
name             KMES
subkeys          0
values           4
last_write_ns    1717243800000000000
hive_generation  42
sd_size          164
volatile         false
symlink          false
```

---

## Writing

### `reg set <key> <value> <data>`

Create or update a value. The `value` name (or `@`) and the `data` token are both required. Writes are tagged with `--layer` (default: base). See [value literals](#value-literals-and-types) for the `data` syntax.

| Option | Effect |
|---|---|
| `--layer NAME` | Write into layer `NAME` instead of the base layer. |
| `-p`, `--parents` | Create any missing ancestor keys. |
| `--expected-seq N` | Compare-and-swap guard: apply only if the value's current sequence is `N`; a mismatch exits `6` without writing. |

```
$ reg set Machine\App Build 4096
set Machine\App Build = REG_DWORD 4096   (layer: base)

$ reg set Machine\App Servers multi:alpha,beta --layer policy
```

`--expected-seq` supports lock-free read-modify-write: read the value with `-L` to learn its sequence, then write back with `--expected-seq` set to that number; if another writer changed it in between, your write fails cleanly instead of clobbering theirs.

### `reg new <key>`

Create a key with no values. Reports whether the key was created or already existed.

| Option | Effect |
|---|---|
| `--layer NAME` | Create the key in layer `NAME`. |
| `-p`, `--parents` | Create any missing ancestor keys. |
| `--volatile` | Create a volatile (RAM-only) key that does not survive a reboot. |

```
$ reg new Machine\App\Cache --volatile
created Machine\App\Cache   (layer: base)
```

### `reg del <key> [value]`

Delete a value, or a key. (Alias: `delete`.)

With a `value`, deletes **this layer's** entry for that value only — lower-layer entries resurface, because a per-layer delete is not a tombstone (use `mask` for that). With no value, deletes the key, which must be empty unless `-r` is given.

| Option | Effect |
|---|---|
| `--layer NAME` | Delete from layer `NAME`. |
| `-r`, `--recursive` | Delete the key and all its descendants (walks and removes children). |
| `-y`, `--yes` | Skip the confirmation prompt (recursive deletes prompt on a terminal). |

```
$ reg del Machine\App Legacy
deleted value Legacy from Machine\App

$ reg del Machine\App\Temp -r
Recursively delete key Machine\App\Temp and all its contents? [y/N] y
deleted Machine\App\Temp (3 keys)
```

See [Deleting keys and values](/peios/using-peios/registry-layers/deleting-keys-and-values.md) for how a per-layer delete differs from a tombstone.

---

## Masking and hiding

These commands expose LCS's tombstone and hide primitives, which *mask* lower-precedence state rather than removing a layer's own entry — the distinction that makes [layers](/peios/using-peios/registry-layers/layers.md) revertible. The layer is chosen with `--layer` (default: base).

### `reg mask <key> [value]` / `reg unmask <key> [value]`

`mask` sets a **tombstone**: a per-value marker in the target layer that hides that value from all layers below it. `unmask` clears it.

| Option | Effect |
|---|---|
| `--layer NAME` | The layer that carries the tombstone. |
| `--all` | Blanket tombstone: mask *all* lower-layer values on the key (no `value` argument). |

A `value` is required unless `--all` is given.

```
$ reg mask Machine\App ApiKey --layer policy
set tombstone on Machine\App ApiKey (layer: policy)

$ reg mask Machine\App --all --layer policy
set blanket tombstone on Machine\App (layer: policy)
```

### `reg hide <key>` / `reg unhide <key>`

`hide` installs a HIDDEN path entry in the target layer, masking the key's existence in that layer and below. `unhide` removes that layer's path entry, letting the key reappear.

| Option | Effect |
|---|---|
| `--layer NAME` | The layer that hides (or unhides) the key. |

```
$ reg hide Machine\App\Debug --layer policy
hid Machine\App\Debug (layer: policy)
```

---

## Layers

### `reg layer ls`

List layers with name, precedence, enabled state, and (informational) owner SID, ordered by descending precedence.

| Option | Effect |
|---|---|
| `-l`, `--long` | Long form. |

```
$ reg layer ls
policy                   prec=100    enabled   owner=S-1-5-32-544
base                     prec=0      enabled
```

### `reg layer new <name>`

Create a layer by writing its metadata key under `Machine\System\Registry\Layers\`.

| Option | Effect |
|---|---|
| `--precedence N` | Precedence (higher wins). A precedence above `0` requires `SeTcbPrivilege`; the tool attempts it and reports any denial. |
| `--owner SID` | Record an informational owner SID. |
| `--disabled` | Create the layer disabled. |

```
$ reg layer new policy --precedence 100 --owner S-1-5-32-544
created layer policy (precedence 100, enabled)
```

### `reg layer set <name>`

Modify an existing layer's metadata.

| Option | Effect |
|---|---|
| `--precedence N` | Change the precedence. |
| `--enable` / `--disable` | Enable or disable the layer. |
| `--owner SID` | Change the informational owner SID. |

### `reg layer del <name>`

Delete a layer — removes its metadata key; LCS tears down the layer's entries. Prompts for confirmation on a terminal.

```
$ reg layer del policy
Delete layer policy and all its entries? [y/N] y
deleted layer policy
```

Managing per-process **private** layer views is out of scope for `reg`; see [private hives and layers](/peios/using-peios/registry-advanced/private-hives-and-layers.md).

---

## Security descriptors

### `reg sd <key>`

Show or set a key's [security descriptor](/peios/security-fundamentals/security-descriptors/overview.md) as SDDL, using the same codec as the [`sd`](/peios/security-fundamentals/security-descriptors/overview.md) tool, so its output is consistent. With no scope flags, the default is owner + group + DACL (a SACL requires the privileged `ACCESS_SYSTEM_SECURITY` right).

| Option | Effect |
|---|---|
| `--set SDDL` | Apply the given SDDL. Without it, `sd` prints the current descriptor. |
| `--owner` | Scope to the owner. |
| `--group` | Scope to the group. |
| `--dacl` | Scope to the DACL. |
| `--sacl` | Scope to the SACL (needs `ACCESS_SYSTEM_SECURITY`). |

```
$ reg sd Machine\App
O:BAG:BAD:(A;;KA;;;BA)(A;;KR;;;WD)

$ reg sd Machine\App --set 'O:BAG:BAD:(A;;KA;;;BA)' --owner --dacl
set security descriptor on Machine\App
```

A security-descriptor change takes effect on future opens only (existing handles keep the access mask they were granted). **Security changes are not layered and are not undone by layer removal** — they mutate the key object directly. See [Access control on keys](/peios/using-peios/registry-security/access-control.md).

---

## Symlinks

### `reg link <key> <target>`

Create a symlink key pointing at the absolute `target` key path. This creates a key with the immutable symlink flag and a default `REG_LINK` value holding the target. Requires `KEY_CREATE_LINK` and `SeTcbPrivilege`/Administrator; the tool attempts it and reports any denial.

| Option | Effect |
|---|---|
| `--layer NAME` | Create the link key in layer `NAME`. |

```
$ reg link Machine\App\CurrentConfig Machine\App\Config\v3
linked Machine\App\CurrentConfig -> Machine\App\Config\v3 (layer: base)
```

`get` and `info` follow symlinks to their target by default; pass `--no-follow` to inspect the link key itself. See [Advanced: registry links](/peios/using-peios/registry-advanced/registry-links.md).

---

## Watching

### `reg watch <key>`

Arm a change watch and stream one event per change until interrupted (or until `--count` events). Each event faithfully means "something under the filter changed — re-read"; `reg` does not decode the change records' contents. With `--json`, emits one JSON object per line.

| Option | Effect |
|---|---|
| `--subtree` | Watch descendant keys too, not just this key. |
| `--filter LIST` | Comma-separated subset of `value`, `subkey`, `sd` (default: all). |
| `--count N` | Exit after `N` events. |

```
$ reg watch Machine\System\KMES --subtree --filter value
changed: Machine\System\KMES
changed: Machine\System\KMES
```

See [Watching for changes](/peios/using-peios/registry-concepts/watches.md) for why a service watches instead of polling.

---

## Batch, export, backup, and restore

Two paths move a subtree around: `export`/`apply` are the *portable, reviewable* path (a diffable text or JSON document); `backup`/`restore` are the *exact, privileged* path (an opaque kernel snapshot).

### `reg apply <file>`

Apply a batch of operations to one hive in a single [transaction](/peios/using-peios/registry-advanced/transactions.md) — all-or-nothing. All operations must target one hive (a cross-hive batch fails with exit `4`). `-` reads standard input.

| Option | Effect |
|---|---|
| `--dir DIR` | Apply every batch file in `DIR` (sorted), each as its own transaction. A missing or empty directory applies nothing and succeeds. |
| `--once-delete` | Delete each batch file after it applies successfully (a drain). |
| `-y`, `--yes` | Skip confirmation. |

The batch format is JSON (canonical and exact) or a line-oriented text format; `apply` auto-detects (a leading `{` or `[` means JSON). **Text-format input is not yet implemented** — supply JSON, or generate one with `reg export --json`. Text *export* works for review.

```
$ reg export --json Machine\App - | reg apply -
applied 4 keys
```

### `reg export <key> [file]`

Dump a subtree to the batch format. Omit `file` (or pass `-`) to write to standard output. Human-readable text by default; `--json` emits the canonical exact form.

| Option | Effect |
|---|---|
| `--layer NAME` | Export one layer's view (default: the effective state). |

```
$ reg export Machine\App app-config.reg
```

### `reg backup <key> <file>`

Write an opaque, exact, binary kernel snapshot of a key and its subtree to `file`. Requires `SeBackupPrivilege`.

```
$ reg backup Machine\System\KMES kmes.snap
backed up Machine\System\KMES -> kmes.snap
```

### `reg restore <key> <file>`

Replace a key and its entire subtree from a snapshot, in one transaction. Requires `SeRestorePrivilege`. Prompts for confirmation on a terminal.

```
$ reg restore Machine\System\KMES kmes.snap
Replace key Machine\System\KMES and its entire subtree from kmes.snap? [y/N] y
restored Machine\System\KMES from kmes.snap
```

See [Backup and restore](/peios/using-peios/registry-administration/backup-and-restore.md) for when to reach for each path.

---

## Output formats

Default output is terse, human-readable, and grep-friendly. `--json` switches every command to structured output — a single self-contained JSON document, except `watch`, which emits JSON-lines (one object per event). In any value listing, the default (`@`) value is emitted first, then named values sorted case-insensitively.

Type formatting on read:

| Registry type | Human form | JSON form |
|---|---|---|
| `REG_SZ` / `REG_EXPAND_SZ` | the string | `{"type":"sz","data":"…"}` |
| `REG_DWORD` / `REG_QWORD` | decimal | `{"type":"dword","data":42}` |
| `REG_MULTI_SZ` | one element per line | `{"type":"multi","data":["a","b"]}` |
| `REG_BINARY` | hex | `{"type":"binary","data":"deadbeef"}` |
| `REG_LINK` | the target path | `{"type":"link","data":"…"}` |

## Exit status

| Code | Meaning | Typical cause |
|---|---|---|
| `0` | Success. | — |
| `1` | Usage error. | Bad arguments; a required option missing. |
| `2` | Key or value not found. | `ENOENT`. |
| `3` | Access denied. | `EACCES`, `EPERM` — the kernel refused the operation. |
| `4` | Invalid specification. | Bad path, type, or literal; a cross-hive batch; a name too long (`EINVAL`, `EXDEV`, `ENAMETOOLONG`). |
| `5` | Syscall or source failure. | `EIO`, `ETIMEDOUT`, `ENOSPC`, `ENOMEM`. |
| `6` | Compare-and-swap conflict. | `EAGAIN` — an `--expected-seq` guard did not match; the value changed under you. |

A denial (`3`) reports the operation, the target, and, where relevant, the access that was required — in the style of the `sd` and `token` tools.

## See also

- [`regman`](/peios/using-peios/registry-administration/regman.md) — the registry *manual*: what a key or value *means*, as opposed to what it is set to. Consult it before you `reg set`.
- [Keys, values, and types](/peios/using-peios/registry-concepts/keys-values-and-types.md) — the data model `reg` addresses.
- [Layers](/peios/using-peios/registry-layers/layers.md) — what "effective" resolves against, and what `--layer`, `mask`, and `hide` operate on.
- [Access control on keys](/peios/using-peios/registry-security/access-control.md) — the check every `reg` operation goes through.

---

# regman

_Peios / Using Peios / Peiosutils / Registry tools_

> regman documents what a registry key or value means — type, default, valid range, and when a change applies — from shipped docs, never the live registry.

[Configuration, not storage](/peios/using-peios/registry-concepts/configuration-and-meaning.md) established that the registry holds values but not their meaning: it keeps a type tag and some bytes, and never knows that `BufferCapacity` must be a power of two or what it is for. That knowledge has to live *somewhere* a person can look it up. It lives in **`regman`**, the registry's manual.

`regman` is the `man` of the registry. Give it a path and it tells you what a key or value actually is. It is the everyday tool for configuring a Peios system — before you touch a knob, `regman` is how you find out what it does and what changing it will cost.

## regman, in one sentence

**`regman <path> [value]` documents what a registry key or value *means* — its type, default, valid values, when a change takes effect, and a prose description — drawn from shipped documentation, never from the live registry.**

That last clause is the one to hold onto; there is a section on it below.

## Looking up a value

Give `regman` a key path and a value name and it prints a **knob-card** — an identity line, an aligned block of facts, and a description:

```
$ regman Machine\System\KMES BufferCapacity

Machine\System\KMES BufferCapacity                    documented by kmes

  Type     REG_QWORD
  Default  4194304  (4 MB)
  Valid    65536–268435456 bytes (64 KB–256 MB), power of two
  Applies  live — ring-buffer swap

Per-CPU ring buffer capacity, in bytes. Must be a power of two; values
that are not are treated as invalid and ignored.
```

Every registry value is a *knob*, and every knob has the same few facts worth knowing, so the card is uniform rather than free-form:

| Field | Tells you |
|---|---|
| **Type** | The value's registry type (`REG_QWORD`, `REG_SZ`, …). |
| **Default** | The value used when nothing is set. |
| **Valid** | The range, set, or constraint a sensible value must satisfy. |
| **Applies** | When a change takes effect — `live`, on `restart`, or on `reboot`. |

The **Applies** field is the one operators reach for most: it is the difference between "edit it and you're done" and "edit it and schedule a reboot". Only the fields that make sense for a given item are shown, and a knob that is being retired carries a prominent deprecation notice at the top of its card.

## Looking up a key

Give `regman` just a key — no value — and it does double duty as an *index*: the key's own description, then a list of every value documented under it, one summary line each.

```
$ regman Machine\System\KMES

Machine\System\KMES                                   documented by kmes

The KMES event subsystem reads its tuning parameters from the values
under this key...

Values
  BufferCapacity         Per-CPU ring buffer capacity in bytes.
  MaxEventSize           Max total size of a userspace-emitted event.
  MaxNestingDepth        Max nesting depth for userspace event payloads.
  MaxEmitRatePerProcess  Max events per second a single process may emit.
```

So you can start at a subtree, see what is configurable there, and drill into any single value.

## Searching, when you do not know the path

If you don't know where a setting lives, `regman -k` searches names and summaries — the registry's `apropos`:

```
$ regman -k buffer
Machine\System\KMES BufferCapacity   Per-CPU ring buffer capacity in bytes.
```

## regman documents intent, not current state

This is the boundary that matters most. **`regman` tells you what a setting *should be* — what it means and which values are legal — not what it is currently set to on this machine.** It reads shipped documentation, never the live registry; it does not talk to [LCS](/peios/using-peios/registry-administration/lcs-and-sources.md) at all. Ask `regman` about `BufferCapacity` and you learn it must be a power of two and defaults to 4 MB; you do *not* learn that this particular box has it set to 8 MB right now. Reading the current value is a separate, live query against the registry — that is [`reg`](/peios/using-peios/registry-administration/reg.md)'s job. `regman` tells you a knob *should* be a power of two; `reg get` tells you what it is set to, and `reg set` changes it. Reach for `regman` to decide what to write, and `reg` to write it.

Put `regman` next to the other two things from [Configuration, not storage](/peios/using-peios/registry-concepts/configuration-and-meaning.md) and a clean division of labour appears:

| To learn… | Look at… |
|---|---|
| What a setting *means*, and what is valid | `regman` — the manual |
| What the value is *set to* | [`reg`](/peios/using-peios/registry-administration/reg.md) — a live query against the store |
| What the subsystem is *actually running on* | the [event log](/peios/security-fundamentals/auditing/overview.md) |

`regman` is the one you consult first, and the only one that works before you have changed anything — because documentation ships with the software, not with the machine's state. It is the natural complement to [reject-or-keep](/peios/using-peios/registry-concepts/configuration-and-meaning.md): `regman` tells you the valid range up front, so you set a good value the first time instead of writing one the owning subsystem will quietly refuse.

## Where the documentation comes from

`regman` has no built-in database of every setting. Each package **ships its own documentation** as files dropped into a well-known directory (`/usr/share/regman/`), one fragment per package documenting that package's whole configuration surface. Install a package and its registry documentation appears; remove the package and it goes away. The package that *owns* a set of keys is the package that documents them — the only arrangement that stays correct as the system changes.

A consequence worth knowing: `regman` can only describe settings that some installed package has documented. An undocumented key is invisible to it — `regman` is exactly as complete as the packages on the machine make it. If two packages happen to document the same item, `regman` shows both and flags the overlap rather than silently picking a winner.

(For the people who *write* that documentation, `regman fmt` and `regman lint` prepare and check fragments, and `regman index` keeps lookups fast on large corpora — the lookup is always correct without an index, which is purely an accelerator. These are packaging concerns rather than everyday operator ones.)

## Where to go next

For the idea `regman` exists to serve — why the registry holds values without holding their meaning, and what reject-or-keep means — read [Configuration, not storage](/peios/using-peios/registry-concepts/configuration-and-meaning.md).

For the other half of the picture — reading what a value is actually *set to*, which is a live query against the store rather than a documentation lookup — read [LCS and sources](/peios/using-peios/registry-administration/lcs-and-sources.md).
