Module 4: Permissions, Processes, and Environment
1. Introduction: who you are, what is running, and what your shell knows
Description
By the end of this lesson you will be able to look at any error message the terminal hands you — Permission denied, command not found, or a port that says it is busy — and know, before touching a single key, which of three problem families it belongs to: permissions, processes, or environment. You are not going to fully solve all three yet — that is the seven lessons that follow — but you are going to stop guessing.
This matters because those three messages account for a huge share of the hours a junior developer loses in their first year, and almost nobody explains why they happen. Imagine your first day at a new job: you inherit the laptop of someone who left the company, you clone the team's repository, and you run the script the README promises will get everything working. The terminal hands you three words — Permission denied — and there goes your morning. The most common reaction, the one almost everyone learns by imitation and not by explanation, is to try brute force: sudo in front of anything that fails, chmod 777 on any file that complains, restarting the computer once nothing else comes to mind. Sometimes it works. It never tells you why it worked, and that "why" is exactly what this module gives you back.
Connection to the module: this lesson opens module 4 with the full map — the three problem families, why brute force seems to work, and what diagnosing instead means — and announces the project that closes the module. The next lesson, Users, groups, and why permissions exist, brings this map down to the first concrete terrain: the Unix user model and why your own laptop behaves as if it were a shared server.
The on-call doctor, not the home remedy
Everyone who starts using the terminal inherits, sooner or later, three home remedies nobody ever really explained: put sudo in front of any command that fails, put chmod 777 on any file that complains, and restart the computer when nothing else comes to mind. They are home remedies in the strict sense of the word: they are not born from understanding the cause, they are born from having seen the symptom disappear once after applying them. Sometimes they work — by coincidence, or by sheer brute force — and that occasional success is exactly what keeps them alive generation after generation of developers.
Compare it to a real on-call doctor. They do not prescribe amoxicillin to anyone who walks in with pain, regardless of whether the pain is an infection, an allergy, or a broken bone: first they ask what hurts, since when, and what makes it worse. Only with that information do they decide the treatment — and sometimes the right treatment is to do nothing yet. This module trains you to do exactly that in front of a terminal, sorting every possible symptom into three medical-record folders:
- Permissions — who is allowed to read, write, or execute each file, and why that is a decision, not an accident.
- Processes — which programs are running on your machine right now, who owns them, and how to end one with precision instead of blindly.
- Environment — what your shell knows about where to look for the commands you type, and what variables it carries with it while it works.
Each of the three has its own chapter later in this module. For now, all you need is the right folder: knowing which of the three the symptom in front of you lives in, before attempting any cure.
Worked example
Let us reproduce, with real commands, the three symptoms that give this module its name. You are not going to fix any of them yet — that is exactly what we are not going to do in this lesson — you are only going to learn to recognize which family each one belongs to.
Symptom 1 — you try to run your own script:
echo 'echo "deploy complete"' > setup.sh
./setup.sh
What to expect (bash):
bash: ./setup.sh: Permission denied
If your shell is zsh (the one macOS ships by default since 2019), the same problem gets reported differently: zsh: permission denied: ./setup.sh. The message's shape changes depending on the shell; the cause you study in lesson 3 is the same.
Symptom 2 — you try to run a tool the project expects you to already have installed:
lint-project
What to expect (bash):
bash: lint-project: command not found
What to expect (zsh):
zsh: command not found: lint-project
Symptom 3 — you try to spin up a test server while you already have another one running. Open two terminal tabs. In the first, leave this running:
python3 -m http.server 8000
Without closing that tab, in the second one run the exact same command:
python3 -m http.server 8000
What to expect in the second tab (the full traceback is longer; only the last line matters here):
Traceback (most recent call last):
...
OSError: [Errno 48] Address already in use
On Linux you are going to see [Errno 98] instead of [Errno 48] — it is the same problem, with the error number each operating system uses.
Now classify all three, with only the information you have today, without decoding anything yet:
| Symptom | Family | Why |
|---|---|---|
Permission denied when running ./setup.sh | Permissions | The file exists and you can read it, but the system did not let you execute it: it is a question about who can do what with that file. |
command not found when running lint-project | Environment | The shell did not find any program with that name in the places it knows to look. It is not that the file exists and is forbidden: it is that your shell cannot locate it. |
OSError: Address already in use when starting the second server | Processes | Something already running on your machine has that port number busy. The problem is neither the file nor the shell: it is a live program that needs to be identified. |
That table is, literally, the module's map. Every row has a lesson — or several — devoted entirely to solving it with precision.
Why the home remedy "works" (and why it charges you later)
It is worth understanding why brute force seems to work, because that is what makes it so hard to give up.
sudo in front of anything tells the system "run this as if you were the most powerful user that exists on this machine, with no check against the normal permissions." That is why it "works": it does not fix the permissions mismatch, it bypasses it entirely, going right over the whole permissions system. The price comes later. The most frequently documented case is sudo npm install -g something: it does install the package, yes, but it leaves package-manager folders owned by root instead of by your normal user. The next time you install something without sudo — which is how it is supposed to be used — you run into a new EACCES, in a place where there was none before. npm's official documentation dedicates an entire page to this exact problem and recommends not using sudo to install packages, precisely for this reason.
chmod 777 on any file that complains gives read, write, and execute permission to anyone with an account on that machine, not just you. On your personal laptop the risk is low. On a shared server — where you are going to work sooner or later — it is opening a door with no lock in a building with more than one tenant. Lesson 3 shows you exactly how much permission you need to give, not one bit more.
Restarting the computer sometimes does fix something: a truly stuck process can disappear with the restart. But it does not tell you which process it was or why it showed up, so if something leaves it running again tomorrow, you are going to restart again with nothing learned. Lesson 5 gives you the exact command to find and end a specific process, without shutting down the other fifty you actually need.
None of these three remedies is forbidden in itself — there are legitimate moments to use sudo, and even to restart. They are a problem as a first reflex, before knowing which family of problem you are facing. That order — diagnose first, act after — is this entire module's skill.
The module's project: the clinic of a broken environment
The seven chapters that follow end in a project that gives this metaphor its name: you are going to receive a work environment deliberately broken — a script with no execute permission, a phantom process holding a port you need, a command that "should" be available and does not show up — and your task is not to fix it with whatever comes to mind first. It is to write, symptom by symptom, your own medical record: what you observed, which family it belongs to, what command you used to confirm it, and only then, the exact, minimal fix. No blind sudo, no spare chmod 777.
Common mistakes
Believing Permission denied is a system error or the terminal misbehaving (conceptual). What happens: someone sees the message and interprets it as a failure — "the terminal is broken," "this file is corrupt" — instead of as information. Why it happens: in almost any other software, a message in red means something went wrong. In Unix, Permission denied almost always means the system did exactly its job: it checked who you are, checked what you asked it to do, and denied it on purpose. How to spot it: if your first reaction to the message is to look up how to "fix the terminal" instead of asking yourself what permission you are missing, you already fell into this confusion. How to fix it: treat every Permission denied as a well-formed question from the system — do you really want this to be possible? — and not as an obstacle to dodge. The next two lessons give you the exact vocabulary to answer it.
Using sudo as the automatic response to any error, without reading what the message says. What happens: a command fails, and the reaction is to prepend sudo without having checked whether the message even mentions permissions. Why it happens: sudo "works" with a surprising frequency because a lot of errors unrelated to permissions — a missing dependency, a busy port, a misspelled path — also happen to disappear if you run as superuser, even for a reason completely different from what you assumed. How to spot it: if you cannot explain in one sentence why the problem was specifically about permissions, sudo was not a diagnosis: it was a bet that paid off. How to fix it: read the full error message before deciding anything; lesson 4 teaches you to tell when sudo really is the solution and when it is a sign the real error is something else.
Restarting the computer in front of an unresponsive process, instead of identifying it. What happens: something gets stuck — a busy port, a hung program — and the go-to solution is restarting the whole system. Why it happens: restarting does end the problematic process, along with every other one you had open, so it feels like a complete solution when it actually is one that distinguishes nothing. How to spot it: if the same symptom — the same busy port, the same hung program — happens to you again weeks later and you still do not know which program caused it, you never diagnosed it: you only postponed it. How to fix it: lesson 5 gives you the exact command to see which process is holding a specific resource and end only that one, leaving everything else untouched.
Exercises
1. Without running anything yet, classify each symptom into one of the three families — permissions, processes, or environment — using only this lesson's criterion (which question each family answers): (a) you run deploy.sh and the terminal responds Permission denied; (b) you try to start your application and see Address already in use; (c) you type terraform on a new machine and the terminal responds command not found; (d) you try to delete a coworker's file and the system stops you; (e) your test server will not start because "something is already listening" on that port.
See solution
(a) Permissions: the file exists; what is missing is authorization to execute it. (b) Processes: there is a live program holding a resource (the port) your new process needs. (c) Environment: the shell did not find any program with that name in the places it knows to look; it might not even be installed, or it might be somewhere your shell does not know about yet. (d) Permissions: it is the same question as the first case — who can do what with a file — applied to deleting instead of executing. (e) Processes: it is the same symptom as the second case, worded differently. This works because each family answers a different question — who is allowed? (permissions), what is running? (processes), where does the shell look? (environment) — and it is enough to identify which question matches the symptom to classify it, with no need for the command that solves it yet.
2. A teammate installs a library with sudo npm install -g something. It works for them that same day. Three weeks later, without using sudo, a different npm install -g fails with EACCES. Explain, in your own words, the full chain: why the first command "worked" and why the second, with no apparent connection, failed.
See solution
sudo npm install -g something ran as superuser, so when npm created or modified folders inside its global installation directory, those folders ended up owned by root instead of by the normal user. That day there was no visible symptom: the package installed and that was it. Three weeks later, an npm install -g run without sudo — the correct way to use it — tries to write into those same folders, and since they now belong to root, the normal user has no permission: hence the EACCES. The problem did not appear out of nowhere; it was planted three weeks earlier, the instant sudo got used the first time. This works because it connects today's error to its real cause — a silent ownership change, not a package-manager bug — which is exactly the mechanism this lesson describes for sudo.
3. You see this message on your terminal, on a machine you just inherited from a coworker: zsh: permission denied: ./run-tests.sh. Without running any kind of chmod yet: which family does it belong to? What is the first thing you should not do? And what do you need to confirm before fixing it, even though you do not know the exact command yet?
See solution
It belongs to the permissions family: the shell found the file (if it did not exist, the error would be no such file or directory, not permission denied), but it will not let you run it. The first thing you should not do is chmod 777 run-tests.sh: it solves the symptom by granting execute permission to anyone with an account on that machine, far more than is needed. Before fixing it with precision — that is exactly lesson 3 — it is worth confirming who currently owns the file and what minimal permission (probably just execute for the owner) solves the problem without opening more than necessary. This works because the message itself distinguishes "does not exist" from "exists but not authorized" — they are two different errors with two different texts — and that distinction is what separates an environment problem from a permissions one.
4. Mark each statement true or false and correct the false ones in one sentence: (a) Permission denied means the file does not exist; (b) command not found and Permission denied are the same problem family; (c) sudo can make an error that had nothing to do with permissions "disappear"; (d) restarting the computer identifies which process was holding a port.
See solution
(a) False: if the file did not exist, the message would be different (something like no such file or directory); Permission denied means the file exists but you are not authorized for the action you requested. (b) False: command not found is an environment problem (the shell did not locate the program); Permission denied is a permissions problem (the system did find the file, but did not authorize the action). (c) True: sudo completely bypasses the normal permissions check, so errors that were not about permissions sometimes disappear anyway, for a reason entirely different from what one assumes. (d) False: restarting ends the process — along with every other one — but does not tell you which one it was or why it showed up; it diagnoses nothing. This works because each correction forces you to distinguish the exact message the system shows in each case, which is the only clue available before learning the diagnostic commands in the lessons ahead.
Summary and next step
You now have the module's full map: three problem families — permissions, processes, environment — each with its own question (who is allowed? what is running? where does the shell look?), and a criterion for recognizing which is which just by reading the error message. You also know why blind sudo, spare chmod 777, and restarting with no diagnosis seem to work, and why that occasional success is exactly what makes them dangerous as a first reflex.
Before moving on you should be able to: classify a new error message — one you did not see in this lesson — into one of the three families, just by reading its text; explain, using the sudo npm install -g example, why a home remedy can plant a problem that shows up weeks later; and name in one sentence what this module's project builds and why it is called a "clinic."
This lesson gave you the map; it has not given you a single real diagnostic command yet. That starts in the next lesson, where you are going to meet the Unix user model — why your laptop, even though only you use it, behaves as if it were a server with tenants — and you are going to understand, before touching a single permission, why something exists for a permission to protect in the first place.
Resources
- A Short Introduction to Sudo — official documentation for the sudo project: what problem it solves and what security policy it implements.
- Resolving EACCES permissions errors when installing packages globally — npm's official page documenting the exact problem from Exercise 2 and why it recommends not using
sudoto install packages. - Command Search and Execution — Bash Reference Manual — GNU's official reference on how bash searches for a command before reporting
command not found; you are going to need it in full in lesson 6. - errno — Standard errno system symbols — Python's official documentation on operating system error codes, including
EADDRINUSE(48 on macOS, 98 on Linux) you saw in Symptom 3. - Change the default shell in Terminal on Mac — Apple Support — Apple's official reference on zsh as the default shell, relevant for the message differences you saw in this lesson.