├── 06-tailing-logs ├── logs │ └── .gitkeep ├── .gitignore ├── README.md └── mk-logs ├── 07-jq-and-redirects ├── .gitignore └── README.md ├── 05-file-version-sha ├── helper.py ├── entry.sh ├── start.py ├── helper.pyc └── README.md ├── images ├── hack-time.mp4 ├── spellbook.jpg ├── sorcerer-mickey-final-dribbble_2x.png └── the-dragon-prince-season-1-26258-1200.jpg ├── 04-kill-process-by-name ├── die-hard.sh └── README.md ├── 02-find-emails ├── README.md └── wwu-depts │ ├── geology.html │ └── mathematics.html ├── 03-count-lines-of-code └── README.md ├── xx-holey-polygons ├── polygon-with-a-hole.geojson ├── README.md ├── no-holes-here.geojson └── has-holes.geojson ├── README.md ├── presentation.md └── 01-xkcd-password └── README.md /06-tailing-logs/logs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /06-tailing-logs/.gitignore: -------------------------------------------------------------------------------- 1 | logs/* 2 | -------------------------------------------------------------------------------- /07-jq-and-redirects/.gitignore: -------------------------------------------------------------------------------- 1 | commits.json 2 | -------------------------------------------------------------------------------- /05-file-version-sha/helper.py: -------------------------------------------------------------------------------- 1 | message = 'Hello, World' -------------------------------------------------------------------------------- /05-file-version-sha/entry.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | python start.py -------------------------------------------------------------------------------- /05-file-version-sha/start.py: -------------------------------------------------------------------------------- 1 | import helper 2 | 3 | print(helper.message) 4 | -------------------------------------------------------------------------------- /images/hack-time.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgschiller/shell-challenges/HEAD/images/hack-time.mp4 -------------------------------------------------------------------------------- /images/spellbook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgschiller/shell-challenges/HEAD/images/spellbook.jpg -------------------------------------------------------------------------------- /05-file-version-sha/helper.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgschiller/shell-challenges/HEAD/05-file-version-sha/helper.pyc -------------------------------------------------------------------------------- /images/sorcerer-mickey-final-dribbble_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgschiller/shell-challenges/HEAD/images/sorcerer-mickey-final-dribbble_2x.png -------------------------------------------------------------------------------- /images/the-dragon-prince-season-1-26258-1200.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bgschiller/shell-challenges/HEAD/images/the-dragon-prince-season-1-26258-1200.jpg -------------------------------------------------------------------------------- /06-tailing-logs/README.md: -------------------------------------------------------------------------------- 1 | In some web environments, you'll have several log files. It can be handy to see all of them at once, but it's annoying to open five terminals to watch them all come in. 2 | 3 | Run `mk-logs.sh`, which writes logs to several files in `./logs/`. Now run `tail -f logs/error.log` to watch one of the files as it is updated. 4 | 5 | Write a command that displays lines from all the log files as they come in. 6 | 7 | The access log is noisy! We probably don't care for that level of detail. Filter it out from the stream. -------------------------------------------------------------------------------- /04-kill-process-by-name/die-hard.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | trap 'not_today' 0 1 2 3 6 14 15 4 | 5 | WORDS=(canteloupe pineapple apple pear cranberry pumpkin lemon lime orange grape grapefruit pomegranate kumquat tangerine clementine banana coconut papaya mango apricot plum strawberry peach cherry blueberry jamberry nectarine tangelo kiwifruit watermelon raspberry) 6 | not_today() { 7 | speak 8 | } 9 | 10 | speak() { 11 | osascript -e "set Volume 5" 12 | local w=${WORDS[RANDOM%${#WORDS[@]}+1]} 13 | echo $w 14 | say $w 15 | } 16 | 17 | while : 18 | do 19 | speak 20 | sleep 30 21 | done -------------------------------------------------------------------------------- /02-find-emails/README.md: -------------------------------------------------------------------------------- 1 | Find lines of an html file that contain an email. I recommend using an imperfect regex to match for emails. There aren't any in there trying to trip you up—the simple regex will probably work. 2 | 3 | Read the `grep` man page to find how to make it print only the matching portion of a line (just the email with none of the surrounding text). 4 | 5 | Write a `find` command to list all `.html` files (imagine there are more than three, so this step would actually be worth your time 😅). 6 | 7 | Consider using `find ... | xargs grep ...`, which says "Take each line of output from the `find` command and add it as an additional argument to `grep ...`". 8 | 9 | Present the final list of emails in sorted order, all lowercase, and with duplicates removed. -------------------------------------------------------------------------------- /03-count-lines-of-code/README.md: -------------------------------------------------------------------------------- 1 | # Count Lines of Code 2 | 3 | Clone down a repo of your choice. Write a command to list the names of every file, even in nested subdirectories, that represents a source file. I recommend `find`. 4 | 5 | Separately, write a command (or pipeline of commands) to print out the number of lines that are non-blank and aren't comment lines (only concern yourself with line comments (eg, `//` or `#`) and not block comments (`/*` or `(*`). Block comments make this a fair bit harder. I recommend `wc` and `sed` or `awk` for this. 6 | 7 | Finally, put the two pieces together! You can use `xargs cat` for this. `xargs` "transposes" the lines of input its given into arguments for a new command. So you should be able to do something like this: 8 | 9 | ```bash 10 | LIST_ALL_SOURCE_FILES | 11 | xargs cat | 12 | COUNT_ALL_NON_COMMENT_NON_BLANK_LINES 13 | ``` 14 | -------------------------------------------------------------------------------- /xx-holey-polygons/polygon-with-a-hole.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Feature", 3 | "properties": {}, 4 | "geometry": { 5 | "type": "Polygon", 6 | "coordinates": [ 7 | [ 8 | [ 9 | -120, 10 | 60 11 | ], 12 | [ 13 | 120, 14 | 60 15 | ], 16 | [ 17 | 120, 18 | -60 19 | ], 20 | [ 21 | -120, 22 | -60 23 | ], 24 | [ 25 | -120, 26 | 60 27 | ] 28 | ], 29 | [ 30 | [ 31 | -60, 32 | 30 33 | ], 34 | [ 35 | 60, 36 | 30 37 | ], 38 | [ 39 | 60, 40 | -30 41 | ], 42 | [ 43 | -60, 44 | -30 45 | ], 46 | [ 47 | -60, 48 | 30 49 | ] 50 | ] 51 | ] 52 | } 53 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shell-challenges 2 | 3 | I find ad-hoc shell pipelines to be a fun and efficient way to explore and manipulate data. However, they're not particularly easy to get started with. If you don't already feel comfortable with this hammer, you won't notice when you could solve a problem with it. 4 | 5 | In this series, I hope to introduce shell commands so you can start seeing when you might apply them. 6 | 7 | 8 | > Don't actually type the `$` part. You only want to type the bit that comes afterwards. The `$` at the beginning of a line represents the _prompt_, a symbol the shell prints to say "go ahead and type here". 9 | 10 | ## Further reading 11 | 12 | more shell, less egg 13 | https://apple.stackexchange.com/a/195387/270535 14 | 15 | ## Ideas for challenges 16 | 17 | - Kill a process 18 | - Explore path with tr, add ~/bin 19 | - Redirects 20 | - Find lines with trailing whitespace, fix them 21 | - Count SLOC 22 | -------------------------------------------------------------------------------- /05-file-version-sha/README.md: -------------------------------------------------------------------------------- 1 | I recently needed to come up with a version string for a collection of files. It would have been possible to manually keep track of a version, ticking it up whenever a file changed. But what can we do with shell? Here are the requirements: 2 | 3 | 1. The version string (which can be any reasonable-length string) should change whenever any of my source files changes. It doesn't need to "increase" though, just change. 4 | 2. The solution should be able to handle files being added or deleted. 5 | 3. Only Python files should affect the version string, not README or other non-code files. Oh, but there's also a shell script that we want to include. 6 | 7 | I recommend using the `shasum` program, which should be already available on your machine (it might be called `sha1sum` or something like that). 8 | 9 | Here's some steps for a possible pipeline: 10 | 11 | 1. list all .py files, plus `start.sh` 12 | 2. concatenate them together 13 | 3. `shasum`s output includes a listing of the name of the file. We only want the hash part, so strip out the filename somehow. 14 | 15 | -------------------------------------------------------------------------------- /04-kill-process-by-name/README.md: -------------------------------------------------------------------------------- 1 | Kill process by name 2 | 3 | First, run the `die-hard.sh` script found in this directory. 4 | 5 | Sometimes a process will rudely refuse to listen to a Ctrl-C. Or maybe it's a background process, and there's nowhere to send it a ctrl-c. 6 | 7 | In those cases, you can use the `kill` program to send it a SIGKILL signal that will stop it right away. But first you need to know its PID (process ID). 8 | 9 | Run `ps ax` to get a list of all running processes on your computer. Pipe that into grep to find the offending process. This should be a much shorter list of processes (one or two probably). 10 | 11 | At this point, you _could_ copy-paste the PID of the offending process and run `kill ` on it. But this is shell class! Let's do it with shell commands. 12 | 13 | We need to isolate just the PID column. Use the `cut` program to grab just the first column of your output. You'll likely need to specify the `-f` (field) and `-d` (delimiter) flags. 14 | 15 | Finally, pipe the whole thing into `xargs kill`. You may need to experiment with flags passed into `kill` to ensure that you're sending a SIGKILL and not a SIGTERM (the default). 16 | -------------------------------------------------------------------------------- /xx-holey-polygons/README.md: -------------------------------------------------------------------------------- 1 | # Holey Polygons 2 | 3 | There are a number of geojson files representing buildings at https://github.com/microsoft/USBuildingFootprints 4 | 5 | We need to know whether these geojson polygons include holes. A polygon in geojson is represented by a list of rings (lists of coordinate pairs). The first ring is always the exterior. Subsequent rings, if there are any, represent holes removing area from the outer ring. 6 | 7 | ```js 8 | { 9 | type: "Polygon", 10 | coordinates: [ 11 | // ring 1, the exterior 12 | [ [ -120, 60 ], [ 120, 60 ], [ 120, -60 ], [ -120, -60 ], [ -120, 60 ] ], 13 | // ring 2, the hole 14 | [ [ -60, 30 ], [ 60, 30 ], [ 60, -30 ], [ -60, -30 ], [ -60, 30 ] ] 15 | ], 16 | } 17 | ``` 18 | 19 | So the problem of deciding "Does this polygon have a hole?" can be simplified to "Does the geojson for this polygon have more than one ring?" 20 | 21 | Let's do a couple warm-ups first to get familiar with the tool we'll be using: `jq`. 22 | 23 | ### Warm-up 1: How many polygons? 24 | 25 | Using `jq`, find the total number of polygons in one of the files. You can check your work 26 | 27 | #### 28 | ## answer: 29 | 30 | ```bash 31 | < Colorado.geojson jq '.features | map(select((.geometry.coordinates | length) > 1))' 32 | ``` -------------------------------------------------------------------------------- /06-tailing-logs/mk-logs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import threading 3 | import time 4 | import random 5 | import datetime 6 | 7 | fruit = 'pineapple grape coconut tangerine grapefruit apple pear cranberry lemon lime peach apricot'.split() 8 | vegetable = 'brocolli green_bean carrot celery arugula romaine brussels_sprout cauliflower peas'.split() 9 | sql = [ 10 | "SELECT * FROM user", 11 | "UPDATE user SET name = 'Heidi' WHERE id = 12", 12 | "DELETE FROM furniture WHERE NOT comfortable", 13 | "INSERT INTO skills VALUES ('juggling'), ('snowshoeing')" 14 | ] 15 | access = [ 16 | "/index.html", 17 | "/favicon.ico", 18 | "/robots.txt", 19 | "/wp-admin/", 20 | "/home", 21 | "/humans.txt"] 22 | 23 | def write_logs(name, lst, sleep_secs): 24 | while True: 25 | w = random.choice(lst) 26 | with open('logs/' + name + '.log', 'a') as f: 27 | f.write('{} {}\n'.format(datetime.datetime.now().isoformat(), w)) 28 | time.sleep(sleep_secs + abs(random.normalvariate(0, 1))) 29 | 30 | args = [ 31 | ('fruit', fruit, 2), 32 | ('vegetable', vegetable, 2), 33 | ('sql', sql, 3), 34 | ('access', access, 0.1), 35 | ] 36 | 37 | threads = [] 38 | 39 | for arg in args: 40 | t = threading.Thread(target=write_logs, args=arg) 41 | t.daemon = True 42 | threads.append(t) 43 | t.start() 44 | 45 | print("writing logs...") 46 | try: 47 | while True: 48 | time.sleep(0.25) 49 | except KeyboardInterrupt: 50 | pass -------------------------------------------------------------------------------- /xx-holey-polygons/no-holes-here.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type":"FeatureCollection", 3 | "features": 4 | [ 5 | {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-77.014904,38.816248],[-77.014842,38.816395],[-77.015056,38.816449],[-77.015117,38.816302],[-77.014904,38.816248]]]},"properties":{}}, 6 | {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-77.008635,38.819964],[-77.008619,38.819878],[-77.008703,38.819868],[-77.008687,38.819784],[-77.008488,38.819807],[-77.008503,38.819888],[-77.008342,38.819906],[-77.008298,38.819665],[-77.008094,38.819688],[-77.008111,38.81978],[-77.00821,38.819769],[-77.008254,38.820006],[-77.008635,38.819964]]]},"properties":{}}, 7 | {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-77.008323,38.820252],[-77.008368,38.820509],[-77.008292,38.820517],[-77.008306,38.8206],[-77.008503,38.820579],[-77.008458,38.820325],[-77.008608,38.820309],[-77.00862,38.820378],[-77.00882,38.820356],[-77.008804,38.820266],[-77.008708,38.820276],[-77.008697,38.820212],[-77.008323,38.820252]]]},"properties":{}}, 8 | {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-77.08184,38.959498],[-77.081704,38.959498],[-77.081704,38.959582],[-77.08184,38.959582],[-77.08184,38.959498]]]},"properties":{}}, 9 | {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-77.061,38.977646],[-77.061139,38.977646],[-77.061139,38.977581],[-77.061,38.977581],[-77.061,38.977646]]]},"properties":{}}, 10 | {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-77.043374,38.9859],[-77.043285,38.985869],[-77.043231,38.985963],[-77.043406,38.986025],[-77.043452,38.985946],[-77.043365,38.985915],[-77.043374,38.9859]]]},"properties":{}} 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /presentation.md: -------------------------------------------------------------------------------- 1 | build-lists: true 2 | 3 | # [fit] Intermediate Shell 4 | 5 | --- 6 | 7 | ### Preparation: 8 | 9 | ``` 10 | brew install fortune cowsay lolcat jq 11 | ``` 12 | --- 13 | 14 | ## Prerequisites 15 | 16 | You should already know (but in case you don't) 17 | 18 | 19 | | *Command* | *Description* | 20 | |-----------|----------------------------| 21 | | `cd` | change directory | 22 | | `ls` | list files and directories | 23 | | `pwd` | display full path of current directory | 24 | | `mkdir` | create a directory | 25 | | `rm` | remove a directory | 26 | | `cp` | copy a file or directory | 27 | | `mv` | move or rename a file or directory | 28 | | `echo` | print text to STDOUT | 29 | | `cat` | print contents of a file (or multiple) | 30 | | `less` | page through a file or STDIN | 31 | | `head` | display the first few lines of a file | 32 | | `tail` | display the last few lines of a file | 33 | | `ctrl-C` | cancel an in-progress command | 34 | 35 | 36 | --- 37 | 38 | ### [fit] The Spirit of the command line 39 | 40 | --- 41 | 42 | ![](./images/sorcerer-mickey-final-dribbble_2x.png) 43 | ![](./images/the-dragon-prince-season-1-26258-1200.jpg) 44 | 45 | --- 46 | 47 | > “ugh, I could write a program to do this for me!” 48 | 49 | --- 50 | 51 | ### Moving around efficiently 52 | 53 | - ctrl-a, ctrl-e, ctrl-w, ctrl-u, option-arrow 54 | - ctrl-x,e to open in $EDITOR 55 | - up-arrow 56 | - ctrl-r, `history | grep` 57 | - tab completion 58 | - drag files to terminal 59 | 60 | --- 61 | [.autoscale: true] 62 | ### Does the walker choose the $PATH, or the $PATH the walker? 63 | 64 | - Make a file called `hello` that has 65 | 66 | ```bash 67 | #!/usr/bin/env sh 68 | say hello there 69 | ``` 70 | 71 | - from this directory, run `hello` (no `./`). Probably you get an error message. 72 | - inspect your PATH by running `echo $PATH | tr : '\n'` 73 | - add `.` (the current directory) to your PATH: `export PATH=$PATH:.`. Now run `hello` again. 74 | - rename the file to `ls`. What happens when you run `ls` now? 75 | 76 | --- 77 | 78 | ### combining functionality: Pipes 79 | 80 | 1. Explore individually `fortune`, `cowsay`, `lolcat`. Use ` --help` or `man ` if you get stuck. I recommend trying to pipe things into these commands with echo (eg, `echo hello | lolcat`) 81 | 2. Write down a brief english description of what each command does. How does it transform its input? Compare notes with a neighbor. 82 | 83 | --- 84 | 85 | # [fit] hacking time 86 | ![autoplay loop](./images/hack-time.mp4) 87 | 88 | --- 89 | 90 | # [fit] github.com/bgschiller/shell-challenges -------------------------------------------------------------------------------- /07-jq-and-redirects/README.md: -------------------------------------------------------------------------------- 1 | Run `curl 'https://api.github.com/repos/Denver-Devs/denverdevs.org/commits?per_page=100'`. 2 | 3 | Ugh! What am I supposed to do with all that output? It scrolled past faster than I could read it. Let's try again. Run `curl 'https://api.github.com/repos/Denver-Devs/denverdevs.org/commits?per_page=100' | jq '.[0]'`, which says "take the output from curl and extract the element at key '0' (the first entry in the array). 4 | 5 | That's at least shorter. But all I really care about right now is the committer name. Before we refine our query though, let's make a structural change. It makes no sense to keep asking github for this json file over and over again. Let's save it and re-use it. Run `curl 'https://api.github.com/repos/Denver-Devs/denverdevs.org/commits?per_page=100' > commits.json`. This says "Take the stdout from curl and write it to the commits.json file". 6 | 7 | Now, instead of running `curl` every time we tweak our `jq` script, we can redirect that file to stdin instead: `< commits.json jq '.[0]'`. Faster _and_ nicer to the github api. 8 | 9 | Run `< commits.json jq '.commit.committer.name'`. You should get a big long list of names. Sort and deduplicate them, and then present them in descending order of frequency. When I'm running this on Nov 26, 2019, I get the following: 10 | 11 | ``` 12 | 50 "GitHub" 13 | 47 "Dan Hannigan" 14 | 2 "Derik Linch" 15 | 1 "Brian Schiller" 16 | ``` 17 | 18 | One thing that's funny about jq is that is uses pipe characters (`|`) in its query language. But, as we've seen, the pipe is an important character to the shell itself. We will usually need to quote our `jq` query in single quotes, so that the shell can tell which pipes are for it and which ones are for `jq`. 19 | 20 | There sure are a lot of commits attributed to "GitHub". That seems funny. Use `jq`'s `select()` function to limit commits to _only_ those where the `.commit.committer.name` is "GitHub", and see what's going on with them (You may need to refer to the [jq manual](https://stedolan.github.io/jq/manual/). Is there some other key we should be using instead of `.commit.committer.name`? Once I'd sorted out this problem, I got the following: 21 | 22 | ``` 23 | 69 "Dan Hannigan" 24 | 10 "Gabi Procell" 25 | 7 "dependabot[bot]" 26 | 3 "James Gibson" 27 | 2 "Jason Rist" 28 | 2 "Derik Linch" 29 | 1 "amng9560" 30 | 1 "Ted Summer" 31 | 1 "Matt Lewis" 32 | 1 "Kevin McKernan" 33 | 1 "Hamp Goodwin" 34 | 1 "Chuck Harmston" 35 | 1 "Brian Schiller" 36 | ``` 37 | 38 | Much better! Something jumped out at me when I was reviewing this data: some of the commits are signed! What kind of crypto nerds are out here signing their commits to denverdevs.org? To find out, we'll need to filter the data down to only verified commits. 39 | 40 | Since this is still exploratory analysis, I kind of want to see all the data again. But not like a caveman! I want it nicely paged, scrollable, and searchable with `less`. If you do `jq ... | less` then `jq` will notice that you're piping its output into a program, not actually displaying it, and so it will strip all the color from its output. (The ascii escape codes used to color text in the terminal can mess up programs that don't expect them). But we still want the color, so pass the `--color-output` flag to jq and we'll be able to use `less` and still keep color. 41 | 42 | > Protip: I have `jl` aliased to `jq '.' --color-output | less` in my terminal. That lets me do `pbpaste | jl` and see colorized, paginated json right away. 43 | 44 | Once you come up with a hypothesis for what the verified commits have in common, write another `jq` command to see if you're correct. 45 | -------------------------------------------------------------------------------- /01-xkcd-password/README.md: -------------------------------------------------------------------------------- 1 | # xkcd Password Generator 2 | 3 | Prompted by the xkcd comic strip, you decide to make a shell pipeline to suggest passwords consisting of 4 random common words. 4 | 5 | 6 | 7 | 8 | 9 | ## Get the words 10 | 11 | First things first, let's download some words. You already know about `/usr/share/dict/words`, but some of those are really obscure. You want a _curated_ list. 12 | 13 | Do some googling to find a list that appeals to you, or use the one at `https://raw.githubusercontent.com/first20hours/google-10000-english/master/google-10000-english-usa-no-swears.txt`. 14 | 15 | You should be able to download it with 16 | 17 | ``` 18 | curl https://raw.githubusercontent.com/first20hours/google-10000-english/master/google-10000-english-usa-no-swears.txt > words 19 | ``` 20 | 21 | curl is a program for making HTTP requests. When called with just a URL and no other arguments, it makes a GET request and outputs the response body to stdout. we're also using shell redirection `> words` to say "the stdout of this curl command should be sent to a file called 'words'". 22 | 23 | ## Trim down the list 24 | 25 | One of the first things we'll want to do is throw away words that are too long or too short. We don't want to make our passwords too difficult to remember, or too easy to guess! You can change this up as you want, but I'm going to use 5-8 letter words as the sweet spot. 26 | 27 | How can we filter down the list? We want to keep only lines that have 5, 6, 7, or 8 characters in them. We can use `grep` to keep only lines that match a pattern. Here's the pattern we'll look for: 28 | 29 | 1. The beginning of the line `^` 30 | 2. A single letter `[A-Za-z]` 31 | 3. A single letter `[A-Za-z]` 32 | 4. A single letter `[A-Za-z]` 33 | 5. A single letter `[A-Za-z]` 34 | 6. A single letter `[A-Za-z]` 35 | 8. Either a single letter, or nothing `[A-Za-z]?` 36 | 9. Either a single letter, or nothing `[A-Za-z]?` 37 | 10. Either a single letter, or nothing `[A-Za-z]?` 38 | 11. The end of the line `$` 39 | 40 | Why do we need to specifically call out the beginning and end of the line? `grep` will include a line if a match is found _anywhere_ in the line. If we didn't require the beginning and end of the line to be on either side of our pattern, we could end up with a 100-letter word, because `grep` noticed that the long word's first eight letters matched our pattern. 41 | 42 | The `[A-Za-z]` is a character class that includes every uppercase and lowercase letter. The question mark modifies it, adding on "or nothing is okay too". Here's that regex all on one line the way we would actually write it: 43 | 44 | ``` 45 | ^[A-Za-z][A-Za-z][A-Za-z][A-Za-z][A-Za-z][A-Za-z]?[A-Za-z]?[A-Za-z]?$ 46 | ``` 47 | 48 | This doesn't look very... efficient. In fact, there are a couple of shorthands we can use to avoid typing so much. First, there's a regex syntax for repeating a pattern: you tack `{min,max}` onto the end. So our regex will now look like: 49 | 50 | ``` 51 | ^[A-Za-z]{5,8}$ 52 | ``` 53 | 54 | Much better! If we're okay with a small change in exactly what the pattern means, we can do even better. There are some character classes used so often that there are built-in shorthands for them. One of those is `[A-Za-z0-9_]`: upper- and lowercase letters, digits, and underscores. The shorthand is `\w` If we're okay with allowing digits and underscores in our words, we can write our regex as: 55 | 56 | ``` 57 | ^\w{5,8}$ 58 | ``` 59 | 60 | Nice! That's starting to look cryptic, as a proper regex should. Let's use it. 61 | 62 | The syntax for `grep` is `grep PATTERN FILE`. We'll also have to use the `-E` flag for "extended regex". So we can run 63 | 64 | ```bash 65 | grep -E "^\w{5,8}$" words 66 | ``` 67 | 68 | This should print a great many words, but if all goes well only ones with between 5 and 8 letters. Be sure to use quotes around the regex pattern. Otherwise your shell will eat the backslash and treat the `{5,8}` bit a bit strangely too. We'll cover that in a later lesson. 69 | 70 | If you want to not overflow your shell, you can use the `head` command as part of a pipeline: 71 | 72 | ```bash 73 | grep -E "^\w{5,8}$" words | head 74 | ``` 75 | 76 | A pipeline is a fun construct in shell scripting that lets us string together many transformations to some data without writing to a file between each one. This one says "make the output of `grep` the input of `head`". So, whatever `grep` produces (a list of words of the proper length) is what `head` will be given to act on. `head`'s function is to print the first 10 lines of a file and no more. 77 | 78 | ## Put it in a random order 79 | 80 | We will use the `sort` program to randomize the order of our list. By default, `sort` will sort its input. We're going to use the `--random-sort` flag to tell it to sort randomly instead. 81 | 82 | Remember that pipeline trick, with `head`? We'll use the same sort of thing again. We want to use the output of `grep` as the input to `sort`. 83 | 84 | ```bash 85 | grep -E "^\w{5,8}$" words | sort --random-sort 86 | ``` 87 | 88 | ### Alternative: `shuf` 89 | 90 | If you have GNU coreutils installed, you can use `shuf` which uses a more efficient algorithm for shuffling than `sort --random-sort`. We'll also explore a more "from scratch" way of doing this in a later challenge, when we pretend we don't know about `shuf` or `sort --random-sort`. 91 | 92 | ## Only take a few 93 | 94 | We've seen already how to limit the output of our pipeline using `head`. As it happens, `head` also accepts a parameter for the number of lines: `-n`. Use this flag to only keep the first four lines of the randomly sorted words. 95 | 96 | ## Put them all on one line 97 | 98 | As it is now, the words are all on separate lines. Let's put them all on one line with the `tr` command, short for "translate". We'll translate all newline characters `"\n"` to spaces `" "`. 99 | 100 | ## All together now 101 | 102 | ```bash 103 | grep -E "^\w{5,8}$" words | # Only 5-8 letter words 104 | sort --random-sort | # in a random order 105 | head -n 4 | # just the first 4 106 | tr "\n" " " # replace newlines with spaces 107 | ``` 108 | 109 | > tourism jelsoft digest riders -------------------------------------------------------------------------------- /xx-holey-polygons/has-holes.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "geometry": { 7 | "type": "Polygon", 8 | "coordinates": [ 9 | [ 10 | [ 11 | -76.989211, 12 | 38.89398 13 | ], 14 | [ 15 | -76.98921, 16 | 38.893691 17 | ], 18 | [ 19 | -76.989036, 20 | 38.893691 21 | ], 22 | [ 23 | -76.989037, 24 | 38.893915 25 | ], 26 | [ 27 | -76.988921, 28 | 38.893915 29 | ], 30 | [ 31 | -76.988922, 32 | 38.893981 33 | ], 34 | [ 35 | -76.989211, 36 | 38.89398 37 | ] 38 | ], 39 | [ 40 | [ 41 | -76.98914855718613, 42 | 38.89390749592519 43 | ], 44 | [ 45 | -76.98914855718613, 46 | 38.89375510170056 47 | ], 48 | [ 49 | -76.98908150196075, 50 | 38.89375510170056 51 | ], 52 | [ 53 | -76.98908686637877, 54 | 38.89390123315527 55 | ], 56 | [ 57 | -76.98914855718613, 58 | 38.89390749592519 59 | ] 60 | ] 61 | ] 62 | }, 63 | "properties": { 64 | "has-a-hole": "you found it!" 65 | } 66 | }, 67 | { 68 | "type": "Feature", 69 | "geometry": { 70 | "type": "Polygon", 71 | "coordinates": [ 72 | [ 73 | [ 74 | -76.987576, 75 | 38.899513 76 | ], 77 | [ 78 | -76.987576, 79 | 38.899432 80 | ], 81 | [ 82 | -76.987539, 83 | 38.899431 84 | ], 85 | [ 86 | -76.987539, 87 | 38.8994 88 | ], 89 | [ 90 | -76.987434, 91 | 38.8994 92 | ], 93 | [ 94 | -76.987434, 95 | 38.899437 96 | ], 97 | [ 98 | -76.987195, 99 | 38.899436 100 | ], 101 | [ 102 | -76.987195, 103 | 38.899511 104 | ], 105 | [ 106 | -76.987576, 107 | 38.899513 108 | ] 109 | ] 110 | ] 111 | }, 112 | "properties": {} 113 | }, 114 | { 115 | "type": "Feature", 116 | "geometry": { 117 | "type": "Polygon", 118 | "coordinates": [ 119 | [ 120 | [ 121 | -76.986613, 122 | 38.89935 123 | ], 124 | [ 125 | -76.986414, 126 | 38.899407 127 | ], 128 | [ 129 | -76.986515, 130 | 38.899621 131 | ], 132 | [ 133 | -76.986672, 134 | 38.899576 135 | ], 136 | [ 137 | -76.986649, 138 | 38.899528 139 | ], 140 | [ 141 | -76.986569, 142 | 38.899551 143 | ], 144 | [ 145 | -76.986512, 146 | 38.89943 147 | ], 148 | [ 149 | -76.986635, 150 | 38.899395 151 | ], 152 | [ 153 | -76.986613, 154 | 38.89935 155 | ] 156 | ] 157 | ] 158 | }, 159 | "properties": {} 160 | }, 161 | { 162 | "type": "Feature", 163 | "geometry": { 164 | "type": "Polygon", 165 | "coordinates": [ 166 | [ 167 | [ 168 | -76.959141, 169 | 38.900827 170 | ], 171 | [ 172 | -76.959357, 173 | 38.90082 174 | ], 175 | [ 176 | -76.959346, 177 | 38.900598 178 | ], 179 | [ 180 | -76.959129, 181 | 38.900605 182 | ], 183 | [ 184 | -76.959141, 185 | 38.900827 186 | ] 187 | ] 188 | ] 189 | }, 190 | "properties": {} 191 | }, 192 | { 193 | "type": "Feature", 194 | "geometry": { 195 | "type": "Polygon", 196 | "coordinates": [ 197 | [ 198 | [ 199 | -76.94937, 200 | 38.900818 201 | ], 202 | [ 203 | -76.94946, 204 | 38.900888 205 | ], 206 | [ 207 | -76.949547, 208 | 38.90082 209 | ], 210 | [ 211 | -76.949457, 212 | 38.90075 213 | ], 214 | [ 215 | -76.94937, 216 | 38.900818 217 | ] 218 | ] 219 | ] 220 | }, 221 | "properties": {} 222 | }, 223 | { 224 | "type": "Feature", 225 | "geometry": { 226 | "type": "Polygon", 227 | "coordinates": [ 228 | [ 229 | [ 230 | -76.951261, 231 | 38.906788 232 | ], 233 | [ 234 | -76.951147, 235 | 38.907258 236 | ], 237 | [ 238 | -76.951654, 239 | 38.907332 240 | ], 241 | [ 242 | -76.95168, 243 | 38.907222 244 | ], 245 | [ 246 | -76.951322, 247 | 38.90717 248 | ], 249 | [ 250 | -76.951409, 251 | 38.90681 252 | ], 253 | [ 254 | -76.951261, 255 | 38.906788 256 | ] 257 | ] 258 | ] 259 | }, 260 | "properties": {} 261 | }, 262 | { 263 | "type": "Feature", 264 | "geometry": { 265 | "type": "Polygon", 266 | "coordinates": [ 267 | [ 268 | [ 269 | -76.960721, 270 | 38.892617 271 | ], 272 | [ 273 | -76.960701, 274 | 38.892653 275 | ], 276 | [ 277 | -76.960656, 278 | 38.892637 279 | ], 280 | [ 281 | -76.96063, 282 | 38.892684 283 | ], 284 | [ 285 | -76.960786, 286 | 38.892738 287 | ], 288 | [ 289 | -76.960832, 290 | 38.892655 291 | ], 292 | [ 293 | -76.960721, 294 | 38.892617 295 | ] 296 | ] 297 | ] 298 | }, 299 | "properties": {} 300 | }, 301 | { 302 | "type": "Feature", 303 | "geometry": { 304 | "type": "Polygon", 305 | "coordinates": [ 306 | [ 307 | [ 308 | -76.950635, 309 | 38.89177 310 | ], 311 | [ 312 | -76.950731, 313 | 38.89177 314 | ], 315 | [ 316 | -76.950731, 317 | 38.891657 318 | ], 319 | [ 320 | -76.950635, 321 | 38.891657 322 | ], 323 | [ 324 | -76.950635, 325 | 38.89177 326 | ] 327 | ] 328 | ] 329 | }, 330 | "properties": {} 331 | }, 332 | { 333 | "type": "Feature", 334 | "properties": {}, 335 | "geometry": { 336 | "type": "Polygon", 337 | "coordinates": [ 338 | [ 339 | [ 340 | -76.9894677400589, 341 | 38.894147568355514 342 | ], 343 | [ 344 | -76.98947846889496, 345 | 38.89396177324133 346 | ], 347 | [ 348 | -76.98928803205489, 349 | 38.89396177324133 350 | ], 351 | [ 352 | -76.98927998542786, 353 | 38.8941538311037 354 | ], 355 | [ 356 | -76.9894677400589, 357 | 38.894147568355514 358 | ] 359 | ], 360 | [ 361 | [ 362 | -76.9893953204155, 363 | 38.894093291181385 364 | ], 365 | [ 366 | -76.98933899402618, 367 | 38.894095378765776 368 | ], 369 | [ 370 | -76.98934435844421, 371 | 38.894009787755586 372 | ], 373 | [ 374 | -76.98940873146056, 375 | 38.894009787755586 376 | ], 377 | [ 378 | -76.9893953204155, 379 | 38.894093291181385 380 | ] 381 | ] 382 | ], 383 | "properties": { 384 | "has-a-hole": "you found it!" 385 | } 386 | 387 | } 388 | } 389 | ] 390 | } -------------------------------------------------------------------------------- /02-find-emails/wwu-depts/geology.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Faculty & Staff Directory - Western Washington University 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 |
25 | 64 |
65 | 87 |
88 | 89 |
90 | 91 |

Faculty & Staff Directory

92 |

Search Results for "Geology"

93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 |
LastFirstPhoneOfficeMS
Job TitleDeptEmail
AmosColin3587ES441MS9080
Associate ProfessorGeology
BabcockScottMS
Geology
BeckMyrl3582ES240MS9080
Geology
BergRichardMS
Geology
BlizzardKate6515ES240MS9080
Admin Services Manager BGeology
BrownNed3582ES240MS9080
Geology
BurdickAidan4127ES56MS9080
Grad Teaching/Research AsstGeology
BurmesterRussES429MS9080
Geology
Caplan-AuerbachJackie4153ES236MS9080
ProfessorGeology
ClarkDoug7939ES210MS9080
Associate ProfessorGeology
DahlRobyn7207ES340MS9080
Assistant ProfessorGeology
DeBariSusan3588ES237MS9080
ProfessorGeology
EasterbrookDonMS
Geology
EngebretsonDavidMS
Geology
ForemanBrady2546ES202MS9080
Assistant ProfessorGeology
Garcia LasantaCristina3835ES104MS9080
PMag Mgr & Research ScientistGeology
HansenThorMS
InstructorGeology
HousenBernard6573ES425MS9080
ProfessorGeology
JensenChantel4127ES56MS9080
Grad Teaching/Research AsstGeology
MitchellRobert3591ES234MS9080
ProfessorGeology
MulcahySean3645ES235MS9080
Assistant ProfessorGeology
ParkHye In6516ES240MS9080
Program CoordinatorGeology
PaulsonBen3585ES208MS9080
Instruct/Clsrm Support Tech 3Geology
Perry-HoutsJonathan3597ES203MS9080
InstructorGeology
PfeifferAllison3654ES339MS9080
Assistant ProfessorGeology
PontonCamilo3648ES204MS9080
Assistant ProfessorGeology
RiceMelissa3592ES205MS9080
Associate ProfessorGeology
RuskBrian3595ES114MS9080
InstructorGeology
SchermerLiz3658ES438MS9080
ProfessorGeology
SchiavettiLucas4198ES67MS4198
Geology
StellingPete4095ES439MS9080
Associate ProfessorGeology
TalbotJames3582ES240MS9080
Geology
ToddDelaney4198ES67MS9080
Grad Teaching/Research AsstGeology
502 |

503 | Unless otherwise noted, (area code)-prefix is (360)-650. 504 |

505 |

506 | If you notice an incorrect listing, changes can be requested by submitting the 507 | Employee 508 | Campus (OF) Address Update form. 509 |

510 | 511 | 512 |

513 | Use of this directory for commercial purposes is prohibited per Washington Code 42.56. 514 |

515 |

 [ Back to Top ]

516 | 517 |
518 |
519 |

Search by name

520 |
521 | 522 | 523 |
524 | 525 |

Browse by department

526 | 902 |

Browse by last name

903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 |
ABCDEFGHIJKLM
NOPQRSTUVWXYZ
935 |
936 |
937 |
938 |
939 | 951 |
952 |
953 | 957 |
958 | 959 | 960 | -------------------------------------------------------------------------------- /02-find-emails/wwu-depts/mathematics.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Faculty & Staff Directory - Western Washington University 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 |
25 | 64 |
65 | 87 |
88 | 89 |
90 | 91 |

Faculty & Staff Directory

92 |

Search Results for "Mathematics"

93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 |
LastFirstPhoneOfficeMS
Job TitleDeptEmail
AmiranEdoh3487BH220MS9063
Associate ProfessorMathematics
AndersonAmy3799BH172MS9063
Associate ProfessorMathematics
AndersonVictoria3793BH178MS9063
Senior InstructorMathematics
BanhamTimothy2296BH423MS9063
InstructorMathematics
BarnardRick2334BH184BMS9063
Assistant ProfessorMathematics
BenyiArpad3710BH218MS9063
ProfessorMathematics
BergetAndrew4510BH214MS9063
Associate ProfessorMathematics
BlakeGeneva2296BH423MS9063
Senior InstructorMathematics
BorowskiRebecca4232BH436MS9063
Assistant ProfessorMathematics
BronesJohn2296BH423MS9063
InstructorMathematics
ChaliceDonaldMS
Mathematics
ChanVictor7260BH176MS9063
Associate ProfessorMathematics
ChintagavongseNathanial4835BH229MS9063
Grad Teaching/Research AsstMathematics
ClarkKatie3785BH202MS9063
InstructorMathematics
CohenJessica3830BH180MS9063
Associate ProfessorMathematics
CraswellKeithMS9063
InstructorMathematics
CurgusBranko3484BH178MS9063
ProfessorMathematics
DeeneyMegan3453BH428CMS9063
Senior InstructorMathematics
DownardTeresa2911BH175MS9063
Senior InstructorMathematics
FetherolfAllyn4231BH428DMS9063
InstructorMathematics
GainesMelissa3785BHMS9063
Office Assistant 3Mathematics
GardnerRichardMS9063
Mathematics
GlimmTilmann2933BH228MS9063
ProfessorMathematics
GoodAndrew3467BH192MS9063
Senior InstructorMathematics
HanseyDiane2911BH175MS9063
InstructorMathematics
HarrisonNicholas3785BH202MS9063
Grad Teaching/Research AsstMathematics
HartenstineDavid6520BH190MS9063
ProfessorMathematics
HixsonAmber3796BH236MS9063
Senior InstructorMathematics
JewettRobertMS
Mathematics
JohnsonJerryMS
Mathematics
JohnsonMillie3459BH213MS9063
Mathematics
KeanEric4271BH428EMS9063
Senior InstructorMathematics
KimHee Jung4274BH224MS9063
Visiting ProfessorMathematics
KuzishchinArtem3785BH202MS9063
Laboratory Assistant 1Mathematics
MarkworthKimberly6284BH242MS9063
Associate ProfessorMathematics
MarleyTom3814BH208MS9063
InstructorMathematics
McCormickHalley3785BH202MS9063
Grad Teaching/Research AsstMathematics
McDowallStephen4272BH186MS9063
ProfessorMathematics
McKeownMichelle3785BH202MS9063
InstructorMathematics
McMullenJamie2216BH194MS9063
Program Support Staff 3Mathematics
MeierJeffrey2988BH212MS9063
Assistant ProfessorMathematics
MockJanet7609BH434MS9063
Senior InstructorMathematics
MurrayWilliam3785BH202MS9063
Grad Teaching/Research AsstMathematics
NimtzJen3801BH430MS9063
Assistant ProfessorMathematics
NoguchiKimihiro2614BH174MS9063
Associate ProfessorMathematics
NuckollsSeth3814BH208MS9063
Senior InstructorMathematics
NymanAdam3464BH232MS9063
ProfessorMathematics
OhanaChris3802BH404MS9063
Supplemental RetireeMathematics
PeiYuan3810BH188MS9063
Assistant ProfessorMathematics
Piyadi GamageRamadha4955BH182MS9063
Assistant ProfessorMathematics
RagsdaleKim3813BH210MS9063
Senior InstructorMathematics
ReadTomMS
Mathematics
RichardsonAndrew4125BH240MS9063
Senior InstructorMathematics
SarkarAmites7569BH216MS9063
ProfessorMathematics
SchivitzBeth4834BH233MS9063
InstructorMathematics
SchwartzGreg3786BH425MS9063
Senior InstructorMathematics
ScolariAndrea3785BH202MS9063
Grad Teaching/Research AsstMathematics
ScottDaphne3815BH432MS9063
Senior InstructorMathematics
ShenYunqiu2858BH222MS9063
ProfessorMathematics
SherwoodTeresa7212BH202BMS9063
Admin Services Manager BMathematics
Smit Vega GarciaMariana3454BH230MS9063
Assistant ProfessorMathematics
St. ClairScott4385BH229MS9063
Grad Teaching/Research AsstMathematics
StablesKatie3474BH238MS9063
Senior InstructorMathematics
TreneerStephanie3468BH206MS9063
Associate ProfessorMathematics
WatsonAnika3785BH202MS9063
Grad Teaching/Research AsstMathematics
WhalenMontgomery3785BH202MS9063
Grad Teaching/Research AsstMathematics
WhelchelSarah4836BH231MS9063
Program Support Staff 1Mathematics
WinbrinckLindsay4836BH231MS9063
Grad Teaching/Research AsstMathematics
WollJohnMS
Mathematics
YpmaTjalling3785BH202AMS9063
ProfessorMathematics
ZhangJianying7259BH226MS9063
Associate ProfessorMathematics
958 |

959 | Unless otherwise noted, (area code)-prefix is (360)-650. 960 |

961 |

962 | If you notice an incorrect listing, changes can be requested by submitting the 963 | Employee 964 | Campus (OF) Address Update form. 965 |

966 | 967 | 968 |

969 | Use of this directory for commercial purposes is prohibited per Washington Code 42.56. 970 |

971 |

 [ Back to Top ]

972 | 973 |
974 |
975 |

Search by name

976 |
977 | 978 | 979 |
980 | 981 |

Browse by department

982 | 1358 |

Browse by last name

1359 | 1360 | 1361 | 1362 | 1363 | 1364 | 1365 | 1366 | 1367 | 1368 | 1369 | 1370 | 1371 | 1372 | 1373 | 1374 | 1375 | 1376 | 1377 | 1378 | 1379 | 1380 | 1381 | 1382 | 1383 | 1384 | 1385 | 1386 | 1387 | 1388 | 1389 | 1390 |
ABCDEFGHIJKLM
NOPQRSTUVWXYZ
1391 |
1392 |
1393 |
1394 |
1395 | 1407 |
1408 |
1409 | 1413 |
1414 | 1415 | 1416 | --------------------------------------------------------------------------------