Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Preface

These guides are not done! Not remotely!

Welcome to CS at Reed! You'll be doing a lot of computer touching during your time here, and we wanted to put together some resources to make this as pleasant an experience as it can possibly be.

There are very many bits and pieces of software that you'll want to pick up and get used to along the way, but the vast majority of these tools can be unintuitive and frustrating. Here, we've collated a lot of words to hopefully make that experience easier.

This site covers a few core topics with copy-pastable snippets and explanations for reference, but if something isn't here, or if you just want some more information on anything, there are some really good resources on the broader internet. If you've got questions about how anything techy works, you might want to consult:

  • Julia Evans! She's really good at writing zines.
  • Anna Ritz (biology) directs her students to this command line bootcamp for a thorough, interactive guide to day-to-day terminal tasks.
  • The Pro Git book provides a good, methodical explanation of Git.
  • Zero to Nix describes the Nix package manager, a popular package manager for students here to use that's also installed on all of the Reednet machines.
  • man pages :(

As a final introductory note, don't be afraid to ask for help. A lot of computer-touching knowledge is really just experience. The more times you see something, the more likely it'll be to click.

Also, if you have questions, yell at us! Issues pertaining to this guide can be sent to GitHub issues, and, along with more general questions, can be emailed to us.

If you want to help us write guides, let us know! If you're familiar with git, we also accept contributions through the guides repository.

Shell

The shell is how most people use the command line or terminal on their computes. It's a really bare-bones interface to your computer, and it can feel obtuse or archaic at times. Unfortunately, it's also a very powerful tool that's certainly worth taking the time to learn.

The first step to doing so, then, is accessing your shell! Go on to installation.

Installation

You might already have a shell installed.

  • If your computer is running Linux, you almost certainly have a shell called Bash installed on your computer already. See the Linux section for instructions on how to access it.

  • If you're running MacOS, you've already got a shell called zsh. See here to learn how to access it.

  • If your computer runs Windows, you'll want to install the Windows Subsystem for Linux (WSL). See here.

Once you've got a shell installed, you'll be able to open your terminal and do something like this:

$ echo "Hello, world!"
Hello, world!

Linux

You've probably got an app called "Terminal" installed. Look for it in your launcher. You'll get an interface like this:

A small black window with a bit of text that says "bash," followed by a single dollar sign.

You can type and run shell commands at this prompt:

The same image as before, but the prompt says "echo Hello!" The line of text below says "Hello!"

That's all there is here! Go to learn the basics.

MacOS

You've already got a terminal: Press ⌘+SPACE to open Spotlight, and type terminal to open the terminal. You'll see something that looks something like this:

Initial MacOS terminal.

Where other operating systems use a shell called Bash, MacOS uses zsh by default. For our purposes here, these shells work mostly the same way, but parts of this guide (especially the scripting section) refer to Bash instead of zsh. This is unlikely to ever be important, but it's something to keep in mind.

Basics

The shell operates under some fairly foreign principles when compared to the rest of the software you might use on a daily basis. It doesn't work how you expect — Ctrl+C doesn't copy and Ctrl+V doesn't paste, for instance — but it's got plenty of conventions that are carried through into other tools you might end up using, so it's worth taking the time to play around a bit. Try entering this into your shell:

$ echo "Hello!"
Hello!

note

Only type the text after the $. Then, press enter, and you should see the same output on your screen as you see in the text above.

The $ here represents your prompt. It might look a little different in your terminal, but many default prompts end in a $, so that's the character most commonly used to represent it in documentation.

echo simply repeats whatever you give it. Despite what it looks like, there's actually a lot going on here. Try the next two commands:

$ echo "Hello," "world!"
Hello, world!
$ echo "Hello, world!"
Hello, world!

These two commands are different, but they produce the same result. This comes down to two very important concepts in the shell that you'll come into contact with again and again: commands and arguments. In the above, we can say that you ran the command echo "Hello!", but we can also say that the command you ran was echo, and the argument was Hello!. This is the difference between echo "Hello," "world!" and echo "Hello, world!". The first has just two arguments, and the second has just one. The command echo just happens to display all of its arguments together, putting a space in between them.

By the way, the quotes are strictly optional. All of the commands below have the same output. Try them!

$ echo "Hello," "world!"
Hello, world!
$ echo Hello, "world!"
Hello, world!
$ echo "Hello, world!"
Hello, world!
$ echo Hello, world!
Hello, world!

Even though these are all the same, it's generally considered good practice to quote arguments. To see why this is, try this command, and guess what the output might be:

$ echo Sorry. All the microchips are gone. I got #hungry.

You'll notice that the final #hungry doesn't display! What's going on here? It turns out that the shell treats everything in a line after the # character to be a comment: It's entirely ignored. You can use this to write out prose descriptions of what your commands do:

$ echo "Hello" # Print a greeting to the display.

This command has three parts: There's the command itself (echo), the argument (Hello), and the comment (everything after the #). What if you really want to echo a #, though? Quoting will help you here:

$ echo "Sorry. All the microchips are gone. I got #hungry."
Sorry. All the microchips are gone. I got #hungry.

These ideas of arguments, comments, and commands make up very much of the command line that you'll be interacting with, but you'll be using some more commands than just echo. Go on to navigation for some more useful techniques.

Navigation

You're probably used to files and folders as they show up on your desktop. You can drag them around, put them in other folders, and so forth. As it turns out, the command line gives you access to the exact same thing.

The simplest command that interacts with your files is pwd. This stands for "print working directory," and if you run it, you'll see your current working directory:

$ pwd
/home/user

note

Your output may be different if you're on MacOS, but it should have a similar form.

This is a very important concept: Every shell always has a working directory and you can always check what it is with pwd. A working directory is itself not too strange a concept: If you open up your files app, you'll see you're looking at some files. You can click on a directory to see the files and folders in that folder, and keep going however long you'd like. It's the same thing here. To see everything that's in your current directory, run ls:

$ ls
Desktop Documents Downloads go Mail Music Pictures Public Templates Videos

note

This is just some possible output. You might have some or none of these folders on your own computer.

Just like in your GUI files app, you can change your working directory. To do so, use the cd command:

$ cd Documents

You'll notice that nothing happened. However, run pwd again and you'll see you're somewhere else:

$ pwd
/home/<your-username>/Documents

As before, you can run ls to see all your documents:

$ ls
essay.txt cstar-guide.html shoelaces.pdf

note

Again, this is just example output. Yours is (presumably!) not quite the same.

The so-called parent directory of your working directory is referred to with ... This means that after you cd into Documents, you can get out just as easily:

$ pwd
/home/<your-username>/Documents
$ cd ..
$ pwd
/home/<your-username>
$ cd Documents
$ pwd
/home/<your-username>/Documents

ls is a command you'll be using a lot, so it's worth spending a little bit of time on it. Just like with echo, ls can take arguments. Try using the -l argument for a long listing:

$ ls -l
-rw-r--r--. 1 user group    0 Jan 24 12:13  essay.txt
-rw-r--r--. 1 user group   45 Jan 28 19:00  cstar-guide.html
-rw-r--r--. 1 user group 100M Jul 22  2024  shoelaces.pdf

note

An argument that begins with a - is usually called a flag.

Reading the output right-to-left, you'll see that we have the filenames just as before. Then, we have the time the files were last modified, preceded by their size. In this example, essay.txt is entirely empty. cstar-guide.html takes up 45 bytes of space, and the M in the 100M taken by shoelaces.pdf stands for megabytes. We'll come back to the rest of the output later, in the section on file permissions.

The echo command can actually be used to create files. Try, for instance, the following:

warning

Make sure you don't already have a file named a.txt in your current working directory. This command will overwrite that file!

$ echo "Hello!" > a.txt

This > is called a redirect, and takes the output of the command (which would normally just be "Hello!") and writes it to a file instead.

Now, if you run ls again, you'll see the same output as before, with one addition:

$ ls
a.txt essay.txt cstar-guide.html shoelaces.pdf

Now that you have that file saved to your disk, the cat command will let you see the contents:

$ cat a.txt
Hello!

The greatest sin of the command line environment is that cat has nothing to do with actual cats, and instead is an abbreviation of concatenate. You can pass two or more filenames as arguments to cat, and you'll be able to see them both, one after the other:

$ echo "Goodbye!" > b.txt
$ cat a.txt b.txt
Hello!
Goodbye!

Redirections work on every command, cat included. Try this:

$ cat a.txt b.txt > c.txt

Now, c.txt has the same content as a.txt and b.txt combined, and if you cat it, you'll see what you expect:

$ cat c.txt
Hello!
Goodbye!

By the way, you can't just cat any arbitrary file. Consider, for example, shoelaces.pdf. This thing is 100M, and, being a PDF, it probably has all sorts of information about formatting, page size, and so forth that just isn't expressed as plain text. It's usually inadvisable to cat these to your terminal, unless of course you're redirecting the output to a file.

note

If you want, you can look for a binary file like an image or a PDF with ls and cd. Then, cat it. You'll see a stream of nonsense pour over your terminal window. Press Ctrl+C to interrupt the command. You probably won't see your usual prompt anymore. Nevertheless, type reset and press Enter. It'll take a couple seconds, but your shell will come back just as before.

You probably want to get rid of a.txt, b.txt, and c.txt now. You can do that from the command line as well:

$ rm a.txt b.txt c.txt

Maybe you want to look for any other .txt files just in case you missed some. This can be achieved with a glob:

$ ls *.txt
essay.txt

note

Your output might be empty if you have no files that match this string. Try playing around with other globs. For instance, *a* matches any file with an a in the name.

Now that you know how to create and remove files, it's good to do the same for directories. The mkdir command does the first:

$ mkdir test-directory

And rm does the second:

$ rm -r test-directory

Notice the -r flag. This causes a recursive removal. That is, you're not just removing the directory you specify (here, that's test-directory), but also all the files and folders inside. Be very careful with this command. Unlike the normal files app, there's no trash can to restore from!

