--- tags: General --- # The Missing Semester of Your CS Education Solution author: [`ngokchaoho`](https://github.com/ngokchaoho) Exercise questions belong to https://missing.csail.mit.edu/2020/course-shell/ Solution provided by: `ngokchaoho` ## Lecture 1 Exercise 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. ```shell= chao@DESKTOP-CAG1EAQ:/mnt/c/Users/HO NGOK CHAO$ echo $SHELL /bin/bash ``` 2. > Create a new directory called missing under /tmp. ```shell= chao@DESKTOP-CAG1EAQ:/mnt/c/Users/HO NGOK CHAO$ mkdir /tmp/missing ``` 3. > Look up the touch program. The man program is your friend. ``` shell= man touch ``` 4. > Use touch to create a new file called semester in missing. ``` shell= chao@DESKTOP-CAG1EAQ:/tmp$ touch missing/semester ``` 5. > Write the following into that file, one line at a time: ```shell= #!/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. ```shell= chao@DESKTOP-CAG1EAQ:/tmp$ cd missing chao@DESKTOP-CAG1EAQ:/tmp/missing$ echo '#!/bin/sh' > semester chao@DESKTOP-CAG1EAQ:/tmp/missing$ echo 'curl --head --silent https://missing.csail.mit.edu' >> semester ``` 6. >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 of ls (hint: look at the permission bits of the file). ```shell= chao@DESKTOP-CAG1EAQ:/tmp/missing$ ./semester -bash: ./semester: Permission denied ``` It can be seen from below that '-' occupies the writing permission bits for owner, owner groups and everyone else and hence nobody except root has execution permission. ```shell= chao@DESKTOP-CAG1EAQ:/tmp/missing$ ls -l semester -rw-r--r-- 1 chao chao 61 Jan 17 22:45 semester ``` 7. >Run the command by explicitly starting the sh interpreter, and giving it the file semester as the first argument, i.e. sh semester. Why does this work, while ./semester didn’t? ```shell= chao@DESKTOP-CAG1EAQ:/tmp/missing$ sh semester HTTP/2 200 content-type: text/html; charset=utf-8 server: GitHub.com last-modified: Sun, 10 Jan 2021 11:40:47 GMT access-control-allow-origin: * etag: "5ffae7bf-1ee8" expires: Sun, 17 Jan 2021 15:02:22 GMT cache-control: max-age=600 x-proxy-cache: MISS x-github-request-id: 0852:6E90:9A2E1:B03E5:60044F26 accept-ranges: bytes date: Sun, 17 Jan 2021 14:52:22 GMT via: 1.1 varnish age: 0 x-served-by: cache-sin18033-SIN x-cache: MISS x-cache-hits: 0 x-timer: S1610895143.561525,VS0,VE248 vary: Accept-Encoding x-fastly-request-id: 2e51b28689d432a378cbca542f3a213661ce2fc2 content-length: 7912 ``` `sh semester` would create a new `sh` process which read the executable contents and run it, in which case since we have execution permission for sh and read permission for semester, it works. On the other hand, `./semester` attempts to run the script directly but since we don't have execution permission it fails, but if we do have execution right the program loader would recognise the `shebang` line run `/bin sh` with the path of `semester` as its first argument. Helpful reading: [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) [what-is-the-difference-between-and-source](https://unix.stackexchange.com/questions/312573/what-is-the-difference-between-and-source) 8. >Look up the chmod program (e.g. use man chmod). ```shel= man chmod ``` 9. >Use chmod to make it possible to run the command ./semester rather than having to type sh semester. How does your shell know that the file is supposed to be interpreted using sh? See this page on the shebang line for more information. ```shell= chmod u+x semester ``` 10. Use | and > to write the “last modified” date output by semester into a file called last-modified.txt in your home directory. ```shell= ./semester | grep last-modified > last-modified.txt ``` # Lecture 2 Shell Tools 1. > Read man ls and write an ls 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 ```shell= ls -lahr --color ``` 2. >Write bash functions marco and polo that do the following. Whenever you execute marco the current working directory should be saved in some manner, then when you execute polo, no matter what directory you are in, polo should cd you back to the directory where you executed marco. For ease of debugging you can write the code in a file marco.sh and (re)load the definitions to your shell by executing source marco.sh ```shell= #!/bin/bash marco () { saved_dir="$(pwd)" } polo() { cd "$saved_dir" } ``` 3. > 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. The folllowing error generation shell script is provided by MIT. Note that >&2 can be placed before or after the simple command. [can-i-specify-a-redirected-input-before-a-compound-command](https://unix.stackexchange.com/questions/156046/can-i-specify-a-redirected-input-before-a-compound-command) error_prone.sh ```shell= #!/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" ``` Solution: examine.sh ```shell= #!/bin/bash echo "Starting program at $(date)" # Date will be substituted echo "Running program $0 with pid $$" rm -f ~/stdout.txt rm -f ~/stderr.txt touch ~/stdout.txt touch ~/stderr.txt count=0 while [[ $? -eq 0 ]]; do count=$((count+1)) ./error_prone.sh >>~/stdout.txt 2>>~/stderr.txt done echo "It ran $count times" cat stdout.txt cat stderr.txt ``` 4. > 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 like tar take inputs from arguments. To bridge this disconnect there’s the xargs command which will execute a command using STDIN as arguments. For example ls | 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 for xargs). >If you’re on macOS, note that the default BSD find is different from the one included in GNU coreutils. You can use -print0 on find and the -0 flag on xargs. 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. > Solution: ```shell= find . -name '*.html' | xargs -d '\n' tar -czf ./target.tar.gz ``` 5. > (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? > Solution: ```shell= find . -type f -printf '%T@ %p\n' \ | sort -n | tail -1 | cut -f2- -d" " ``` from https://stackoverflow.com/questions/4561895/how-to-recursively-find-the-latest-modified-file-in-a-directory To list all by recency Solution: ```shell= find . -type f -printf '%T@ %p\n' | sort -n | cut -f2 -d" " ``` # Lecture 3 Exercise > 1. Complete vimtutor. Note: it looks best in a 80x24 (80 columns by 24 lines) terminal window. ```shell= vimtutor ``` If you are on windows, maybe you might want to map caps to `Esc` first, by downloading https://www.autohotkey.com/ and run this script: ``` Capslock::Esc return ``` > 2.Download our basic vimrc and save it to ~/.vimrc. Read through the well-commented file (using Vim!), and observe how Vim looks and behaves slightly differently with the new config. ```shell= curl https://missing.csail.mit.edu/2020/files/vimrc -o ~/.vimrc ``` > 3.Install and configure a plugin: ctrlp.vim. Create the plugins directory with mkdir -p ~/.vim/pack/vendor/start Download the plugin: cd ~/.vim/pack/vendor/start; git clone https://github.com/ctrlpvim/ctrlp.vim Read the documentation for the plugin. Try using CtrlP to locate a file by navigating to a project directory, opening Vim, and using the Vim command-line to start :CtrlP. Customize CtrlP by adding configuration to your ~/.vimrc to open CtrlP by pressing Ctrl-P. >4. To practice using Vim, re-do the Demo from lecture on your own machine. ```python= import sys def fizz_buzz(limit): for i in range(1,limit): limit = 10 if i % 3 == 0: print('fizz',end='') if i % 5 == 0: print('buzz',end='') if i % 3 and i % 5: print(i) def main(): fizz_buzz(int(sys.argv[1])); if __name__ == '__main__': main(); ``` >5. Use Vim for all your text editing for the next month. Whenever something seems inefficient, or when you think “there must be a better way”, try Googling it, there probably is. If you get stuck, come to office hours or send us an email. >6. Configure your other tools to use Vim bindings (see instructions above). >7. Further customize your ~/.vimrc and install more plugins. >8. (Advanced) Convert XML to JSON (example file) using Vim macros. Try to do this on your own, but you can look at the [macros](https://missing.csail.mit.edu/2020/editors/#macros) section above if you get stuck. Solution: Is within macros already. Honestly, it makes me realise VIM is a programming language by itself. # Lecture 4 Exercise > Take this short interactive regex tutorial. Solution: https://regexone.com