Chapter 4

Directories

Having examined many of the Unix utilities for dealing with files, the time has come to learn about directories, sometimes known by the synonym folders (Figure 4.1). As we’ll see, many of the ideas developed in the context of files also apply to directories, but there are many differences as well.

Image

Figure 4.1: The correspondence between folders & directories.

4.1 Directory Structure

The structure of Unix-style directories is typically indicated using a list of directory names separated by forward slashes, which we can combine with the ls command (Section 2.2) like this:

$ ls /Users/mhartl/ruby

or like this:

$ ls /usr/local/bin

As seen in Figure 4.1, these representations correspond to directories in a hierarchical filesystem, with (say) mhartl a subdirectory of Users and ruby a subdirectory of mhartl.

Conventions vary when speaking about directories: a user directory like /Users/mhartl would probably be read as “slash users slash mhartl” or “slash users mhartl”, whereas omitting the initial slash in spoken language is common for system directories such as /usr/local/bin, which would probably be pronounced “user local bin”.1 Because all Unix directories are ultimately subdirectories of the root directory / (pronounced “slash”), the leading and separator slashes are implied. Note: Referring to forward slashes incorrectly as “backslashes” is a source of intense suffering, and should be strictly avoided.

1. For more on Unix system directories, see “What is /usr/local/bin?” (https://unix.stackexchange.com/questions/4186/what-is-usr-local-bin) at the Unix StackExchange. Thanks to reader Joost van der Linden for the suggestion.

The most important directory for a particular user is the home directory, which on my macOS system is /Users/mhartl, corresponding to my username (mhartl). The home directory can be specified as an absolute path, as in /Users/mhartl, or using the shorthand for the home directory, the tilde character ~ (which is typed using Shift-backtick, located just to the left of the number 1 on most keyboards). As a result, the two paths shown in Figure 4.1 are identical: /Users/mhartl/ruby/projects is the same as ~/ruby/projects. (Amusingly, the reason the tilde character is used for the home directory is simply because the “Home” key was the same as the key for producing “~” on some early keyboards.)

In addition to user directories, every Unix system has system directories for the programs essential for the computer’s normal operation. Modifying system files or directories requires special powers granted only to the superuser, known as root. (This use of “root” is unrelated to the “root directory” mentioned above.) The superuser is so powerful that it’s considered bad form to log in as root; instead, tasks executed as root should typically use the sudo command (Box 4.1).

4.1.1 Exercises

  1. Write in words how you might speak the directory ~/foo/bar.

  2. In /Users/bill/sonnets, what is the home directory? What is the username? Which directory is deepest in the hierarchy?

  3. For a user with username bill, how do /Users/bill/sonnets and ~/sonnets differ (if at all)?

4.2 Making Directories

So far in this tutorial, we’ve created (and removed) a large number of text files. The time has finally come to make a directory to contain them. Although most modern operating systems include a graphical interface for this task, the Unix way to do it is with mkdir (short for “make directory”, per Figure 2.4):

$ mkdir text_files

Having made the directory, we can move the text files there using a wildcard:

$ mv *.txt text_files/

We can confirm the move worked by listing the directory:

$ ls text_files/
sonnet_1.txt    sonnet_1_reversed.txt sonnets.txt

(Depending on how closely you’ve followed this tutorial, your results may vary.)

By default, running ls on a directory shows its contents, but we can show just the directory using the -d option:

$ ls -d text_files/
text_files/

This usage is especially common with the -l option (Section 2.2):

$ ls -ld text_files/
drwxr-xr-x 7 mhartl staff 238 Jul 24 18:07 text_files

Finally, we can change directories using cd:

$ cd text_files/

Note that cd typically supports tab completion, so (as described in Box 2.4) we can actually type cd tex images.

After running cd, we can confirm that we’re in the correct directory using the “print working directory” command, pwd, together with another call to ls:

$ pwd
/Users/mhartl/text_files
$ ls
sonnet_1.txt     sonnet_1_reversed.txt sonnets.txt

These last steps of typing pwd to double-check the directory, and especially running ls to inspect the directory contents, are a matter of habit for many a grizzled command-line veteran. (Your result for pwd will, of course, be different, unless you happen to be using the username “mhartl” on macOS.)

4.2.1 Exercises

  1. What is the option for making intermediate directories as required, so that you can create, e.g., ~/foo and ~/foo/bar with a single command? Hint: Refer to the man page for mkdir.

  2. Use the option from the previous exercise to make the directory foo and, within it, the directory bar (i.e., ~/foo/bar) with a single command.

  3. By piping the output of ls to grep, list everything in the home directory that contains the letter “o”.

4.3 Navigating Directories

We saw in Section 4.2 how to use cd to change to a directory with a given name. This is one of the most common ways of navigating, but there are a couple of special forms worth knowing. The first is changing to the directory one level up in the hierarchy using cd .. (read “see-dee dot-dot”):

$ pwd
/Users/mhartl/text_files
$ cd ..
$ pwd
/Users/mhartl

In this case, because /Users/mhartl is my home directory, we could have accomplished the same thing using cd by itself:

$ cd text_files/
$ pwd
/Users/mhartl/text_files
$ cd
$ pwd
/Users/mhartl

The reason this works is that cd by itself changes to the user’s home directory, wherever that is. This means that

$ cd

and

$ cd ~

are equivalent.

When changing directories, it’s frequently useful to be able to specify the home directory somehow. For example, suppose we make a second directory and cd into it:

$ pwd
/Users/mhartl
$ mkdir second_directory
$ cd second_directory/

Now if we want to change to the text_files directory, we can cd to text_files via the home directory ~:

$ pwd
/Users/mhartl/second_directory
$ cd ~/text_files
$ pwd
/Users/mhartl/text_files

Incidentally, we’re now in a position to understand the prompts shown in Figure 1.6: I have my prompt configured to show the current directory, which might be something like [~], [ruby], or [projects]. (We’ll discuss how to customize the prompt in Part II (Section 6.7.1). Especially eager readers can exercise their technical sophistication (Box 1.4) by Googling for how to do it.)

Closely related to .. for “one directory up” is . (read “dot”) which means “the current directory”. The most common use of . is when moving or copying files to the current directory:

$ pwd
/Users/mhartl/text_files
$ cd ~/second_directory
$ ls
$ cp ~/text_files/sonnets.txt .
$ ls
sonnets.txt

Note that the first call to ls returns nothing, because second_directory is initially empty.

Another common use of . is in conjunction with the find command, which like grep is incredibly powerful, but in my own use it looks like this 99% of the time:

$ cd
$ find . -name '*.txt'
./text_files/sonnet_1.txt
./text_files/sonnet_1_reversed.txt
./text_files/sonnets.txt

In words, what this does is find files whose names match the pattern *.txt, starting in the current directory . and then in its subdirectories.2 The find utility is incredibly useful for finding a misplaced file at the command line.

2. My directory has a huge number of text files, ’cause that’s just how I roll, so the command I ran was actually find . -name '*.txt' | grep text_files, which filters out anything that doesn’t match the directory being used in this tutorial.

Perhaps my favorite use of . is in “open dot”, which will work only on macOS:

$ cd ~/ruby/projects
$ open .

The remarkable open command opens its argument using whatever the default program is for the given file or directory. (A similar command, xdg-open, works on some Linux systems.) For example, open foo.pdf would open the PDF file with the default viewer (which is Preview on most Macs). In the case of a directory such as ., that default program is the Finder, so open . produces a result like that shown in Figure 4.1.

A final navigational command, and one of my personal favorites, is cd -, which cds to the previous directory, wherever it was:

$ pwd
/Users/mhartl/second_directory
$ cd ~/text_files
$ pwd
/Users/mhartl/text_files
$ cd -
/Users/mhartl/second_directory

I find that cd - is especially useful when combining commands, as described in Box 4.2.

4.3.1 Exercises

  1. How do the effects of cd and cd ~ differ (or do they)?

  2. Change to text_files, then change to second_directory using the “one directory up” double-dot operator ...

  3. From wherever you are, create an empty file called nil in text_files using whatever method you wish.

  4. Remove nil from the previous exercise using a different path from the one you used before. (In other words, if you used the path ~/text_files before, use something like ../text_files or /Users/<username>/text_files.)

4.4 Renaming, Copying, and Deleting Directories

The commands for renaming, copying, and deleting directories are similar to those for files (Section 2.3), but there are some subtle differences worth noting. The command with the least difference is mv, which works just as it does for files:

$ mkdir foo
$ mv foo/ bar/
$ cd foo/
-bash: cd: foo: No such file or directory
$ cd bar/

Here the error message indicates that the mv worked—there is no file or directory called foo:

$ cd foo/
-bash: cd: foo: No such file or directory

(The word bash refers to the name of the particular shell program being run, which in this case is the “Bourne Again SHell”.) The only minor subtlety is that the trailing slashes (which are typically added automatically by tab completion (Box 2.4)) are optional:

$ cd
$ mv bar foo
$ cd foo/

This issue with trailing slashes never makes a difference with mv, but with cp it can be a source of much confusion. In particular, when copying directories, the behavior you usually want is to copy the directory contents including the directory, which on many systems requires leaving off the trailing slash. When copying files, you also need to include the -r option (for “recursive”). For example, to copy the contents of the text_files directory to a new directory called foobar, we use the command shown in Listing 4.1.

Listing 4.1: Copying a directory.

$ cd
$ mkdir foobar
$ cd foobar/
$ cp -r ../text_files .
$ ls
text_files

Note that we’ve used .. to make a relative path, going up one directory and then into text_files. Also note the lack of a trailing slash in cp -r ../text_files .; if we included it, we’d get Listing 4.2 instead.

Listing 4.2: Copying with a trailing slash.

$ cp -r ../text_files/ .
$ ls
sonnet_1.txt     sonnet_1_reversed.txt sonnets.txt   text_files

In other words, Listing 4.2 copies the individual files, but not the directory itself. As a result, I recommend always omitting the trailing slash, as in Listing 4.1; if you want to copy only the files, be explicit using the star operator, as in:

$ cp ../text_files/* .

Unlike renaming (moving) and copying directories, which use the same mv and cp commands used for files, in the case of removing directories there’s a dedicated command called rmdir. In my experience, though, it rarely works, as seen here:

$ cd
$ rmdir second_directory
rmdir: second_directory/: Directory not empty

The error message here is what happens 99% of the time when I try to remove directories, because rmdir requires the directory to be empty. You can of course empty it by hand (using rm repeatedly), but this is frequently inconvenient, and I almost always use the more powerful (but much more dangerous) “remove recursive force” command rm -rf, which removes a directory, its files, and any subdirectories without confirmation (Listing 4.3).

Listing 4.3: Using rm -rf to remove a directory.

$ rm -rf second_directory/
$ ls second_directory
ls: second_directory: No such file or directory

As the error message from ls in Listing 4.3 indicates (“No such file or directory”), our use of rm -rf succeeded in removing the directory.

The powerful command rm -rf is too convenient to ignore, but remember: “With great power comes great responsibility” (Figure 4.2).3

3. Image courtesy of MeskPhotography/Shutterstock.

Image

Figure 4.2: This superhero understands how to use the power of rm -rf responsibly.

4.4.1 Grep Redux

Now that we know a little about directories, we are in a position to add a useful grep variation to our toolkit from Section 3.4. As with cp and rm, grep takes a “recursive” option, -r, which in this case greps through a directory’s files and the files in its subdirectories. This is incredibly useful when you’re looking for a string in a file somewhere in a hierarchy of directories, but you’re not sure where the file is. Here’s the setup, which puts the word “sesquipedalian” in a file called long_word.txt:

$ cd text_files/
$ mkdir foo
$ cd foo/
$ echo sesquipedalian > long_word.txt
$ cd

The final cd puts us back in the home directory. Suppose we now want to find the file containing “sesquipedalian”. The way not to do it is this:

$ grep sesquipedalian text_files     # This doesn't work.
grep: text_files: Is a directory

Here grep’s error message indicates that the command didn’t work, but adding -r does the trick:

$ grep -r sesquipedalian text_files
text_files/foo/long_word.txt:sesquipedalian

Because we don’t usually care about case when searching files, I recommend making a habit of adding the -i option when grepping recursively, as follows:

$ grep -ri sesquipedalian text_files
text_files/foo/long_word.txt:sesquipedalian

Armed with grep -ri, we are now equipped to find strings of our choice in arbitrarily deep hierarchies of directories.

4.4.2 Exercises

  1. Make a directory foo with a subdirectory bar, then rename the subdirectory to baz.

  2. Copy all the files in text_files, with directory, into foo.

  3. Copy all the files in text_files, without directory, into bar.

  4. Remove foo and everything in it using a single command.

4.5 Summary

Important commands from this chapter are summarized in Table 4.1.

Table 4.1: Important commands from Chapter 4.

Command

Description

Example

mkdir <name>

Make directory with name

$ mkdir foo

pwd

Print working directory

$ pwd

cd <dir>

Change to <dir>

$ cd foo/

cd ~/<dir>

cd relative to home

$ cd ~/foo/

cd

Change to home directory

$ cd

cd -

Change to previous directory

$ cd && pwd && cd -

.

The current directory

$ cp ~/foo.txt .

..

One directory up

$ cd ..

find

Find files & directories

$ find . -name foo*.*

cp -r <old> <new>

Copy recursively

$ cp -r ~/foo .

rmdir <dir>

Remove (empty) dir

$ rmdir foo/

rm -rf <dir>

Remove dir & contents

$ rm -rf foo/

grep -ri <string> <dir>

Grep recursively (case-insensitive)

$ grep -ri foo bar/

4.5.1 Exercises

  1. Starting in your home directory, execute a single command-line command to make a directory foo, change into it, create a file bar with content “baz”, print out bar’s contents, and then cd back to the directory you came from. Hint: Combine the commands as described in Box 4.2.

  2. What happens when you run the previous command again? How many of the commands executed? Why?

  3. Explain why the command rm -rf / is unbelievably dangerous, and why you should never type it into a terminal window, not even as a joke.

  4. How can the previous command be made even more dangerous? Hint: Refer to Box 4.1. (This command is so dangerous you shouldn’t even think it, much less type it.)

4.6 Conclusion

Congratulations! You’ve officially learned enough command line to be dangerous. Of course, this is only one step on a longer journey, both toward command-line excellence (Box 4.3) and software development wizardry. As you proceed on this journey, you will probably discover that learning computer magic can be exciting and empowering, but it can also be hard. Indeed, you may already have discovered this fact, either on your own or while following Part I of Learn Enough Developer Tools to Be Dangerous. To those brave magicians-in-training who wish to proceed, I offer the following sequence:

  1. Learn Enough Developer Tools to Be Dangerous

    1. Part I: Learn Enough Command Line to Be Dangerous (https://www.learnenough.com/command-line) (you are here)

    2. Part II: Learn Enough Text Editor to Be Dangerous (https://www.learnenough.com/text-editor)

    3. Part III: Learn Enough Git to Be Dangerous (https://www.learnenough.com/git)

  2. Web Basics

    1. Learn Enough HTML to Be Dangerous (https://www.learnenough.com/html)

    2. Learn Enough CSS & Layout to Be Dangerous (https://www.learnenough.com/css-and-layout)

    3. Learn Enough JavaScript to Be Dangerous (https://www.learnenough.com/javascript)

  3. Application Development

    1. Learn Enough Ruby to Be Dangerous (https://www.learnenough.com/ruby)

    2. Ruby on Rails Tutorial (https://www.railstutorial.org/)

    3. Learn Enough Action Cable to Be Dangerous (https://www.learnenough.com/action-cable) (optional)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.76.250