Now that you can make and remove files and folders, you've already mastered about half of the command line. Next up are some niceties in line editing.

Line Editing

As you might have noticed, using the command line can involve repeating yourself a lot. The shell itself actually helps you avoid most of the manual repetition by keeping track of your command history. You can see this for yourself: There's a file in your home directory called .bash_history on Linux and WSL or .zsh_history on MacOS which stores all the commands you've run in a given terminal session.

Close your terminal window and open a new one. Then, look for this file:

$ ls -h

Because this filename starts with a ., your history file is actually a hidden file, and you need the -h flag in ls to see it. Now, once you know if you have .bash_history or .zsh_history, check what's in it:

$ cat .bash_history # or .zsh_history!

By the way, you almost certainly have some other hidden files in this directory. Recall that the command for seeing those is ls -h, but you've already run it. Press the up arrow to cycle through your previous commands. First you'll see cat .bash_history, then ls -h. The shell will let you get through your entire history this way! Play around with the command line a little bit. Scroll up, modify a command, and run it. To modify a command, your normal keyboard shortcuts won't all work. You can likely use the arrow keys to move your cursor over the line, but there are some other, more shell-specific shortcuts that you may want to use.

ShortcutAction
Ctrl+HDelete the last character.
Ctrl+WDelete the last word.
Ctrl+AGo to the start of the line.
Ctrl+EGo to the end of the line.
Ctrl+FGo to the next character.
Ctrl+BGo to the last character.
Alt+FGo to the next word.
Alt+BGo to the last word.
Go to the previous (less recent) item in the history.
Go to the next (more recent) item in the history.

