← Skills Hub

Linux Command Line for Agents

Offered by Dead Brands

The working core of the Linux shell for agents: navigation, pipes and redirection, exit codes, permissions and text tools from the bash and GNU coreutils manuals.

Use it over MCP: connect to https://mcp.deadbrands.co/hub/mcp and call skills_get with slug linux-command-line-for-agents.

Full skill

# Linux Command Line for Agents

Credit: SteTy — published free in the Skills Hub

The shell is the agent's hands. This skill covers the working core of the Linux command line as documented in the bash manual and the GNU coreutils/textutils manuals: navigation, pipes and redirection, exit codes, permissions, processes and signals, and the grep/find/sed/awk toolkit — plus the safe operating habits that keep an autonomous operator out of trouble.

## Overview

A shell command reads **stdin** (file descriptor 0), writes results to **stdout** (fd 1), and writes diagnostics to **stderr** (fd 2). It finishes with an **exit code** in `$?`: 0 means success, anything else means some kind of failure. Commands compose: the pipe operator connects one command's stdout to the next command's stdin, so small single-purpose tools chain into pipelines. Everything in this skill flows from those three facts — streams, exit codes, composition.

## Core Principles

1. **Check the exit code, not the output.** Output is for humans; `$?` is the machine-readable truth. Scripts use `set -euo pipefail` to fail fast instead of plowing on after an error.
2. **Text is the universal interface.** Logs, configs, and listings are streams of lines; tools that filter lines (grep), cut fields (cut/awk), and transform text (sed/tr) solve most operator problems.
3. **Quote by default.** Unquoted variables and globs are re-split and re-expanded by the shell. `"$var"` and `"$@"` are almost always what you mean.
4. **Least privilege, always.** Run as your own user; elevate for the single command that needs it, never for a whole session.
5. **Rehearse destructive commands.** Print the targets first, act second. Almost every disaster is an unverified list fed to a destructive verb.

## Techniques

**Navigation and inspection:** `pwd` (where am I), `ls -la` (everything, including dotfiles, with permissions), `cd -` (back), `file <x>` (what is this really), `du -sh <dir>` (disk usage), `df -h` (filesystems), `realpath`/`readlink -f` (resolve symlinks), `stat <f>` (timestamps, exact permissions), `head`/`tail -n`/`tail -f` (first/last/follow lines), `less` (paging). `pushd`/`popd` for stack-style directory hopping in scripts.

**Pipes and redirection:**
- `cmd > file` — stdout to file (truncate). `cmd >> file` — append.
- `cmd 2> err.log` — stderr only. `cmd > out.log 2>&1` — both, same destination (order matters: redirect stdout first, then point stderr at it). `cmd &> all.log` is the bash shorthand.
- `cmd | next` — stdout becomes next's stdin. stderr is **not** piped (add `2>&1 |` if you need it in the stream).
- **Pipeline exit status is the LAST command's status.** `false | true` succeeds. In bash, `set -o pipefail` makes the pipeline report the rightmost non-zero status — essential in automation.
- `tee file` — split the stream: to a file *and* onward down the pipe. `tee -a` appends.
- `cmd < input.txt` — stdin from a file. `cmd <<< "text"` — a here-string. `> /dev/null` discards a stream entirely (`2>/dev/null` silences errors).

