Twenty-ish Terminal Commands for Getting Around the File System

Recently I was digging around in the terminal and wanted an ls command that behaved like a fully opened tree view. That led me back through a bunch of the file-system commands I use all the time, along with a few switches that make them far more useful.

This is the resulting quick reference. I’m writing this on macOS with zsh, but most of it applies directly to Linux too. A few tools—particularly tree and fd—may need to be installed first.

First, the Many Faces of ls

The plain command lists the current directory.

ls

Add -l for the vertical, long-listing format. This shows permissions, link count, owner, group, size, modified date, and name.

ls -l

Add -a to include hidden entries—names beginning with a dot—and -h to make sizes human-readable. The switches can be combined.

ls -lah

To descend through every directory and list its contents, add -R for recursive.

ls -laRh

That gets us the fully opened contents, although the output is grouped by directory rather than drawn as a visual tree.

Navigation

1. pwd — Where Am I?

When I’ve wandered six directories deep and forgotten where I am, pwd prints the full path to the working directory.

pwd

2. cd — Change Directory

Move into a directory, up one level, back to the previous directory, or straight home.

cd projects
cd ..
cd -
cd ~

Paths containing spaces need quotes, as in cd "My Projects".

3. pushd and popd — Directory Bookmarks, Sort Of

pushd changes directories while saving the current location on a stack. popd takes me back. This is excellent when bouncing between two distant parts of a repository.

pushd ~/Code/my-project/docs
popd

4. tree — The Actual Tree View

Unlike recursive ls, tree draws the hierarchy. -a includes hidden entries, -L 2 limits output to two levels, and -h prints readable sizes.

tree -a -h -L 2

On macOS, install it with brew install tree if it is not already available.

Finding and Inspecting Things

5. find — Search the Directory Tree

Find every Markdown file below the current directory. Here . means “start here,” -type f limits results to files, and -name supplies the filename pattern.

find . -type f -name "*.md"

Use -type d to find directories instead.

6. fd — A Friendlier Find

fd provides a concise, fast alternative when installed. This finds Markdown files while including hidden paths but excluding .git.

fd --hidden --exclude .git '\.md$'

Install it on macOS with brew install fd.

7. stat — All the File Details

stat reports metadata such as size, permissions, timestamps, and inode information.

stat quick-article.md

The exact output differs between macOS and Linux, but the intent is the same.

8. file — What Is This Thing?

Extensions can lie. file inspects the contents and reports what it believes the file actually is.

file mysterious-download

9. du — What Is Using the Space?

du measures disk usage. -s summarizes instead of listing everything below the target, and -h makes the result readable.