note

Most of these shortcuts are taken from a very old, decrepit piece of software called Emacs. You can see key bindings that look like this pretty much everywhere if you know where to look.

One last convenience: You can use Ctrl+R to search through your command history. You'll see something like this:

(reverse i-search)`':

Typing will match through your history in real time. You can use and to look through the matches, and edit the result just like normal.

File Permissions

We'll take another look at the output of ls -l:

$ ls -l
drwxr-xr-x. 1 user group 451M Jul 22  2024  Documents
-rw-r--r--. 1 user group    0 Jan 24 12:13  essay.txt
-rw-r--r--. 1 user group   45 Jan 28 19:00  cstar-guide.html
-rw-r--r--. 1 user group 100M Jul 22  2024  shoelaces.pdf

When you run this, you likely won't see user or group. Instead, you'll likely see your computer username in that first column and something like it in the next. These are the names of a user and of a group, respectively. In all likelihood, these are the names of your user and your group.

The systems we're using — Linux, MacOS, or WSL on Windows — all descend from an operating system called UNIX, which achieved success in part due to its multi-user support. On our personal computers, we don't generally use multi-user features all that frequently or explicitly, but they're baked in to the computer so much that, in fact, every single file is owned by one of many users, and, simultaneously, one of many groups.

If you want to know what your user is called, you can always run whoami:

$ whoami
atalii

note

This is just my personal username. Yours will be different!

Similarly, groups can tell you what groups you're a member of:

$ groups
atalii wheel input

note

Again, this is just the output on my machine. You'll probably be a member of some different groups. You don't need to worry about what each one is for right now.

This is the basis for the UNIX permission model: Every file has three sets of permissions. First, the owning user can read the file, write the file, or execute the file. Then, a different set of permissions (one, none, or all of reading, writing, and executing) apply to a user who is not the owner but is in the owning group. Then, there's a third set of permissions for everyone else.

This is actually what you see in the leftmost column of the long listing. The first letter (above, this is either - or d) indicates the kind of file this is. - indicates that it's just a normal file, and d indicates it's a directory. Then, there are three characters describing what the owning user can do. The first of these three indicates whether or not the owner can read the file (r if they can, - if they can't), the second (r or -) indicates whether they can write to the file, and the third (x or -) indicates whether or not they can execute this file. We have another group of the same three that describe permissions for members of the owning group, and a final group of three that describe permissions for everyone else.

If this feels a bit abstract, we can actually see these permissions working with a few commands. You can create a file with the touch command:

$ touch perm-example.txt

note

This command has no output. One of the recurring themes in CLIs is that if they don't complain, you're probably safe to assume that nothing's gone wrong. You can run echo $? to see the exit code of the process, which will always be 0 if it's successful.

Now, look at how perm-example.txt has been created:

$ ls -l perm-example.txt
-rw-r--r--. 1 atalii users 0 Feb  6 08:29 perm-example.txt

We can see that in this case, the owning user is atalii, and the owning group is users. atalii has the rw- perms associated: They can read from and write to the file. Anyone in the users group can read the file but do nothing else, as indicated by r--. And, with the same r-- repeated, anyone outside the owning group can only read the file.

Let's take a closer look at what exactly this all means. To be able to read from a file means you're allowed to look at the contents, which, in this case, are empty:

$ cat perm-example.txt

To be able to write to a file means you're able to modify its contents. Try this:

$ echo "I can write to this file!" >> perm-example.txt

note

Notice that we use >> here instead of > as we used previously. The difference is that > will always overwrite a file, replacing the contents with whatever it wants. By contrast, >> appends to the file, leaving alone whatever was there previously.

Now, if you read it again, you'll see that line of text:

$ cat perm-example.txt
I can write to this file!

Execution may, unfortunately, be a bit more mistifying. We'll explore what it means for a file to be executable in more detail when we talk about compiling code or, in this chapter, writing scripts. For now, it's enough to say that lots of the commands we've been using (cat, ls, touch, and so forth) are actually files on disk just like everything else. Because we can run them as commands, then, we know they're executable.

There's one last meaning to "executability" that should concern you, and that's executability for directories. If you'll recall the example at the beginning, my documents folder can be read, written, and executed by me, while it can only be written and executed by anyone else. Here, execution does not actually mean execution. Instead, it means that a user with executable permissions on the directory can cd into it.

This permission system would only be so useful if you couldn't change permissions. Fortunately, you can use the chmod command to change permissions. While there's some more advanced usages we won't cover here, the basics are good to know. Before we discuss exactly what this command does, try it out:

$ chmod ugo-r perm-example.txt

Now, if you try to read perm-example.txt, you should see an error:

$ cat perm-example.txt
cat: perm-example.txt: Permission denied

If you check perms with ls -l, you'll get a good idea of what that chmod command did:

$ ls -l perm-example.txt
--w-------. 1 atalii atalii 0 Feb  6 08:29 perm-example.txt

You'll see that you only have write permissions to the file. You can actually test that if you'd like:

$ echo "test" > perm-example.txt

Remember, you don't have read permissions on this test file, so if you try to cat it, you'll get that same error. Instead, you have to give yourself read permissions:

$ chmod u+r perm-example.txt
$ cat perm-example.txt
test

Hopefully you can start guessing at what the arguments to chmod do. You can give or take permissions with + or -. These permissions can be r, w, or x. So, for instance, to give everyone executable permissions, you can run chmod +x on a file. If you want only the owning user to get those executable permissions, however, run chmod u+x. You can put any combination of r, w, or x on the right hand side, and any combination of u, g, or o on the left hand side, for the owning user, the owning group, or other users.

chmod can be run on a file by the owning user, but it can also be run on any file by the root user: This is a special system account meant for use in modifying and administering the system, so it (or somebody logged in as this root user) can change permissions, read, write, and modify nearly any file.

Let's say that we've decided that perm-example.txt is an incredibly important file for the functioning of your computer, and we therefore want to make it accessible only to the specially-privileged root user. This requires two new commands: chown, and sudo.

$ sudo chown root:root perm-example.txt
$ ls -l perm-example.txt
-rw-------. 1 root root 5 Feb  6 09:18 perm-example.txt

sudo will probably give you a very scary prompt and ask for for your password. Once you confirm that you are indeed the user you're logged in as, sudo will pretend you're root to run the real command, which is chown root:root perm-example.txt. As the name suggests, this changes ownership of the file perm-example.txt to be owned by the root user and the root group, and, as we can see from the output of ls -l, only root can read and write.

Once you decide you no longer need this file, you can remove it, but only as the root user. So, you'll have to use sudo again:

$ sudo rm perm-example.txt

caution

The note before about being careful with rm goes double for sudo rm. If you make a mistake, you can use this command to really break some things.

With this and some practice, you're well on your way to being comfortable on the command line. Next up are some topics in configuration before covering a small amount about scripting.

Useful Commands

The command line can make it fairly difficult to discover new tools. If you're going to know that anything exists, you have to read about it somewhere. What follows, then, is a list for your reference of a whole bunch of things you may want to refer to.

Unless otherwise noted, these commands work in roughly the same way. They take some input from a file specified as a command line argument, perform some operation, and output the result to the console. If you're ever unsure how a specific command works, try running it with -h to get a help message. If that doesn't work, try --help. If that doesn't work, see the section on getting help.

Once you're done familiarizing yourself with the essentials, go on to learn some scripting techniques.

Essentials

CommandDescription
cdChange the current working directory.
lsList the files in the current working directory. You can also give it a target directory to list from, e.g., ls ./example.
ls -lList the files in the current working directory, but with a little more information.
catConcatenate files, or, with one argument, print a file to the console.
headGet the first couple lines of a file.
tailGet the last couple lines of a file.
grep <pattern> <file>Output all lines in file that contain <pattern>

Some Stuff More

You'll run these less often, but it can still be good to know they exist.

CommandDescription
tail -fWatch a file and output new lines as they're written to it. This can be useful for things like logs that are written to by another process over time.
wc -lCount the number of lines in stdin or from a file.
wc -cCount the number of characters in stdin or from a file.
wc -wCount the number of words in stdin or from a file.

Some Options

A lot of the above commands are very old (cat, e.g., descends from 1971), and therefore has some characteristics you may or may not like. These are really beyond the scope of the basics here, but if you find yourself curious, there are some potentially preferable replacements for the old tools if you're looking to play around a bit:

CommandReplacement
catbat
cdzoxide
findfd
grepripgrep
lseza
nanomicro

Scripting

The first and most important rule for writing Bash scripts is that you shouldn't. However, some concepts of Bash scripts matter a lot in most any interaction with the shell. Let's start simple by describing variables:

$ echo $HOME
/home/atalii
$ echo $PATH
"/bin:/sbin:/usr/bin:/usr/local/bin:...

Here, you've asked Bash to give you the values of two variables, $HOME, and $PATH. You might recognize $HOME as the path of your home directory (it's what'll show up if you run pwd in a new terminal), but $PATH is new: Instead of being one path, it's actually a : separated list of paths. You probably see the string bin in here quite a bit: This is because PATH lists the locations of certain kinds of binary files, or, as you may already know them, executables. For instance, you can run:

$ which cat
/usr/bin/cat

and you'll see that the cat command is actually a file located in /usr/bin. Check the output of the echo command above, and you'll likely see that /usr/bin is in your $PATH.

note

Some systems will have cat at /bin/cat, or maybe /sbin/cat. If your output is slightly different, that's okay. The same principle applies.

Were $PATH just a static list of paths, it wouldn't be so useful. However, you can put whatever directory you want in there.

We'll demonstrate this by creating a command of our own. Using a text editor of your choice, make a file in your home directory named foo.sh with the following contents:

#!/usr/bin/env bash

echo foo

The line that begins with a #! is called a shebang, and tells the operating system that it can run this script with bash. Now, make it executable:

$ chmod +x home.sh

If this command succeeds (and it should!), you'll be able to run it:

$ ./foo.sh
foo

Note that we're referring to the file by qualifying its path with that of the current directory. We can't just use foo.sh without the ./, otherwise Bash won't know that this is a file in the current directory. Instead, it'll look through $PATH, find nothing named foo.sh, and complain:

$ foo.sh
bash: foo.sh: command not found

However, we can set $PATH to include the directory in which foo.sh lives, and thereby let Bash run it just like any other command.

$ export PATH="$PATH:$HOME"
$ foo.sh
foo

The first command here is the one that does the work. It sets the $PATH variable equal to the value of $PATH followed by a colon, in turn followed by the value of $HOME, thus adding $HOME to list. Now, you can run every executable in your home directory just like anything else.

warning

Adding your $HOME directory to $PATH would make a lot of people angry and be widely regarded as a bad move. Part of the concern is security: While it takes elevated permissions to put an executable in /bin, /usr/bin, or the like, you only need your permission to put something in $HOME: If your user is compromised, or you download a suspicious file and put it in $HOME for later, you might end up running something malicious on accident.

More practically, though, it's just regarded as kind of a messy practice. Binaries are best off being put in places well-marked for them: e.g., /bin.

If you don't like this: Don't worry, your changes above only matter in your current shell session: Close your terminal or open a new one and your path will be back to normal.

Regardless, there are some nuances in the command above to examine. Start by creating a dummy variable to experiment with:

$ export FOO="bar"

If you're really enthusiastic about foo, you might even want two of them:

$ echo "$FOO $FOO"
bar bar

This is basically the same kind of concatenation operation we did with $PATH, but notice the following doesn't work:

$ echo '$FOO $FOO'
$FOO $FOO

Like with comments, you can use single quotes to avoid interpolating variables. In double quotes, Bash will read $ as letting it know that the following characters are the name of a variable: In single quotes, however, $ is just another character.

note

By convention, variables in Bash are written in UPPER_CASE.

For some reason, let's say you really like $FOO and want to save it:

$ export MY_FAV_VAR="My favorite variable is: $FOO."
$ echo "$MY_FAV_VAR"
My favorite variable is: bar

Note that the kind of interpolation bash does is very flexible: Wherever double quotes can appear, you can use and manipulate variables.

Scripting in Bash is a very finnicky topic: Variables are really just the tip of the iceberg. Much like many other programming languages, you can use loops, functions, and conditional statements. For an example of some more involved scripting, check out a hidden file in your home directory called ~/.bashrc: This is run every time you open your shell, and is usually populated with some interesting knobs you can turn by editing the file and opening a new shell (or by sourcing ~/.bashrc with the . command).

Ideally, you're now comfortable in the shell and have a good grasp of the kinds of things it can do: If not, don't worry! As always throughout this guide, experience is the best way to learn. Everything will become easier with repetition, practice, and conversations with other computer-touchers. Enjoy!

SSH

SSH, being an initialism for Secure SHell, is a set of programs and protocols that allow a user to access a shell on a remote device.

note

Throughout this guide, we'll use an example machine named Empanada, which is available by email request to CSTAR for Reed students.

As a protocol, SSH is a standardized method for two computers to communicate with each other. One computer, an SSH server, allows clients to authenticate themselves and, once authenticated, run commands on the server. Unless you're setting up a machine, you'll usually interact with this protocol from the perspective of the client, so that's what we'll cover here. The usual SSH flow will look something like below, where I log in to one of the Reed computers from my own laptop:

atali@my-laptop:~$ hostname
my-laptop
atali@my-laptop:~$ ssh atali@empanada.reed.edu
atali@empanada:~$ hostname
empanada
atali@empanada:~$ exit # or press Ctrl+D
atali@my-laptop:~$ hostname
my-laptop

note

Some things here, like my hostname, are specific to my computer. Others, like, empanada.reed.edu, are only accessible on the Reed network. If you'd like to follow along, send an email to CSTAR to get access to some machines.

Notice a new command: hostname tells you the name of the device your shell is running in. Every machine is given one of these names, and it's helpful to have a human-recognizable identifier for each computer. Then, the second new command, ssh, logs in to empanada.reed.edu as the user atali and opens up a shell. Once we're in the shell, we can see that this machine's hostname is empanada, and we run the exit command to close this shell and get back into the client. We then confirm that we're there with one last invocation of the hostname command.

Setting This up for Yourself

note

We recommend following along to get some practical experience. Contact CSTAR for access to some machines with an SSH server enabled.

Once an SSH server is aware of your existence, there are actually many ways to authenticate to it. You'll probably only come across the two most common ones, however:

  1. Password-based authentication.
  2. Key-based authentication.

These both work about as you'd expect. If the server is configured to accept password-based auth (and some other requirements are met), SSH will prompt you for your password:

atali@my-laptop:~$ ssh atali@empanada.reed.edu
atali@empanada.reed.edu's password:
atali@empanada:~$ # We're logged in!

When you type in your password, nothing will appear on screen. Once you've typed it correctly, pressing enter sends it off to the server to be checked, and, if correct, you'll find yourself logged in.

Despite being simple, password-based authentication is generally not preferred to key-based authentication. This authentication method revolves around a key pair that you generate on your own device. This consists of a public key and a private key — you keep the private key secret, but the server gets your public key. Via some witchcraft, if another party has your public key, they can confirm that you know your private key without ever knowing what that private key precisely is. This is unfortunately a slightly more involved process to set up, but leads to a more convenient and secure SSH login process later.

We'll start by creating a keypair. On your SSH client machine, run ssh-keygen

$ ssh-keygen
Generating public/private ed25519 key pair.
Enter passphrase for "id_ed25519" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_ed25519
Your public key has been saved in id_ed25519.pub
The key fingerprint is:
SHA256:QtXVxq6HOFptCucsULsI2bH3Tp0t4Xj6MO0BeThtfQI tali@thing-in-itself
The key's randomart image is:
+--[ED25519 256]--+
|        .. ..o   |
|       .  .   +  |
|      .     Eo   |
|     .. .  + o.  |
|     o.+S.=o=oo .|
|    o +.+ =X=+.o |
|     . + X=+O..  |
|      . +.=* o   |
|         oo.o    |
+----[SHA256]-----+

Fortunately, you only have to make one decision, and that's whether to protect your private key with a password. In some circumstances, you may prefer this, but it's also acceptable to leave it blank. Then, it gives you some information about your key. Pay particularly close attention to the fact that two new files have been created: id_ed25519 and id_ed25519.pub. id_ed25519 is your private key, and id_ed25519 is your public key. This information is enough to set up key-based SSH authentication:

atali@my-laptop:~$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMPdyjTGPE4WBxR75SJrOHs60flkRqJIyiNr7GNC8OcV atali@my-laptop
atali@my-laptop:~$ ssh atali@empanada.reed.edu
atali@empanada.reed.edu's password:
atali@empanada:~$ echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMPdyjTGPE4WBxR75SJrOHs60flkRqJIyiNr7GNC8OcV tali@thing-in-itself' >> ~/.ssh/authorized_keys
atali@empanada:~$ exit
atali@my-laptop:~$ ssh atali@empanada.reed.edu
atali@empanada:~$ # Notice how you haven't been asked for a password: SSH is automatically using your key!

All you're doing is adding your SSH public key to a file of authorized keys, which the SSH server can scan to let you in.

If this is confusing, don't worry. The best way to understand this is to do it. If you want practice, email CSTAR for access to our machines.

Nix Cheatsheet

You can use Nix to install additional software anywhere Nix itself is installed.

  • To search for a package:

    $ nix search nixpkgs hello
    * legacyPackages.x86_64-linux.hello (2.12.1)
    A program that produces a familiar, friendly greeting
    
  • To install a package:

    $ nix profile install nixpkgs#hello
    
  • To start a temporary shell without installing a package:

    $ nix shell nixpkgs#hello
    $ hello
    Hello, world!
    
  • To list installed packages:

    $ nix profile list
    Index:              0
    Flake attribute:    legacyPackages.x86_64-linux.hello
    Original flake URL: flake:nixpkgs
    Locked flake URL:   github:NixOS/nixpkgs/2646b294a146df2781b1ca49092450e8a32814e1
    Store paths:        /nix/store/z6hzxgy21zimj58xhy06f2f49qgy06gg-hello-2.12.1
    
  • To uninstall a package by index:

    nix profile remove 0
    removing 'flake:nixpkgs#legacyPackages.x86_64-linux.hello'
    

For more information about Nix, you can visit https://zero-to-nix.com.

note

This section is TODO.

Writing Code

This section covers some more mechanical issues with writing code. How is code stored? How is it executed? How do you edit it? These questions are the frustrating, tooling-immersed bretheren of the more interesting questions you'll study in your classes, but, nevertheless, they demand answering. If you've written a good amount of code before, nothing here should be surprising. If you haven't, this can serve as a reference and also a brief explanation on the more mundane steps involved in that thing we call computer programming.

Certain concepts are universal to most programming languages — these concepts are specified further in this chapter — but certain languages demand specific toolchains to run code written in those languages. The language you're using largely depends on the class you're taking. Feel free to follow the links as appropriate or simply explore:

  • 121 is taught with Python.
  • 221 is taught with C, C++, and Assembly.
  • You will encounter other toolchains; e.g., Jupyter notebooks for Deep Learning, JS (or possibly TypeScript soon!) for graphics, and OCaml for Programming Languages. If you have toolchain questions, let us know.

Git

Git is a version control system. This allows you to access multiple versions of the same directory on one disk. Git is immensely powerful, but this page will only cover the basics. If you're looking for a more in-depth reference, Julia Evans wrote a very good introduction, which you can also find in Erica's office.

Getting Started With Git

If you want to use version control on your own machine (e.g. a coding project or homework), you can create a repository, or a collection of files that git is able to keep track of and version. There are two relevant ways to create a repository: Either you're workin with something that somebody's already made, or you're starting an empty repository. In this first case, you'll use git clone, and in the second, you'll use git init.

As an example of the first, you can get the sources for this book with this:

$ git clone https://github.com/reed-cstar/guides

Cloning into 'guides'...
remote: Enumerating objects: 211, done.
remote: Counting objects: 100% (118/118), done.
remote: Compressing objects: 100% (88/88), done.
remote: Total 211 (delta 54), reused 77 (delta 30), pack-reused 93 (from 1)
Receiving objects: 100% (211/211), 632.06 KiB | 11.92 MiB/s, done.
Resolving deltas: 100% (82/82), done.

If you made changes to a repository, and you would like to track and version them, you can make a commit. A commit is the unit by which Git records changes made to some files. Alongside the content of those changes (including creations, deletions, and modifications), it also records the author, the date, and also some optional further metadata regarding the changes.

Before you start committing code, you'll need to tell git who you are. Git provides a clear interface for this with the config subcommand. Try the following, replacing the quoted vale Your Name with your name, and your.email@example.com with your email:

$ git config --global user.name 'Your Name'
$ git config --global user.email 'your.email@example.com'

Note the use of the --global flag: This instructs git to store a default value for these settings across all repositores. If you leave it out, git will associate your name and email only with the repository in your current working directory, and future invocations of git will use this value to override any global value that may or may not be set.

Now that git is sufficiently configured and you're in a repository, you can make some changes to the files and add them to the staging area with the add subcommand:

$ git add ./path/to/modified/files

note

You'll often see git add .: If run from the root of your repository, this'll add all changes to the staging area.

With your changes added, the commit subcommand will allow you to add them to version control:

$ git commit

note

If you aren't currently in in a repository, git commit will tell you with the only moderately cryptic error message:

fatal: not a git repository (or any of the parent directories): .git

You'll see that git brings up a text editor wherein you can write a commit message. Most likely, this'll be a text editor called nano — git will run whatever command you specify in the $VISUAL environment variable. Regardless, writing some text, saving it, and exiting (via Ctrl+x in Nano) will be enough to create a commit, and you'll be dropped back in your shell.

Reed CS Resources

You have access to many tools, like your own Reed website, compute resources, and access to Polytopia (located in LIB387). If you have any questions about any of these resources, feel free to email CSTAR: it's harder to look up any Reed resources than anything else in this guide.

Machines

Reed provides some machines for you to use. Some of these can be accessed in person, but others are best accessed over ssh.

note

To get access to all of these machines, email CSTAR.

These machines all run Ubuntu with Nix installed for user package management.

note

Don't know what this means? Don't worry. We'll deal with it later.

Peggy, Patty, and Polly

Peggy is in the corner of Polytopia and runs Ubuntu. You can also access it from the Reed college Wi-fi over ssh at peggy.reed.edu.

Polly and Patty are in Jim's office, and can be accessed solely by ssh over the Reed college Wi-fi at polly.reed.edu and patty.reed.edu, respectively. Patty in particular is useful when you're in need of some extra compute power.

The Dumplings

Banku, Empanada, Gyoza, and Pierogi are iMacs running Ubuntu available for use in Polytopia. These are available at banku.reed.edu, empanada.reed.edu, gyoza.reed.edu, and pierogi.reed.edu over the Reed Wi-fi.

Accounts

At Reed's instruction, your username is fixed at account creation to be your Kerberos username. Then, your password is set on Polly and copied out to everywhere else. Don't try to change your password on other machines; always SSH into Polly.1

Graduation

After you graduate, we'll delete your account, including any data that you may have.

Spring class of 2025: We've already deleted your accounts, but your home directories are still around. Send an email if you want me an archive of them.

Fall class of 2025 and onwards: We'll send you your home directories as a tarball via email. Then, we'll delete everything. If you've got any additional last wishes, just send an email.


  1. At some point, I (Tali) might make a web app to change your password without SSH'ing. If this happens, I'll link it.

Services

We self-host some services that you can use.

Coder

Coder is a web app that provides access to development environments running in Docker containers. This can be a convenient way to set up an environment for some specialized tasks. It runs physically on Patty, so it's got access to a good amount of compute. In particular, there's a pre-existing template you can use to make a container for CUDA development if you're having issues on your own machine or if you don't have an Nvidia GPU.

More detailed instructions on general use and configuring CUDA will be available here soon. If you have a question or what some guidance right now, send an email.

Authentication

These services are all accessed via an SSO login distinct from your normal Reed credentials. You can test your login (from a device on the Reed network) by logging into Polly over the web. Your username and password for this SSO system is the same as that which you'd use to log in to Polly over SSH.1


  1. This isn't strictly true right now. If you change your password, you'll need to ping a CSTAR member (for now, Tali) to get everything to work. I (Tali) will make this automatic soon!

Procedures

note

This page is meant primarily for CSTAR members administering these machines.

Authentication Mechanisms

We are not able to tie into Kerberos for a 'proper' SSO. Instead, we run a combination of two mechanisms, both on Polly, to make sure that everything stays in sync and that users don't need to change their password on 7+ machines.

Authelia

Authelia is our OIDC provider. With minimal extra configuration, it can also run as part of a reverse proxy to provide authentication to web services which don't have OAuth/OIDC support. Authelia has a few other mechanisms available for serving auth, and should be able to integrate with most services.

It runs on Polly and keeps a list of users from a users.yml file which is autogenerated from /etc/shadow/.

warning

Authelia runs as a service called authelia-custom. Do not use authelia.service.

shadow-syncd

shadow-syncd is the name of a small Python script that runs on Polly and propagates all changes in /etc/shadow to the other machines. This keeps SSH and physical logins consistent without the complication of PAM for LDAP, OIDC, or anything of the sort.

Adding a New Machine

If the machine is owned by Reed (it should have an asset number and barcode; ask CUS in unclear cases), it needs to have an admin (sudo-capabale) account named for cus and for reedadmin. Additionally, it'll need to have Crowdstrike installed, so it should be running Ubuntu.

To set it up, make sure the MAC address isn't being blocked (i.e., that it has internet access over Ethernet). If there are problems here, the issue should be brought to CUS. Install Ubuntu and set up the polytopia admin account. Email CUS to ask them to set up their admin accounts and Crowdstrike. Then, add it to the inventory.yaml file and run any applicable Ansible playbooks.

If, on the other hand, the machine is not Reed-owned (e.g., Quail), CUS asks us to have it set up individually on Netreg. That is, you're meant to set it up in netreg as your own individual device. Then, you should install either NixOS or Ubuntu,1 set up the polytopia admin account, and get it set up with Ansible as per usual.

Updating the Machines

Major version updates will happen during school breaks.


  1. The account mechanisms as of now rely on a writable /etc. In particular, since Polly tries to copy its shadow into other machines, a NixOS machine meant to be accessed by all CSTAR-registered users will require some special treatment. My (Tali's) suggestion is to reserve personal machines for servers that don't require SSH access by individuals, where we can get away just with a Polytopia admin account.