Course outline
Course / The terminal and the language

Linux and the Terminal

Reading

The idea

Picture a giant filing cabinet. Each drawer is a directory (a folder that holds files or other folders). Drawers hold smaller folders, which hold more folders, all the way down to the papers: your files. The very front of the cabinet is the root, written /. An absolute path is the full chain of drawers from the front of the cabinet to one paper: /home/you/notes/today.txt means front, then home, then you, then notes, then the paper today.txt. A relative path starts from the drawer you already have open, so notes/today.txt means "the today.txt in the notes drawer right here." Three shortcuts: . is the open drawer, .. is the drawer that holds it, and ~ is your home drawer.

The terminal is a control room for that cabinet. Instead of clicking icons, you type a short written order, called a command, press Enter, and the computer carries it out. That sounds slower, but it pays off: a typed order can be repeated, saved in a file, and chained with other orders, and every server you will ever support is run this way.

Setup: on Windows, run wsl --install in PowerShell to install WSL2 (Windows Subsystem for Linux 2), restart, then open the Ubuntu app. On macOS and Linux, just open Terminal. On Windows, keep your course files inside the Linux side, under your home folder (~), and not under /mnt/c/, which is your Windows drive seen from Linux: it is slower and mixes up file permissions. Everything you type in this course runs in a shell, the program inside the terminal that reads your orders. On Ubuntu it is called bash.

Moving around: pwd, ls, and cd

The Go programs you write later will run from this control room, so the orders come first. Three commands tell you where you are and how to move:

pwd          # print working directory: shows your current folder
ls           # list: shows what is in the current folder
cd projects  # change directory: walk into the "projects" drawer
cd ..        # walk up one level, to the parent folder
cd .         # walk nowhere: "." is the drawer you already have open
cd ~         # jump to your home drawer from anywhere
cd -         # jump back to the drawer you were in a moment ago
clear        # wipe the screen clean (Ctrl+L does the same)
exit         # close the terminal session

pwd prints a path such as /home/you. If cd says No such file or directory, the drawer is not in the one you have open, so check with ls.

Two flags (options that change how a command works) make ls more useful:

ls -a   # -a also shows hidden files, whose names start with a dot
ls -lh  # -l is the long listing; -h prints sizes people can read, like 4.0K
ls -R   # -R lists the folder and every folder inside it, level by level

Flags can be glued together: ls -lha means ls -l -h -a.

Making, copying, moving, and deleting

echo prints the text you give it, and cat prints a whole file on the screen:

mkdir notes              # make directory: create a new folder
mkdir -p a/b/c           # -p creates the whole chain of folders in one go
touch todo.txt           # create an empty file named todo.txt
echo "hi" > todo.txt     # echo prints text; with > it puts that text in the file
cat todo.txt             # prints: hi
cp todo.txt backup.txt   # copy: duplicate a file
cp -r notes notes2       # -r copies a folder and everything inside it
mv todo.txt done.txt     # move: rename or relocate a file
rm -i done.txt           # remove: delete a file (-i asks you first)
rmdir notes2             # remove directory: delete an empty folder
rm -r a                  # -r removes a folder and everything inside it

rmdir refuses a folder that has things in it, but rm -r does not. Neither has an undo. cat can also take several files and prints them one after the other. mv and cp overwrite an existing file of the same name without asking, so use cp -i and mv -i when you are unsure. And never run rm -rf / or rm -rf ~: -f (force) removes without asking, and those two erase everything.

If a name contains a space, wrap it in quotes, otherwise the shell sees two separate names:

touch "my notes.txt"   # one file called: my notes.txt
touch my notes.txt     # two files: my and notes.txt

Wildcards

Typing every file name gets old. A wildcard is a pattern the shell fills in with every matching name before the command even runs.

ls *.txt          # * matches any run of characters: a.txt, b.txt, report.txt
ls ?.txt          # ? matches exactly one character: a.txt, b.txt, but not report.txt
ls [ab].txt       # [ab] matches one character from the list: a.txt and b.txt
cp *.txt backup/  # copy every .txt file into the backup folder
rm *.tmp          # delete every .tmp file: check with ls *.tmp first

The shell does the matching, not ls or cp. If you run echo *.txt you see a.txt b.txt, the names the shell filled in. Two things follow. A wildcard only matches names that exist, and a pattern that matches nothing is passed along unchanged. And when a command needs to receive the * itself, such as find . -name "*.log" in the next lesson, you quote it so the shell leaves it alone.

Standard output and standard error

Every command has two places to print: standard output for its normal results and standard error for its complaints. Both show up on your screen, so they look the same, but they are separate streams. > only captures the first one.

ls /nonexistent > out.txt     # the complaint still appears on screen; out.txt stays empty
ls /nonexistent 2> err.txt    # 2> sends the complaint to a file instead
ls /nonexistent 2>/dev/null   # /dev/null is a bin that swallows everything: hide the complaint
ls a.txt /nonexistent > both.txt 2>&1   # 2>&1 sends errors to the same place as normal output

ls: cannot access '/nonexistent': No such file or directory is the complaint here, printed on standard error. 2>/dev/null is how you silence noise you have already decided to ignore, such as Permission denied lines when searching the whole disk. Use it carefully, because it also hides real problems.

Editing files with nano

Almost everything from here on needs you to write text into a file: scripts, notes, programs. echo ... > works for one line, but for real editing you need an editor. nano is a simple one that runs inside the terminal.

nano notes.txt

