Appearance
Linux & the Terminal
You're trying to get a model running locally, and it crashes with a CUDA error. The fix is in a GitHub issue, but every instruction starts with cd ~/some-project && grep -r "something" . and you're not sure whether that's one command or three, or what the && does, or why grep is printing 400 lines when you only needed the one with the error message. You copy-paste the whole thing, it fails with "permission denied," and now you're stuck on a problem that has nothing to do with the model.
This is the thing about AI development: almost everything runs through the terminal. Python scripts, Docker containers, model downloads, database queries, git operations — they all live at the command line. The terminal isn't a relic of 1970s computing that we haven't gotten around to replacing. It's the most direct, composable interface for controlling a machine, and the AI ecosystem is built on top of it. Every tutorial, every README, every debugging session assumes you're comfortable there.
If you can read what the terminal prints back at you and compose a few commands into a pipeline, you go from "the thing broke and I don't know why" to "the process on port 8000 didn't release the socket, kill it and try again" in about fifteen seconds. That's the difference this lesson is about.
What you'll learn
- The terminal is the control plane for every AI tool you'll use — Python, Docker, git, model runners, databases
- Core commands (ls, cd, cp, mv, rm, grep, pipes) cover 80% of what you'll do day to day
- Process management (ps, kill, htop) is how you recover when things go wrong
- The shell is programmable — once you internalize pipes and redirection, you stop doing things one at a time
The problem: the terminal is the control plane for everything
A terminal window runs a shell — a program that waits for you to type a command, runs it, prints the result, and waits again. On most Linux systems the default shell is bash. When you open a terminal, you're looking at bash's prompt, which usually ends in $ and tells you your username, hostname, and current directory.
The commands you type aren't magic words the terminal "understands." They're actual programs sitting somewhere on disk (usually in /usr/bin or /usr/local/bin). When you type ls, bash searches a list of directories (your PATH) until it finds a program called ls, then runs it. You can see where any command lives with which ls.
This matters because it means the terminal isn't a walled garden. Anything you can write a program to do, you can do from the terminal. The AI tools you'll use — python, uv, docker, git — are programs too, same as ls. They all speak the same language: arguments, flags, standard input, standard output, exit codes.
Options & when to use each
When to reach for the terminal vs. a GUI
| Approach | Good for | Costs you | When to pick it |
|---|---|---|---|
| GUI file manager | Browsing photos, quick drag-and-drop moves | Can't script, can't filter, can't chain with other tools | Casual one-off file operations |
| Terminal commands | Bulk operations, searching, scripting, remote servers | Steeper initial learning curve | Anything you'll do more than once, or anything on a remote machine |
| Shell script | Repeated workflows, automation, CI/CD | Another file to maintain and debug | Any sequence of commands you've typed manually three times |
For AI development, you'll spend almost all your time in the terminal column. SSH into a cloud GPU instance? Terminal. Restart a Docker container that's holding a port? Terminal. Search every Python file in a project for import torch? Terminal in one line, GUI in... you'd still be clicking.
Build it: the commands you will use every day
Here are the commands that cover the overwhelming majority of what you'll type in a terminal. We're not going to list every flag — man ls already does that — but we'll cover what each is for and the flags you'll actually reach for.
Navigation and inspection
bash
# Where am I?
pwd
# What's in this directory? (-l for details, -a for hidden files)
ls -la
# Move somewhere else
cd ~/projects/my-agent
cd .. # up one level
cd - # back to the last directory you were in
cd # home directoryls -la is the one you'll type the most. The -l gives you permissions, owner, size, and modification time. The -a shows files starting with . — configuration files like .bashrc, .gitignore, and .env that are hidden by default.
Reading files
bash
# Print the whole file to the terminal
cat config.yaml
# Page through a large file (space for next page, q to quit)
less server.log
# First 10 lines, last 20 lines
head -n 10 data.csv
tail -n 20 server.log
# Watch a file grow in real time (great for log files)
tail -f server.logcat is fine for small files. For anything longer than your screen, less is what you want — it loads the file incrementally and doesn't choke on multi-gigabyte logs. tail -f is how you watch a running process: start your agent server in one terminal, tail -f server.log in another, and you'll see every log line as it's written.
Creating, moving, deleting
bash
# Create a directory (and any parents needed)
mkdir -p ~/projects/agent/logs
# Copy a file
cp model_config.yaml model_config.yaml.bak
# Move or rename
mv old_name.py new_name.py
# Delete a file (this is permanent — no trash bin)
rm old_checkpoint.pt
# Delete a directory and everything in it — be careful with this one
rm -rf ~/projects/agent/old_runsA word about rm: there is no undo. When you delete something with rm, it's gone. This isn't Windows or macOS where deleted files sit in a trash folder. Get in the habit of typing the full path and pausing for half a second before you hit enter. If you're deleting something with rm -rf, double-check the path twice.
Searching and filtering
bash
# Search inside files for a pattern
grep -r "cuda" ~/projects/my-agent/
# Case-insensitive, show line numbers
grep -rin "error" server.log
# Count matches instead of showing them
grep -c "200" access.log
# Find files by name (much faster than grep -r for filenames)
find . -name "*.py"
find . -name "*.py" -mtime -1 # modified in the last daygrep is probably the single most important command in your toolkit. The -r flag makes it recursive — it'll search every file in every subdirectory. Combine it with a pipe and wc -l to count results, or pipe to less when you've got hundreds of matches and need to scroll through them.
Pipes and redirection: the secret weapon
The shell's real power is composition. Every command reads from standard input (stdin, file descriptor 0) and writes to standard output (stdout, fd 1). Errors go to standard error (stderr, fd 2). Pipes connect one command's stdout to the next command's stdin.
bash
# Find all Python files, search for "import torch", count them
find . -name "*.py" | xargs grep -l "import torch" | wc -l
# Find the 5 largest files in a directory
du -sh * | sort -hr | head -5
# Save command output to a file
pip freeze > requirements.txt
# Append to a file instead of overwriting
echo "export CUDA_VISIBLE_DEVICES=0" >> ~/.bashrc
# Throw away output (useful for noisy commands)
some_loud_command > /dev/null 2>&1The find / xargs / grep pipeline is worth studying because it's a pattern you'll use constantly. find lists files, pipe to xargs to feed them as arguments to another command, grep searches inside them, pipe to wc -l to count. Once this clicks, you stop opening files one at a time.
Process management: when things go wrong
AI workloads are resource-hungry. Models consume GPU memory, training loops peg CPUs, and sometimes a process hangs and won't let go of a port. You need to be able to see what's running and stop what needs stopping.
bash
# What's running? (-a for all users, -u for user-oriented format, -x for processes without a terminal)
ps aux
# Find a specific process
ps aux | grep python
pgrep -fl python
# Live, interactive view (htop is more readable than top)
htop
# Kill a process by PID
kill 12345 # polite — asks the process to shut down
kill -9 12345 # forceful — the kernel terminates it immediately
# Kill by name
pkill -f "python train.py"
# What's using port 8000?
lsof -i :8000
ss -tulpn | grep 8000htop is your dashboard. Start it in a spare terminal pane while you're running training or inference and you'll see CPU usage, memory consumption, and every process on the system in a sortable, scrollable table. Press F9 to kill a process, F6 to sort by a different column.
The port-checking pattern (lsof -i :PORT or ss -tulpn | grep PORT) solves the most common AI dev error: "Address already in use." When your FastAPI server or Jupyter notebook refuses to start because something is already listening on its port, one of these commands tells you the PID, and kill removes it.
Build it: a real terminal session
Let's walk through a realistic scenario. You've got an agent project that should be running on port 8000, but it's not responding. Here's how you diagnose and fix it from the terminal.
Step 1: Check if anything is listening on the port.
bash
ss -tulpn | grep 8000If you see output, something is bound to that port. Note the PID. If you see nothing, the server isn't running at all.
Step 2: Check if the process is even alive.
bash
ps aux | grep "uvicorn\|fastapi\|your_server_name"If it's there but not responding, it might be hung. If it's not there, it crashed.
Step 3: Look at the logs.
bash
tail -100 server.log | grep -i "error\|traceback\|exception"This narrows the last 100 lines of the log to anything that looks like an error. Often the root cause is right there — a missing environment variable, a model file that didn't download, a CUDA out-of-memory.
Step 4: Kill the stuck process and restart.
bash
# Get the PID from step 1 or 2
kill 12345
# If it doesn't respond, force it
kill -9 12345
# Verify the port is free
ss -tulpn | grep 8000
# Restart
python server.pyStep 5: Confirm it's serving.
bash
curl -I http://localhost:8000/healthYou should see HTTP/1.1 200 OK or similar. If you get "connection refused," the server isn't up yet — check the terminal where you started it for errors.
What goes wrong
| Mistake | How you notice it | The fix |
|---|---|---|
Running rm -rf in the wrong directory | You meant rm -rf ./build/ but typed rm -rf / build/ — that space is catastrophic. Your prompt might show you're suddenly in / | There's no undo. Prevention: always type ./ for relative paths, pause before enter, use rm -ri for interactive confirmation on risky deletes |
Forgetting sudo on a system command | "Permission denied" — you tried to apt install or systemctl restart as your regular user | Prefix with sudo. Not every permission error needs sudo though — check if the file is actually owned by someone else (ls -la) before escalating |
grep returning nothing when you know the text is there | grep "Error" log.txt returns empty, but you can see "Error" in the file | Try grep -i "error" (case-insensitive). Or the pattern contains regex special characters — use grep -F "literal.string" to disable regex |
| A pipe that seems to hang forever | You typed grep something huge_file | sort and nothing happens | The first command is still running. For large files, pipe through head early: grep something huge_file | head -20. Or the second command is waiting for the first to finish before it can sort |
kill doesn't kill the process | kill 12345 returns no error, but `ps aux | grep 12345` still shows it running |
Confirm it worked
Open a terminal and work through this checklist:
- Run
ls -la ~and identify which entries are files, which are directories, and what the permission string means for one of them - Find every
.pyfile in your project directory withfind . -name "*.py" | wc -l - Start
htop, sort by memory usage (F6, then select MEM%), and identify the top 3 processes - Run a long command (like
find /usr -name "*.so") and cancel it with Ctrl+C before it finishes - Pipe
ps auxthroughgrepto find your own shell process, then throughwc -lto count how many you have
If you can do all five without reaching for a search engine, you're comfortable enough in the terminal for everything this portal throws at you.
Next: Finding Your Way Around Linux — the filesystem tree, permissions, and how to find anything on disk.