du -sh .
du -sh ./*

10. df — How Much Disk Is Left?

Where du examines files and directories, df reports free and used space on mounted file systems. Again, -h keeps the numbers readable.

df -h

11. less — Read Without Flooding the Terminal

For a file longer than one screen, use less. Search with /text, advance with the space bar, move backward with b, and quit with q.

less application.log

12. head and tail — Inspect the Edges

These show the beginning or end of a file. -n 20 asks for twenty lines. tail -f keeps watching as new lines arrive, which is particularly handy for logs.

head -n 20 application.log
tail -n 20 application.log
tail -f application.log

Creating and Managing Files

13. mkdir — Create Directories

The useful switch here is -p: it creates missing parent directories and does not complain if the path already exists.

mkdir -p notes/terminal/examples

14. touch — Create an Empty File

If the file does not exist, touch creates it. If it does exist, touch updates its timestamps without changing the contents.

touch notes.md

15. cp — Copy

Copy a file with plain cp. Use -R to copy a directory recursively and -i to ask before overwriting something.

cp -i notes.md notes-backup.md
cp -Ri source-folder destination-folder

16. mv — Move or Rename

mv handles both jobs. I often include -i for an overwrite prompt.

mv -i draft.md published.md
mv -i published.md archive/

17. ln — Create a Link

ln -s creates a symbolic link. The first path is the existing target; the second is the new link.

ln -s ~/Code/my-project/current-config.json config.json

18. rmdir — Remove an Empty Directory

rmdir only removes empty directories. That limitation makes it useful when I want the command to refuse anything containing files.

rmdir old-empty-folder

19. rm — Remove Files Carefully

Plain rm removes files. -i asks before each removal. Recursive -R removes directories and their contents, so double-check the path before pressing Return.

rm -i unwanted.txt
rm -Ri unwanted-folder

There generally is no built-in undo. On macOS, moving something to the Trash in Finder is often a better choice when I’m not absolutely sure.

20. open — Hand It to macOS

open opens a file in its default application. Give it a directory and Finder opens there; use -a to choose an application.

open README.md
open .
open -a "Visual Studio Code" .

On Linux, the closest general equivalent is usually xdg-open.

A Few Combinations I Actually Use

Here are the quick combinations I tend to reach for most often.

# Everything here, including hidden entries, with readable details.
ls -lah

# Everything below here, recursively, in long format.
ls -laRh

# A manageable visual overview, two levels deep.
tree -a -L 2

# Find the biggest immediate entries in the current directory.
du -sh ./* | sort -h

# Jump somewhere temporarily, inspect it, and jump back.
pushd ~/Code/some-project
ls -lah
popd

That’s the lot: twenty-ish commands and a pile of switches that turn the terminal into a quick file-system navigator. The commands themselves are only half of the story. Run man ls, man find, or man followed by any of the built-in commands above to spelunk through everything else they can do.

Documentation First w/ README.md && Project Tree Build

I’m sitting here trying to get the folder structure for my project into a kind of ASCII Tree or something. I wasn’t going to manually do this, it would be insane. Especially on any decent size Enterprise Project with an endless supply of folders and nested content. I went digging to come up with a better solution. On Linux I immediately found the Tree Utility which was perfect.

Except I was on OS-X.

First option I gave a go to was to build the thing. Because I like to do things the hard way sometimes. First I needed to get the source, which is available here.

[sourcecode language=”bash”]curl -O ftp://mama.indstate.edu/linux/tree/tree-1.7.0.tgz[/sourcecode]

Once downloaded, unzip the source into a directory and find the following section for the particular operating system you want to use the utility on. The section for OS settings looked like this when I finished editing it.

[sourcecode language=”bash”]
# Uncomment options below for your particular OS:

# Uncomment for OS X:
CC=cc
CFLAGS=-O2 -Wall -fomit-frame-pointer -no-cpp-precomp
LDFLAGS=
MANDIR=/usr/share/man/man1
OBJS+=strverscmp.o
[/sourcecode]

Now get a good build of the command file.

[sourcecode language=”bash”]
./configure
make
[/sourcecode]

Now let’s get tree into the executable path.

[sourcecode language=”bash”]
sudo mkdir -p /usr/local/bin
sudo cp tree /usr/local/bin/tree
[/sourcecode]

Make sure your ~/.bash_profile is setup right, include this.

[sourcecode language=”bash”]
export PATH="/usr/local/bin:$PATH"
[/sourcecode]

Reload the shell and tree should be available as a command.

The other option which is really simple, if you don’t want to compile to code, is to just use brew to install it.

[sourcecode language=”bash”]
brew install tree
[/sourcecode]

So now you can use tree, and do cool stuff like pipe it out to a file. If you’re running this against a Node.js Project you may want to delete the node_modules directory and then just reinstall it after running the tree command.

[sourcecode language=”bash”]
tree > prof-tree.md
[/sourcecode]

Then in your README.md file you can include the folder structure in the description of the project. Here’s a sample output!

[sourcecode language=”bash”]
.
├── README.md
├── client
│   └── README.md
├── package.json
├── proj-tree.md
├── server
│   ├── boot
│   │   ├── authentication.js
│   │   ├── explorer.js
│   │   ├── rest-api.js
│   │   └── root.js
│   ├── config.json
│   ├── datasources.json
│   ├── middleware.json
│   ├── model-config.json
│   └── server.js
└── test
└── test_exists.js

4 directories, 14 files
[/sourcecode]

That’s a super easy way to offer better documentation that provides some real insight into what various parts of the project structure are actually for.