- 1/13/20 : Course overview + the shell
- 1/14/20 : Shell Tools and Scripting
- 1/15/20 : Editors (Vim)
- 1/16/20 : Data Wrangling
- 1/21/20 : Command-line Environment
- 1/22/20 : Version Control (Git)
- 1/23/20 : Debugging and Profiling
- 1/27/20 : Metaprogramming
- 1/28/20 : Security and Cryptography
- 1/29/20 : Potpourri
Lesson 1
-
For this course, you need to be using a Unix shell like Bash or ZSH. If you are on Linux or macOS, you don’t have to do anything special. If you are on Windows, you need to make sure you are not running cmd.exe or PowerShell; you can use Windows Subsystem for Linux or a Linux virtual machine to use Unix-style command-line tools. To make sure you’re running an appropriate shell, you can try the command
echo $SHELL
. If it says something like/bin/bash
or/usr/bin/zsh
, that means you’re running the right program. -
Create a new directory called
missing
under/tmp
.1 2
cd /tmp mkdir missing
-
Look up the
touch
program. Theman
program is your friend.1
man touch
-
Use
touch
to create a new file calledsemester
inmissing
.1 2
cd /tmp/missing touch semester
-
Write the following into that file, one line at a time:
1 2
#!/bin/sh curl --head --silent https://missing.csail.mit.edu
The first line might be tricky to get working. It’s helpful to know that
#
starts a comment in Bash, and!
has a special meaning even within double-quoted ("
) strings. Bash treats single-quoted strings ('
) differently: they will do the trick in this case. See the Bash quoting manual page for more information.1 2
cat '#!/bin/sh' >> semester cat 'curl --head --silent https://missing.csail.mit.edu' >> semester
-
Try to execute the file, i.e. type the path to the script (
./semester
) into your shell and press enter. Understand why it doesn’t work by consulting the output ofls
(hint: look at the permission bits of the file).1 2
./semester ls -l
The file semester has no execution permission.
-
Run the command by explicitly starting the
sh
interpreter, and giving it the filesemester
as the first argument, i.e.sh semester
. Why does this work, while./semester
didn’t? -
Look up the
chmod
program (e.g. useman chmod
). -
Use
chmod
to make it possible to run the command./semester
rather than having to typesh semester
. How does your shell know that the file is supposed to be interpreted usingsh
? See this page on the shebang line for more information.1
chmod +x semester
-
Use
|
and>
to write the “last modified” date output bysemester
into a file calledlast-modified.txt
in your home directory.1
date -r test | cat > ~/last-modified.txt
-
Write a command that reads out your laptop battery’s power level or your desktop machine’s CPU temperature from
/sys
. Note: if you’re a macOS user, your OS doesn’t have sysfs, so you can skip this exercise.1
cat /sys/class/power_supply/BAT0/capacity
Lesson 2
-
Read
man ls
and write anls
command that lists files in the following manner- Includes all files, including hidden files
- Sizes are listed in human readable format (e.g. 454M instead of 454279954)
- Files are ordered by recency
- Output is colorized
A sample output would look like this
1 2 3 4 5
-rw-r--r-- 1 user group 1.1M Jan 14 09:53 baz drwxr-xr-x 5 user group 160 Jan 14 09:53 . -rw-r--r-- 1 user group 514 Jan 14 06:42 bar -rw-r--r-- 1 user group 106M Jan 13 12:12 foo drwx------+ 47 user group 1.5K Jan 12 18:08 ..
Sol:
1
ls -lhat --color=auto
-
Write bash functions
marco
andpolo
that do the following. Whenever you executemarco
the current working directory should be saved in some manner, then when you executepolo
, no matter what directory you are in,polo
shouldcd
you back to the directory where you executedmarco
. For ease of debugging you can write the code in a filemarco.sh
and (re)load the definitions to your shell by executingsource marco.sh
.1 2 3 4 5 6 7 8
marco() { cur=$(pwd) } polo() { cd $cur }
-
Say you have a command that fails rarely. In order to debug it you need to capture its output but it can be time consuming to get a failure run. Write a bash script that runs the following script until it fails and captures its standard output and error streams to files and prints everything at the end. Bonus points if you can also report how many runs it took for the script to fail.
1 2 3 4 5 6 7 8 9 10 11
#!/usr/bin/env bash n=$(( RANDOM % 100 )) if [[ n -eq 42 ]]; then echo "Something went wrong" >&2 echo "The error was using magic numbers" exit 1 fi echo "Everything went according to plan"
Sol:
1 2 3 4 5 6 7 8 9 10 11
#!/usr/bin/bash n=0 ./run.sh >/dev/null while [[ $? -ne 1 ]]; do n=$(( $n + 1 )) echo $n ./run.sh >/dev/null done echo "Error!"
-
As we covered in the lecture
find
’s-exec
can be very powerful for performing operations over the files we are searching for. However, what if we want to do something with all the files, like creating a zip file? As you have seen so far commands will take input from both arguments and STDIN. When piping commands, we are connecting STDOUT to STDIN, but some commands liketar
take inputs from arguments. To bridge this disconnect there’s thexargs
command which will execute a command using STDIN as arguments. For examplels | xargs rm
will delete the files in the current directory.
Your task is to write a command that recursively finds all HTML files in the folder and makes a zip with them. Note that your command should work even if the files have spaces (hint: check-d
flag forxargs
).
If you’re on macOS, note that the default BSDfind
is different from the one included in GNU coreutils. You can use-print0
onfind
and the-0
flag onxargs
. As a macOS user, you should be aware that command-line utilities shipped with macOS may differ from the GNU counterparts; you can install the GNU versions if you like by using brew.1
find . -name "*.html" | xargs -d "\n" tar cf target.tar
-
(Advanced) Write a command or script to recursively find the most recently modified file in a directory. More generally, can you list all files by recency?
1
tac <(find . -exec sh -c "date -r {} | xargs echo -n; echo -n '|'; echo -n {}; echo ;" \; | sort)
Lesson 5
Job control
-
From what we have seen, we can use some
ps aux | grep
commands to get our jobs’ pids and then kill them, but there are better ways to do it. Start asleep 10000
job in a terminal, background it withCtrl-Z
and continue its execution withbg
. Now usepgrep
to find its pid andpkill
to kill it without ever typing the pid itself. (Hint: use the-af
flags).1 2
sleep 10000 & pgrep sleep | kill
-
Say you don’t want to start a process until another completes. How would you go about it? In this exercise, our limiting process will always be
sleep 60 &
. One way to achieve this is to use thewait
command. Try launching the sleep command and having anls
wait until the background process finishes.
However, this strategy will fail if we start in a different bash session, sincewait
only works for child processes. One feature we did not discuss in the notes is that thekill
command’s exit status will be zero on success and nonzero otherwise.kill -0
does not send a signal but will give a nonzero exit status if the process does not exist. Write a bash function calledpidwait
that takes a pid and waits until the given process completes. You should usesleep
to avoid wasting CPU unnecessarily.1 2 3 4 5 6 7 8 9 10 11
#!/usr/bin/bash pidwait() { kill -0 $1 while [[ $? -ne 1 ]]; do sleep 1 kill -0 $1 done echo Done. }
Terminal multiplexer
- Follow this
tmux
tutorial and then learn how to do some basic customizations following these steps.
Aliases
-
Create an alias
dc
that resolves tocd
for when you type it wrongly.1
alias dc=cd
-
Run
history | awk '{$1="";print substr($0,2)}' | sort | uniq -c | sort -n | tail -n 10
to get your top 10 most used commands and consider writing shorter aliases for them. Note: this works for Bash; if you’re using ZSH, usehistory 1
instead of justhistory
.1
history | sed -E 's/\s+[0-9]+\s+//' | sort | uniq -c | sort -n | tail -n 10
Dotfiles
Let’s get you up to speed with dotfiles.
- Create a folder for your dotfiles and set up version control.
- Add a configuration for at least one program, e.g. your shell, with some customization (to start off, it can be something as simple as customizing your shell prompt by setting
$PS1
). - Set up a method to install your dotfiles quickly (and without manual effort) on a new machine. This can be as simple as a shell script that calls
ln -s
for each file, or you could use a specialized utility. - Test your installation script on a fresh virtual machine.
- Migrate all of your current tool configurations to your dotfiles repository.
- Publish your dotfiles on GitHub.
I would recommend
yadm
for managing dotfiles.
Lesson 6
-
If you don’t have any past experience with Git, either try reading the first couple chapters of Pro Git or go through a tutorial like Learn Git Branching. As you’re working through it, relate Git commands to the data model.
-
Clone the repository for the class website.
-
Explore the version history by visualizing it as a graph.
1
git log --graph
-
Who was the last person to modify
README.md
? (Hint: usegit log
with an argument).1
git log --raw
-
What was the commit message associated with the last modification to the
collections:
line of_config.yml
? (Hint: usegit blame
andgit show
).1 2
git blame _config.yml -L 18 git show a88b4eac
-
-
One common mistake when learning Git is to commit large files that should not be managed by Git or adding sensitive information. Try adding a file to a repository, making some commits and then deleting that file from history (you may want to look at this).
-
Clone some repository from GitHub, and modify one of its existing files. What happens when you do
git stash
? What do you see when runninggit log --all --oneline
? Rungit stash pop
to undo what you did withgit stash
. In what scenario might this be useful? -
Like many command line tools, Git provides a configuration file (or dotfile) called
~/.gitconfig
. Create an alias in~/.gitconfig
so that when you rungit graph
, you get the output ofgit log --all --graph --decorate --oneline
. Information about git aliases can be found here.1 2
[alias] graph = log --all --graph --decorate --oneline
-
You can define global ignore patterns in
~/.gitignore_global
after runninggit config --global core.excludesfile ~/.gitignore_global
. Do this, and set up your global gitignore file to ignore OS-specific or editor-specific temporary files, like.DS_Store
. -
Fork the repository for the class website, find a typo or some other improvement you can make, and submit a pull request on GitHub (you may want to look at this).