Linux & Bash · Part I · Module 2 · The Filesystem and Paths
The Working Directory, and the Shorthands . .. and ~
Chapter 2.2 left one piece unexplained: the working directory that completes every relative path. This chapter makes it precise, then adds the three shorthands that real paths are full of. Two of them are genuine directories that the filesystem stores and you can list. The third is not a directory at all — it is text that Bash swaps out before the command ever runs. That difference sounds like trivia and is not: it is what explains why a script sitting in front of you refuses to run until you put two extra characters in front of its name.
Foundation16 min readLinuxBashPart IFundamentalsPaths
Part I · Module 2 · Chapter 2.3 · The Working Directory, and the Shorthands
1. The Working Directory Belongs to a Process
Chapter 1.7 established that a process is a running program with its own state, and that a child gets a copy of some of that state. One of the things in that state is the working directory.
So, precisely:
- every process has exactly one working directory;
- your shell is a process, so your shell has one;
- a program your shell starts is a different process, and it starts with a copy of your shell's;
- changing the copy does not change yours.
That last point is Chapter 1.7's rule, and it is why cd had to be a builtin. Nothing new is being claimed here — the same fact is simply being applied to the thing it was always about.
pwd from Chapter 1.4 asks the shell for its own value:
$ pwd
/home/engineer/projectThe value is always an absolute path. There is no such thing as being "relatively located".
2. . — Here
A single dot means this directory — whichever directory the path is being resolved from.
.
./alu.sv
./rtlApplying Chapter 2.2's rule from /home/engineer/project:
/home/engineer/project + . → /home/engineer/project
/home/engineer/project + ./rtl → /home/engineer/project/rtlSo ./rtl and rtl name exactly the same directory. The dot adds nothing to the meaning.
Which raises a reasonable question: if it adds nothing, why does it exist? Two reasons, and §7 is about the second.
The first is that it gives you a way to name the current directory, rather than merely resolve from it. Some commands need a location as an argument, and "here" has to be spellable:
$ pwd
/home/engineer/project
$ ls .
docs rtl scripts sim3. .. — The Parent
Two dots mean the parent directory — the directory that contains this one, in Chapter 2.1's sense.
From /home/engineer/project/rtl:
/home/engineer/project/rtl + .. → /home/engineer/projectTwo dots can be used in the middle of a longer path, and that is where they earn their keep:
/home/engineer/project/sim + ../rtl → /home/engineer/project/rtlRead that in two steps: .. moves up from sim to project, then rtl moves down into rtl. Figure 1 walks it one step at a time.
Chaining upward
.. can be repeated. Each one moves up one more level.
From /home/engineer/project/sim/logs:
.. → /home/engineer/project/sim
../.. → /home/engineer/project
../../.. → /home/engineer
../../../.. → /homeThe reliable way to read a path full of two-dot steps is to count them, walk up that many levels from your working directory, then read the rest of the path downward. Resist doing it in your head in one jump; counting is faster than guessing and far more accurate.
What is above the root?
A fair question with a tidy answer.
The root has no parent. Rather than being an error, .. in the root points at the root itself. So this is harmless:
/ + .. → /And it means you cannot walk out of the top of the tree by adding more two-dot steps. ../../../../../.. from anywhere eventually arrives at / and stays there.
4. These Two Are Real
Here is the fact that most introductions skip.
. and .. are not notation. They are actual entries that exist in every directory. The filesystem stores them. They are not something the shell invents and substitutes.
You can see them. ls -a shows entries that ls normally hides, and Chapter 2.6 is entirely about why they are hidden:
$ pwd
/home/engineer/project
$ ls -a
. .. docs rtl scripts simThe first two entries in that output are the two shorthands, sitting in the listing alongside the ordinary directories. Every directory on the machine has both.
This is why .. works identically in every program, not just in Bash. A text editor, a compiler, a simulator — all of them resolve .. the same way, because they are not interpreting a shell convention. They are looking up a directory entry that is genuinely there.
5. ~ — Your Home, and It Is Different
A tilde means your home directory.
~ → /home/engineer
~/project → /home/engineer/project
~/project/rtl → /home/engineer/project/rtlConvenient, and used constantly. But the mechanism is completely different from the previous two, and the difference is the point of this section.
~ is not a directory. There is no entry called ~ anywhere in the filesystem. Nothing in ls -a will ever show one.
What happens instead is Chapter 1.2's step 2. Before the command runs, Bash replaces the tilde with the text of your home directory path. By the time the program starts, there is no tilde left anywhere — the program receives /home/engineer/project and has no idea a shortcut was used.
$ echo ~
/home/engineer
$ echo ~/project
/home/engineer/projectecho prints the words it is given. It printed full paths, because full paths are what it was given. The tilde was gone before echo existed.
6. The Shorthands Side by Side
| Written | Means | Who handles it | Real directory entry? |
|---|---|---|---|
/ | the root | the filesystem | yes |
. | this directory | the filesystem | yes |
.. | the parent directory | the filesystem | yes |
~ | your home directory | Bash, before the command runs | no |
That fourth column is the one to remember. Three of these are places; one is a substitution.
7. Why ./script.sh Needs the Dot
Now the question this chapter exists to answer.
Put an executable script in a directory and try to run it by name:
$ pwd
/home/engineer/project/scripts
$ ls
run.sh
$ run.sh
bash: run.sh: command not foundThe file is right there. ls just listed it. And yet the shell says it cannot find a command by that name.
Read the error with Chapter 1.3's rule first: the prefix is bash:, so nothing ran. This is the shell reporting, not a program.
The explanation comes from Chapter 1.7. When you type a bare word, the shell does not treat it as a path — it treats it as a command name, and looks that name up: an alias, a function, a builtin, and then a file found by searching a list of directories.
That list does not include your current directory. So the shell searched the places it searches, did not find anything called run.sh, and reported exactly that.
The fix is to stop giving a name and start giving a path:
$ ./run.sh./run.sh contains a slash. That is the signal: a word with a slash in it is a path, not a name to look up. The shell skips the whole search and goes straight to the file, resolving ./run.sh against the working directory exactly as Chapter 2.2 described.
So the dot is not magic punctuation. It is the cheapest way to turn a name into a path, because . is a real directory and adding it forces a slash into the word.
$ ./run.sh # a path — runs the file here
$ /home/engineer/project/scripts/run.sh # also a path — same file
$ run.sh # a name — searched for, not foundWhy is the current directory excluded from the search? Briefly, because including it is dangerous: a file dropped into a directory you happen to be in could be run by accident simply because its name matched something you meant to type. Requiring ./ makes running a local program a deliberate act. The search list itself — what it contains and how to change it — is Module 10's subject, and it is deliberately not opened here.
8. Putting the Shorthands Together
A few realistic combinations, all resolved from /home/engineer/project/sim:
. → /home/engineer/project/sim
./logs → /home/engineer/project/sim/logs
.. → /home/engineer/project
../rtl → /home/engineer/project/rtl
../rtl/alu.sv → /home/engineer/project/rtl/alu.sv
../.. → /home/engineer
../../project → /home/engineer/project
~ → /home/engineer
~/project/docs → /home/engineer/project/docs
/etc → /etcTwo rows in that list are worth comparing directly.
../../project and ~/project arrive at the same place here, by different routes: one counts upward from where you are standing, the other starts from your home directory regardless of where you are. If you move somewhere else, the first changes meaning and the second does not — Chapter 2.2's distinction, now wearing shorthand.
And the last row is a reminder: /etc starts with a slash, so none of this applies. It is absolute, and your working directory is irrelevant to it.
9. Misconceptions
"~ is a directory." It is text the shell replaces. No entry named ~ exists. . and .. genuinely do exist, in every directory.
"~ means the root." It means your home directory, which is several levels below the root. This is Chapter 2.1's warning in its most common form. / is the top of the machine; ~ is your corner of it.
". and .. are the same kind of thing as ~, just shorter." They are resolved by different machinery at different times, as §5 showed.
"A single dot does nothing, so ./ is pointless." It does nothing to the meaning of a path. It does something decisive to how the shell classifies the word — §7.
"cd .. goes back to where I was." It goes up to the parent, which is often not where you came from. If you reached a directory by an absolute path from somewhere unrelated, .. has no idea about that history. Chapter 2.4 introduces cd -, which does mean "back to where I was".
"Two dots inside a path are evaluated by Bash." They are not. Bash passes them along; the filesystem resolves them when the program looks the path up. One observable consequence appears once symbolic links exist, which is Module 6.
10. Prediction Practice
Use the module's tree:
/home/engineer/project
├── docs
├── rtl
│ ├── alu.sv
│ └── counter.sv
├── scripts
│ └── run.sh
└── sim
└── logsAssume the home directory is /home/engineer. Resolve each one before reading on.
| Working directory | You write | Resolves to? |
|---|---|---|
/home/engineer/project/rtl | .. | ? |
/home/engineer/project/rtl | ../sim/logs | ? |
/home/engineer/project/sim/logs | ../../rtl/alu.sv | ? |
/home/engineer/project/docs | ./../scripts/run.sh | ? |
/tmp | ~/project/rtl | ? |
/home/engineer/project | ../../.. | ? |
Answers:
/home/engineer/project./home/engineer/project/sim/logs— up toproject, then down two./home/engineer/project/rtl/alu.sv— count the two-dot steps:logs→sim→project, then down intortl./home/engineer/project/scripts/run.sh. The leading./contributes nothing to the meaning;..does the work./home/engineer/project/rtl. The tilde was replaced by Bash first, so the working directory/tmpplayed no part at all./— up toengineer, up tohome, up to the root. A further..would stay at the root, per §3.
Row 5 is the one that catches people. A path beginning with ~ becomes an absolute path before the command runs, so it behaves like /etc and not like ../rtl.
11. Check Your Understanding
Question 1. Which of ., .. and ~ exist as real entries in a directory, and which one does not? Who handles the one that does not?
Question 2. You are in /home/engineer/project/sim. Write two different paths that both name /home/engineer/project/rtl/counter.sv — one using .., one using ~.
Question 3. run.sh gives bash: run.sh: command not found, but ls shows the file. Explain in two sentences what the shell did, and give the fix.
Question 4. You run ./run.sh and get Permission denied instead of command not found. What does that change tell you about which problem you now have?
Question 5. Why does cd .. not necessarily take you back to the directory you were in a moment ago?
Question 6. From /, what does ../../.. name? Why is that not an error?
12. Remember
- Every process has one working directory, always an absolute path. Your shell has one; a program it starts gets a copy and cannot change yours.
.is this directory and..is the parent — and both are real entries that every directory contains.ls -ashows them.- Count the two-dot steps, walk up that many levels, then read the rest downward. That method does not fail; guessing does.
..in the root points at the root, so you cannot walk off the top of the tree.~is not a directory. Bash replaces it with your home path before the command runs, which is why a tilde path behaves like an absolute one.- A word containing a slash is a path; a bare word is a command name. The current directory is not in the list of places searched for command names.
./run.shexists to add that slash — it turns a name into a path. Whether the file then runs is a permissions question, and a different error message.command not foundandPermission deniedare different stages. The first means the file was never located; the second means it was.
You can now write any path you need. The next chapter picks up the three commands that let you move through the tree and confirm, every time, that you landed where you intended.
Where this fits
Part of the Linux & Bash curriculum.