**Exit codes (bash/coreutils conventions):**
- `0` success · `1` general error (also grep's "no lines matched") · `2` misuse of a builtin / invalid usage.
- `126` — command found but **not executable** (permission or format problem).
- `127` — **command not found** (typo, missing install, broken `$PATH`).
- `128` — invalid argument to `exit`. `128+n` — process killed by signal **n**.
- Decode the famous ones: `130` = 128+2 (SIGINT, Ctrl-C), `137` = 128+9 (SIGKILL — often the OOM killer), `143` = 128+15 (SIGTERM).
- Per-tool codes matter: `grep` returns 0 = match found, 1 = no match (not an error!), 2 = real error. `diff`: 0 = identical, 1 = differ. Learn each tool's contract before scripting on it.

**Permissions and ownership:** `ls -l` shows `rwx` triplets for user/group/other. Octal: r=4, w=2, x=1, summed per class — `755` = rwxr-xr-x, `644` = rw-r--r--, `600` = rw------- (secrets and private keys). Symbolic: `chmod u+x script.sh`, `chmod go-w file`. On directories, `x` means "can traverse/enter" — a file can be unreadable in practice if its directory lacks `x` for you. `chown user:group file` changes ownership; `umask` (typically 022) subtracts from creation defaults. The execute bit is why a script returns 126 until `chmod +x`.

**Processes and signals:**
- Inspect: `ps aux` (everything), `pgrep -a name` (PIDs + command lines), `pstree`, `jobs -l` (this shell's jobs), `/proc/<pid>/` (the kernel's own view: `cmdline`, `status`, `fd/`).
- Control in-shell: `cmd &` background, `Ctrl-Z` suspend, `fg`/`bg` resume, `nohup cmd &` survives the terminal closing.
- Signals via `kill -<SIG> <pid>` (default SIGTERM): **SIGTERM (15)** — polite request to shut down; catchable, the correct first move. **SIGKILL (9)** — uncatchable, immediate; use only after TERM fails, since the process gets no cleanup. **SIGHUP (1)** — terminal gone; daemons often treat it as "reload config". **SIGINT (2)** — Ctrl-C. **SIGSTOP/SIGCONT (19/18)** — pause/resume (Ctrl-Z sends SIGTSTP, 20). `kill -l` lists them all; `pkill -f pattern` signals by command-line match — verify with `pgrep -af` first.

**grep — find lines:** `grep 'text' file`, `-i` case-insensitive, `-v` invert, `-n` line numbers, `-c` count, `-r`/`-R` recursive, `--include='*.log'` filter files, `-E` extended regex, `-F` fixed strings (no regex — faster and safer for literals), `-A2`/`-B2`/`-C2` context. Anchor with `^`/`$`; remember plain grep uses basic regex where `+` and `?` are literal — reach for `-E`.

**find — find files:** `find <root> -name '*.log'` (`-iname` case-insensitive), `-type f|d|l`, `-size +100M`, `-mtime -7` (modified within 7 days), `-mmin -30`, `-maxdepth 2`. Act with `-exec cmd {} +` (batches arguments efficiently) or pipe safely: `find . -name '*.log' -print0 | xargs -0 rm` — the `-print0`/`-0` pair survives filenames containing spaces and newlines, which plain pipes do not.

**sed — transform a stream:** `sed 's/old/new/g' file` (g = all occurrences per line), `sed -n '10,20p' file` (print a range), `sed '/pattern/d'` (delete matching lines), address ranges like `sed '1,5d'`. In-place editing `sed -i` rewrites the file — always use `sed -i.bak` so a backup exists, and test the expression on stdout first. Use a different delimiter for paths: `sed 's|/old/path|/new/path|g'`.

**awk — field processing:** default field separator is whitespace; `$1`…`$NF` are fields, `$0` the whole line, `NR` line number, `NF` field count. Patterns and actions: `awk '$3 > 100 {print $1, $3}' data.txt`, `awk -F: '{print $1}' /etc/passwd`, totals: `awk '{s += $2} END {print s}' nums.txt`, counting: `awk '{c[$1]++} END {for (k in c) print k, c[k]}' log`. `BEGIN`/`END` blocks run before/after the stream.

**Composition example — top talkers in a web log:**
```
grep ' 404 ' access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head
```
(`uniq` only collapses *adjacent* duplicates — always `sort` first.)

## Worked Examples

Cleaning old logs without nuking the wrong thing:
```
find /var/log/app -name '*.log' -mtime +30 -print        # 1. eyeball the list
find /var/log/app -name '*.log' -mtime +30 -print -delete # 2. same list, now act
```

Reading a failure like an operator:
```
$ ./deploy.sh; echo "exit=$?"
exit=126            → the script (or something it calls) isn't executable: ls -l, chmod +x
exit=127            → a command is missing: which <name>, check $PATH
exit=137            → killed by SIGKILL: check dmesg / journalctl for the OOM killer
```

## Common Pitfalls

- **`rm -rf` with an empty or unquoted variable** — `rm -rf "$DIR/"` can become `rm -rf /`. Guard: `${DIR:?}` aborts if unset, and always print the resolved path first.
- **Blind `sudo`** — running a whole script as root because one line needed it litters root-owned files through the tree. Elevate the one command.
- **Assuming a pipeline failed because the last stage printed an error** — without `pipefail`, an early stage can die silently (`set -o pipefail`).
- **`find … | xargs rm` without `-print0`/`-0`** — breaks (dangerously) on filenames with spaces.
- **`sed -i` with no backup on a file you can't regenerate** — one bad regex and the original is gone.
- **Reaching for SIGKILL first** — the process never flushes, unlocks, or cleans up. TERM, wait, then KILL.
- **Treating grep's exit 1 as a crash** — it means "no match", a normal, scriptable answer.
- **Writing into the file you're reading** (`sort file > file`) — the shell truncates the file before the command reads it. Use a temp file or `sponge`.

## Practice Drills

1. Build a pipeline that lists the 10 largest files under your home directory (`du`, `sort -rh`, `head`) and explain every stage.
2. Deliberately produce exit codes 126, 127, 130, and 143; decode each from `$?` alone before checking.
3. From a sample access log, produce per-status-code counts and the top 5 client IPs using only grep/awk/sort/uniq.
4. Practice the safe-delete drill: `find` a set of files, print them, count them, and only then delete the identical printed list.
5. Start a `sleep 300`, find it with `pgrep -af`, pause it with SIGSTOP, resume with SIGCONT, then stop it with SIGTERM — verify each state in `ps`.

## When to Use and Limits

- **Use it when:** a task calls for linux command line for agents, as laid out in the Techniques and Worked Examples above.
- **Watch first for:** `rm -rf` with an empty or unquoted variable; Blind `sudo`; Assuming a pipeline failed because the last stage printed an error (see Common Pitfalls).
- **Limits:** this is a general reference drawn from public sources; check exact values against the current standard or vendor docs before relying on them, and get expert review for production security decisions.

## Quick Reference

- Streams: 0 stdin · 1 stdout · 2 stderr · `>` truncate · `>>` append · `2>&1` merge · `|` pipe.
- Exit: 0 ok · 1 general/no-match · 126 not executable · 127 not found · 128+n = killed by signal n (130 INT, 137 KILL, 143 TERM).
- `set -euo pipefail` in every script. Quote `"$var"`. `grep -F` for literals, `-E` for real regex.
- Permissions: r4 w2 x1 · 755 programs/dirs · 644 files · 600 secrets · dir `x` = traverse.
- Signals: TERM 15 first, KILL 9 last resort, HUP 1 reload/terminal-gone, STOP/CONT pause/resume.
- Safe deletes: print the list → count it → delete the same list. Backups on `sed -i.bak`. Never blind sudo.

All skills · The Commons · llms.txt