nano opens the file (creating it if it does not exist) and you just type. The commands are listed along the bottom of the screen, where ^ means the Ctrl key. Press Ctrl+O, then Enter, to save ("write out"). Press Ctrl+X to exit; if there are unsaved changes it asks whether to save them. Ctrl+W searches for text. If you prefer a graphical editor, Visual Studio Code works well with WSL2: from a folder, run code . and it opens that folder.

Write plain text files only, not Word documents. Files saved from Windows Notepad can carry hidden carriage-return characters at the end of every line (written \r), which break scripts in confusing ways, so use nano or VS Code for anything you will run.

Pipes and redirects

A pipe is a conveyor belt from one machine to the next: what the left command prints becomes what the right command reads. A redirect sends that printed output into a file instead of the screen.

ls | sort             # pipe: list files, then sort the lines alphabetically
ls > files.txt        # redirect: overwrite files.txt with the listing
ls >> files.txt       # redirect: append the listing to files.txt instead

sort puts lines in alphabetical order. After the two redirects, files.txt holds the listing once from > and again from >>.

Permissions

Permissions are keys and badges for each door. Every file has an owner, a group, and rules for who may read (r), write (w), or execute (x, run as a program) it. ls -l shows ten characters like -rw-r--r--. The first is the type (- for a file, d for a drawer). Then come three letters each for the owner, the group, and everyone else. So -rw-r--r-- is a file the owner can read and write, and everyone else can only read.

ls -l                 # long listing: permissions, owner, size per file
chmod u+x script.sh   # u = you, the owner; +x = add the execute badge

After chmod u+x, the owner's letters change from rw- to rwx. To run a script file that has the execute badge, put ./ in front of its name, which means "the file right here": ./script.sh. Without the badge, run it through the shell instead: sh script.sh.

Some actions are reserved for the administrator, whom Linux calls root. sudo runs one command with that power, asks for your password (nothing shows as you type), and is how you install software: sudo apt update refreshes Ubuntu's list of available programs, and sudo apt install tree installs one called tree. Only use sudo when a command tells you it needs it, and read the command twice first.

Getting help and working faster

Forgot what a command does? Read its man page (manual page: the built-in instructions).

man ls              # opens the manual for ls; press q to quit
history             # lists the orders you gave earlier; the up arrow recalls them

which ls prints where a program lives on disk, which answers "is this installed?" (it prints nothing when it is not).

The shell's error messages are short but precise. Three you will meet constantly:

  • command not found: you misspelled the command, or the program is not installed.
  • No such file or directory: the path is wrong. Check your spelling, check where you are with pwd, and check the folder with ls.
  • Permission denied: you are not allowed to read, write or run that file. Look at ls -l, and do not reach for sudo unless you understand why.

Two keys save time. Tab completes a name you have started typing. Ctrl+C stops a command that is still running.

Common mistakes

  • rm has no trash can. The file is gone, so check with pwd and ls first, and use rm -i while learning.
  • rm -r deletes a whole folder at once. Read the path twice before pressing Enter.
  • > overwrites the whole file; use >> to add to it.
  • cd .. goes up one level; cd . stays where you are.
  • Linux commands and file names are case-sensitive.
  • Forgetting quotes around a name with a space, so the shell splits it into two names.
  • Running rm * or rm *.txt without first running ls with the same pattern to see exactly what it will hit.
  • Expecting > to capture error messages. Those need 2>.
  • Editing scripts in a word processor instead of nano or VS Code.

Try it

  1. Open your terminal (Windows: the Ubuntu app after WSL2). Run pwd. You see a path starting with /, like /home/you.
  2. Run cd ~, then pwd. You are in your home drawer. Run cd .., then pwd: one level up. Run cd ., then pwd: the same path as before.
  3. Run cd ~, then ls, ls -a, and ls -lh. ls -a adds names starting with a dot. In the long listing, each line starts with permission letters like -rw-r--r-- (a file, where the owner can read and write and everyone else can only read) or drwxr-xr-x (a drawer).
  4. Run mkdir -p practice/a/b, then cd practice/a/b, then pwd. The path now ends in practice/a/b.
  5. Run cd ~/practice, then echo "hello" > hello.txt and cat hello.txt. You see hello.
  6. Run cp hello.txt copy.txt, mv copy.txt renamed.txt, then ls. You see a, hello.txt, and renamed.txt.
  7. Run cp -r a backup, then ls. You now also see backup. Run ls | sort: the same names in alphabetical order.
  8. Run ls > list.txt, then ls >> list.txt, then cat list.txt. The listing appears twice, because >> added to the file.
  9. Run chmod u+x hello.txt, then ls -l. The permission letters for hello.txt now include x.
  10. Run rm -i renamed.txt and answer y. Then run rm -r backup, then ls. Both names are gone.
  11. Run history to see your earlier orders. Type cat with no file name: it waits for typing. Press Ctrl+C to stop it. Type cd ~/pra and press Tab: it completes to practice.
  12. Run man ls, skim it, press q to quit. Then cd ~ to return home.
  13. Run cd ~/practice, then touch a.txt b.txt c.md. Run ls *.txt, ls ?.txt and echo *.md and compare what each prints. Then run mkdir stash and cp *.txt stash/, and ls stash.
  14. Run ls /nonexistent and read the complaint. Then run ls /nonexistent 2> err.txt (the screen stays quiet), cat err.txt, and ls /nonexistent 2>/dev/null.
  15. Run nano hello.sh, type echo hi, press Ctrl+O then Enter to save, and Ctrl+X to exit. Then run sh hello.sh, and cat hello.sh to see the file you wrote.
  16. Run touch "my notes.txt", then ls my notes.txt and read the two complaints, then ls "my notes.txt" with the quotes.

Something wrong with this lesson? Report a problem