1# ShellJS - Unix shell commands for Node.js 2 3[![Travis](https://img.shields.io/travis/shelljs/shelljs/master.svg?style=flat-square&label=unix)](https://travis-ci.org/shelljs/shelljs) 4[![AppVeyor](https://img.shields.io/appveyor/ci/shelljs/shelljs/master.svg?style=flat-square&label=windows)](https://ci.appveyor.com/project/shelljs/shelljs/branch/master) 5[![Codecov](https://img.shields.io/codecov/c/github/shelljs/shelljs/master.svg?style=flat-square&label=coverage)](https://codecov.io/gh/shelljs/shelljs) 6[![npm version](https://img.shields.io/npm/v/shelljs.svg?style=flat-square)](https://www.npmjs.com/package/shelljs) 7[![npm downloads](https://img.shields.io/npm/dm/shelljs.svg?style=flat-square)](https://www.npmjs.com/package/shelljs) 8 9ShellJS is a portable **(Windows/Linux/OS X)** implementation of Unix shell 10commands on top of the Node.js API. You can use it to eliminate your shell 11script's dependency on Unix while still keeping its familiar and powerful 12commands. You can also install it globally so you can run it from outside Node 13projects - say goodbye to those gnarly Bash scripts! 14 15ShellJS is proudly tested on every node release since `v4`! 16 17The project is [unit-tested](http://travis-ci.org/shelljs/shelljs) and battle-tested in projects like: 18 19+ [Firebug](http://getfirebug.com/) - Firefox's infamous debugger 20+ [JSHint](http://jshint.com) & [ESLint](http://eslint.org/) - popular JavaScript linters 21+ [Zepto](http://zeptojs.com) - jQuery-compatible JavaScript library for modern browsers 22+ [Yeoman](http://yeoman.io/) - Web application stack and development tool 23+ [Deployd.com](http://deployd.com) - Open source PaaS for quick API backend generation 24+ And [many more](https://npmjs.org/browse/depended/shelljs). 25 26If you have feedback, suggestions, or need help, feel free to post in our [issue 27tracker](https://github.com/shelljs/shelljs/issues). 28 29Think ShellJS is cool? Check out some related projects in our [Wiki 30page](https://github.com/shelljs/shelljs/wiki)! 31 32Upgrading from an older version? Check out our [breaking 33changes](https://github.com/shelljs/shelljs/wiki/Breaking-Changes) page to see 34what changes to watch out for while upgrading. 35 36## Command line use 37 38If you just want cross platform UNIX commands, checkout our new project 39[shelljs/shx](https://github.com/shelljs/shx), a utility to expose `shelljs` to 40the command line. 41 42For example: 43 44``` 45$ shx mkdir -p foo 46$ shx touch foo/bar.txt 47$ shx rm -rf foo 48``` 49 50## Plugin API 51 52ShellJS now supports third-party plugins! You can learn more about using plugins 53and writing your own ShellJS commands in [the 54wiki](https://github.com/shelljs/shelljs/wiki/Using-ShellJS-Plugins). 55 56## A quick note about the docs 57 58For documentation on all the latest features, check out our 59[README](https://github.com/shelljs/shelljs). To read docs that are consistent 60with the latest release, check out [the npm 61page](https://www.npmjs.com/package/shelljs) or 62[shelljs.org](http://documentup.com/shelljs/shelljs). 63 64## Installing 65 66Via npm: 67 68```bash 69$ npm install [-g] shelljs 70``` 71 72## Examples 73 74```javascript 75var shell = require('shelljs'); 76 77if (!shell.which('git')) { 78 shell.echo('Sorry, this script requires git'); 79 shell.exit(1); 80} 81 82// Copy files to release dir 83shell.rm('-rf', 'out/Release'); 84shell.cp('-R', 'stuff/', 'out/Release'); 85 86// Replace macros in each .js file 87shell.cd('lib'); 88shell.ls('*.js').forEach(function (file) { 89 shell.sed('-i', 'BUILD_VERSION', 'v0.1.2', file); 90 shell.sed('-i', /^.*REMOVE_THIS_LINE.*$/, '', file); 91 shell.sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, shell.cat('macro.js'), file); 92}); 93shell.cd('..'); 94 95// Run external tool synchronously 96if (shell.exec('git commit -am "Auto-commit"').code !== 0) { 97 shell.echo('Error: Git commit failed'); 98 shell.exit(1); 99} 100``` 101 102## Exclude options 103 104If you need to pass a parameter that looks like an option, you can do so like: 105 106```js 107shell.grep('--', '-v', 'path/to/file'); // Search for "-v", no grep options 108 109shell.cp('-R', '-dir', 'outdir'); // If already using an option, you're done 110``` 111 112## Global vs. Local 113 114We no longer recommend using a global-import for ShellJS (i.e. 115`require('shelljs/global')`). While still supported for convenience, this 116pollutes the global namespace, and should therefore only be used with caution. 117 118Instead, we recommend a local import (standard for npm packages): 119 120```javascript 121var shell = require('shelljs'); 122shell.echo('hello world'); 123``` 124 125<!-- DO NOT MODIFY BEYOND THIS POINT - IT'S AUTOMATICALLY GENERATED --> 126 127 128## Command reference 129 130 131All commands run synchronously, unless otherwise stated. 132All commands accept standard bash globbing characters (`*`, `?`, etc.), 133compatible with the [node `glob` module](https://github.com/isaacs/node-glob). 134 135For less-commonly used commands and features, please check out our [wiki 136page](https://github.com/shelljs/shelljs/wiki). 137 138 139### cat([options,] file [, file ...]) 140### cat([options,] file_array) 141 142Available options: 143 144+ `-n`: number all output lines 145 146Examples: 147 148```javascript 149var str = cat('file*.txt'); 150var str = cat('file1', 'file2'); 151var str = cat(['file1', 'file2']); // same as above 152``` 153 154Returns a string containing the given file, or a concatenated string 155containing the files if more than one file is given (a new line character is 156introduced between each file). 157 158 159### cd([dir]) 160 161Changes to directory `dir` for the duration of the script. Changes to home 162directory if no argument is supplied. 163 164 165### chmod([options,] octal_mode || octal_string, file) 166### chmod([options,] symbolic_mode, file) 167 168Available options: 169 170+ `-v`: output a diagnostic for every file processed 171+ `-c`: like verbose, but report only when a change is made 172+ `-R`: change files and directories recursively 173 174Examples: 175 176```javascript 177chmod(755, '/Users/brandon'); 178chmod('755', '/Users/brandon'); // same as above 179chmod('u+x', '/Users/brandon'); 180chmod('-R', 'a-w', '/Users/brandon'); 181``` 182 183Alters the permissions of a file or directory by either specifying the 184absolute permissions in octal form or expressing the changes in symbols. 185This command tries to mimic the POSIX behavior as much as possible. 186Notable exceptions: 187 188+ In symbolic modes, `a-r` and `-r` are identical. No consideration is 189 given to the `umask`. 190+ There is no "quiet" option, since default behavior is to run silent. 191 192 193### cp([options,] source [, source ...], dest) 194### cp([options,] source_array, dest) 195 196Available options: 197 198+ `-f`: force (default behavior) 199+ `-n`: no-clobber 200+ `-u`: only copy if `source` is newer than `dest` 201+ `-r`, `-R`: recursive 202+ `-L`: follow symlinks 203+ `-P`: don't follow symlinks 204 205Examples: 206 207```javascript 208cp('file1', 'dir1'); 209cp('-R', 'path/to/dir/', '~/newCopy/'); 210cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp'); 211cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above 212``` 213 214Copies files. 215 216 217### pushd([options,] [dir | '-N' | '+N']) 218 219Available options: 220 221+ `-n`: Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated. 222+ `-q`: Supresses output to the console. 223 224Arguments: 225 226+ `dir`: Sets the current working directory to the top of the stack, then executes the equivalent of `cd dir`. 227+ `+N`: Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. 228+ `-N`: Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. 229 230Examples: 231 232```javascript 233// process.cwd() === '/usr' 234pushd('/etc'); // Returns /etc /usr 235pushd('+1'); // Returns /usr /etc 236``` 237 238Save the current directory on the top of the directory stack and then `cd` to `dir`. With no arguments, `pushd` exchanges the top two directories. Returns an array of paths in the stack. 239 240 241### popd([options,] ['-N' | '+N']) 242 243Available options: 244 245+ `-n`: Suppress the normal directory change when removing directories from the stack, so that only the stack is manipulated. 246+ `-q`: Supresses output to the console. 247 248Arguments: 249 250+ `+N`: Removes the Nth directory (counting from the left of the list printed by dirs), starting with zero. 251+ `-N`: Removes the Nth directory (counting from the right of the list printed by dirs), starting with zero. 252 253Examples: 254 255```javascript 256echo(process.cwd()); // '/usr' 257pushd('/etc'); // '/etc /usr' 258echo(process.cwd()); // '/etc' 259popd(); // '/usr' 260echo(process.cwd()); // '/usr' 261``` 262 263When no arguments are given, `popd` removes the top directory from the stack and performs a `cd` to the new top directory. The elements are numbered from 0, starting at the first directory listed with dirs (i.e., `popd` is equivalent to `popd +0`). Returns an array of paths in the stack. 264 265 266### dirs([options | '+N' | '-N']) 267 268Available options: 269 270+ `-c`: Clears the directory stack by deleting all of the elements. 271+ `-q`: Supresses output to the console. 272 273Arguments: 274 275+ `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero. 276+ `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero. 277 278Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if `+N` or `-N` was specified. 279 280See also: `pushd`, `popd` 281 282 283### echo([options,] string [, string ...]) 284 285Available options: 286 287+ `-e`: interpret backslash escapes (default) 288+ `-n`: remove trailing newline from output 289 290Examples: 291 292```javascript 293echo('hello world'); 294var str = echo('hello world'); 295echo('-n', 'no newline at end'); 296``` 297 298Prints `string` to stdout, and returns string with additional utility methods 299like `.to()`. 300 301 302### exec(command [, options] [, callback]) 303 304Available options: 305 306+ `async`: Asynchronous execution. If a callback is provided, it will be set to 307 `true`, regardless of the passed value (default: `false`). 308+ `silent`: Do not echo program output to console (default: `false`). 309+ `encoding`: Character encoding to use. Affects the values returned to stdout and stderr, and 310 what is written to stdout and stderr when not in silent mode (default: `'utf8'`). 311+ and any option available to Node.js's 312 [`child_process.exec()`](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback) 313 314Examples: 315 316```javascript 317var version = exec('node --version', {silent:true}).stdout; 318 319var child = exec('some_long_running_process', {async:true}); 320child.stdout.on('data', function(data) { 321 /* ... do something with data ... */ 322}); 323 324exec('some_long_running_process', function(code, stdout, stderr) { 325 console.log('Exit code:', code); 326 console.log('Program output:', stdout); 327 console.log('Program stderr:', stderr); 328}); 329``` 330 331Executes the given `command` _synchronously_, unless otherwise specified. When in synchronous 332mode, this returns a `ShellString` (compatible with ShellJS v0.6.x, which returns an object 333of the form `{ code:..., stdout:... , stderr:... }`). Otherwise, this returns the child process 334object, and the `callback` receives the arguments `(code, stdout, stderr)`. 335 336Not seeing the behavior you want? `exec()` runs everything through `sh` 337by default (or `cmd.exe` on Windows), which differs from `bash`. If you 338need bash-specific behavior, try out the `{shell: 'path/to/bash'}` option. 339 340 341### find(path [, path ...]) 342### find(path_array) 343 344Examples: 345 346```javascript 347find('src', 'lib'); 348find(['src', 'lib']); // same as above 349find('.').filter(function(file) { return file.match(/\.js$/); }); 350``` 351 352Returns array of all files (however deep) in the given paths. 353 354The main difference from `ls('-R', path)` is that the resulting file names 355include the base directories (e.g., `lib/resources/file1` instead of just `file1`). 356 357 358### grep([options,] regex_filter, file [, file ...]) 359### grep([options,] regex_filter, file_array) 360 361Available options: 362 363+ `-v`: Invert `regex_filter` (only print non-matching lines). 364+ `-l`: Print only filenames of matching files. 365+ `-i`: Ignore case. 366 367Examples: 368 369```javascript 370grep('-v', 'GLOBAL_VARIABLE', '*.js'); 371grep('GLOBAL_VARIABLE', '*.js'); 372``` 373 374Reads input string from given files and returns a string containing all lines of the 375file that match the given `regex_filter`. 376 377 378### head([{'-n': \<num\>},] file [, file ...]) 379### head([{'-n': \<num\>},] file_array) 380 381Available options: 382 383+ `-n <num>`: Show the first `<num>` lines of the files 384 385Examples: 386 387```javascript 388var str = head({'-n': 1}, 'file*.txt'); 389var str = head('file1', 'file2'); 390var str = head(['file1', 'file2']); // same as above 391``` 392 393Read the start of a file. 394 395 396### ln([options,] source, dest) 397 398Available options: 399 400+ `-s`: symlink 401+ `-f`: force 402 403Examples: 404 405```javascript 406ln('file', 'newlink'); 407ln('-sf', 'file', 'existing'); 408``` 409 410Links `source` to `dest`. Use `-f` to force the link, should `dest` already exist. 411 412 413### ls([options,] [path, ...]) 414### ls([options,] path_array) 415 416Available options: 417 418+ `-R`: recursive 419+ `-A`: all files (include files beginning with `.`, except for `.` and `..`) 420+ `-L`: follow symlinks 421+ `-d`: list directories themselves, not their contents 422+ `-l`: list objects representing each file, each with fields containing `ls 423 -l` output fields. See 424 [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) 425 for more info 426 427Examples: 428 429```javascript 430ls('projs/*.js'); 431ls('-R', '/users/me', '/tmp'); 432ls('-R', ['/users/me', '/tmp']); // same as above 433ls('-l', 'file.txt'); // { name: 'file.txt', mode: 33188, nlink: 1, ...} 434``` 435 436Returns array of files in the given `path`, or files in 437the current directory if no `path` is provided. 438 439 440### mkdir([options,] dir [, dir ...]) 441### mkdir([options,] dir_array) 442 443Available options: 444 445+ `-p`: full path (and create intermediate directories, if necessary) 446 447Examples: 448 449```javascript 450mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g'); 451mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above 452``` 453 454Creates directories. 455 456 457### mv([options ,] source [, source ...], dest') 458### mv([options ,] source_array, dest') 459 460Available options: 461 462+ `-f`: force (default behavior) 463+ `-n`: no-clobber 464 465Examples: 466 467```javascript 468mv('-n', 'file', 'dir/'); 469mv('file1', 'file2', 'dir/'); 470mv(['file1', 'file2'], 'dir/'); // same as above 471``` 472 473Moves `source` file(s) to `dest`. 474 475 476### pwd() 477 478Returns the current directory. 479 480 481### rm([options,] file [, file ...]) 482### rm([options,] file_array) 483 484Available options: 485 486+ `-f`: force 487+ `-r, -R`: recursive 488 489Examples: 490 491```javascript 492rm('-rf', '/tmp/*'); 493rm('some_file.txt', 'another_file.txt'); 494rm(['some_file.txt', 'another_file.txt']); // same as above 495``` 496 497Removes files. 498 499 500### sed([options,] search_regex, replacement, file [, file ...]) 501### sed([options,] search_regex, replacement, file_array) 502 503Available options: 504 505+ `-i`: Replace contents of `file` in-place. _Note that no backups will be created!_ 506 507Examples: 508 509```javascript 510sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js'); 511sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js'); 512``` 513 514Reads an input string from `file`s, and performs a JavaScript `replace()` on the input 515using the given `search_regex` and `replacement` string or function. Returns the new string after replacement. 516 517Note: 518 519Like unix `sed`, ShellJS `sed` supports capture groups. Capture groups are specified 520using the `$n` syntax: 521 522```javascript 523sed(/(\w+)\s(\w+)/, '$2, $1', 'file.txt'); 524``` 525 526 527### set(options) 528 529Available options: 530 531+ `+/-e`: exit upon error (`config.fatal`) 532+ `+/-v`: verbose: show all commands (`config.verbose`) 533+ `+/-f`: disable filename expansion (globbing) 534 535Examples: 536 537```javascript 538set('-e'); // exit upon first error 539set('+e'); // this undoes a "set('-e')" 540``` 541 542Sets global configuration variables. 543 544 545### sort([options,] file [, file ...]) 546### sort([options,] file_array) 547 548Available options: 549 550+ `-r`: Reverse the results 551+ `-n`: Compare according to numerical value 552 553Examples: 554 555```javascript 556sort('foo.txt', 'bar.txt'); 557sort('-r', 'foo.txt'); 558``` 559 560Return the contents of the `file`s, sorted line-by-line. Sorting multiple 561files mixes their content (just as unix `sort` does). 562 563 564### tail([{'-n': \<num\>},] file [, file ...]) 565### tail([{'-n': \<num\>},] file_array) 566 567Available options: 568 569+ `-n <num>`: Show the last `<num>` lines of `file`s 570 571Examples: 572 573```javascript 574var str = tail({'-n': 1}, 'file*.txt'); 575var str = tail('file1', 'file2'); 576var str = tail(['file1', 'file2']); // same as above 577``` 578 579Read the end of a `file`. 580 581 582### tempdir() 583 584Examples: 585 586```javascript 587var tmp = tempdir(); // "/tmp" for most *nix platforms 588``` 589 590Searches and returns string containing a writeable, platform-dependent temporary directory. 591Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir). 592 593 594### test(expression) 595 596Available expression primaries: 597 598+ `'-b', 'path'`: true if path is a block device 599+ `'-c', 'path'`: true if path is a character device 600+ `'-d', 'path'`: true if path is a directory 601+ `'-e', 'path'`: true if path exists 602+ `'-f', 'path'`: true if path is a regular file 603+ `'-L', 'path'`: true if path is a symbolic link 604+ `'-p', 'path'`: true if path is a pipe (FIFO) 605+ `'-S', 'path'`: true if path is a socket 606 607Examples: 608 609```javascript 610if (test('-d', path)) { /* do something with dir */ }; 611if (!test('-f', path)) continue; // skip if it's a regular file 612``` 613 614Evaluates `expression` using the available primaries and returns corresponding value. 615 616 617### ShellString.prototype.to(file) 618 619Examples: 620 621```javascript 622cat('input.txt').to('output.txt'); 623``` 624 625Analogous to the redirection operator `>` in Unix, but works with 626`ShellStrings` (such as those returned by `cat`, `grep`, etc.). _Like Unix 627redirections, `to()` will overwrite any existing file!_ 628 629 630### ShellString.prototype.toEnd(file) 631 632Examples: 633 634```javascript 635cat('input.txt').toEnd('output.txt'); 636``` 637 638Analogous to the redirect-and-append operator `>>` in Unix, but works with 639`ShellStrings` (such as those returned by `cat`, `grep`, etc.). 640 641 642### touch([options,] file [, file ...]) 643### touch([options,] file_array) 644 645Available options: 646 647+ `-a`: Change only the access time 648+ `-c`: Do not create any files 649+ `-m`: Change only the modification time 650+ `-d DATE`: Parse `DATE` and use it instead of current time 651+ `-r FILE`: Use `FILE`'s times instead of current time 652 653Examples: 654 655```javascript 656touch('source.js'); 657touch('-c', '/path/to/some/dir/source.js'); 658touch({ '-r': FILE }, '/path/to/some/dir/source.js'); 659``` 660 661Update the access and modification times of each `FILE` to the current time. 662A `FILE` argument that does not exist is created empty, unless `-c` is supplied. 663This is a partial implementation of [`touch(1)`](http://linux.die.net/man/1/touch). 664 665 666### uniq([options,] [input, [output]]) 667 668Available options: 669 670+ `-i`: Ignore case while comparing 671+ `-c`: Prefix lines by the number of occurrences 672+ `-d`: Only print duplicate lines, one for each group of identical lines 673 674Examples: 675 676```javascript 677uniq('foo.txt'); 678uniq('-i', 'foo.txt'); 679uniq('-cd', 'foo.txt', 'bar.txt'); 680``` 681 682Filter adjacent matching lines from `input`. 683 684 685### which(command) 686 687Examples: 688 689```javascript 690var nodeExec = which('node'); 691``` 692 693Searches for `command` in the system's `PATH`. On Windows, this uses the 694`PATHEXT` variable to append the extension if it's not already executable. 695Returns string containing the absolute path to `command`. 696 697 698### exit(code) 699 700Exits the current process with the given exit `code`. 701 702### error() 703 704Tests if error occurred in the last command. Returns a truthy value if an 705error returned, or a falsy value otherwise. 706 707**Note**: do not rely on the 708return value to be an error message. If you need the last error message, use 709the `.stderr` attribute from the last command's return value instead. 710 711 712### ShellString(str) 713 714Examples: 715 716```javascript 717var foo = ShellString('hello world'); 718``` 719 720Turns a regular string into a string-like object similar to what each 721command returns. This has special methods, like `.to()` and `.toEnd()`. 722 723 724### env['VAR_NAME'] 725 726Object containing environment variables (both getter and setter). Shortcut 727to `process.env`. 728 729### Pipes 730 731Examples: 732 733```javascript 734grep('foo', 'file1.txt', 'file2.txt').sed(/o/g, 'a').to('output.txt'); 735echo('files with o\'s in the name:\n' + ls().grep('o')); 736cat('test.js').exec('node'); // pipe to exec() call 737``` 738 739Commands can send their output to another command in a pipe-like fashion. 740`sed`, `grep`, `cat`, `exec`, `to`, and `toEnd` can appear on the right-hand 741side of a pipe. Pipes can be chained. 742 743## Configuration 744 745 746### config.silent 747 748Example: 749 750```javascript 751var sh = require('shelljs'); 752var silentState = sh.config.silent; // save old silent state 753sh.config.silent = true; 754/* ... */ 755sh.config.silent = silentState; // restore old silent state 756``` 757 758Suppresses all command output if `true`, except for `echo()` calls. 759Default is `false`. 760 761### config.fatal 762 763Example: 764 765```javascript 766require('shelljs/global'); 767config.fatal = true; // or set('-e'); 768cp('this_file_does_not_exist', '/dev/null'); // throws Error here 769/* more commands... */ 770``` 771 772If `true`, the script will throw a Javascript error when any shell.js 773command encounters an error. Default is `false`. This is analogous to 774Bash's `set -e`. 775 776### config.verbose 777 778Example: 779 780```javascript 781config.verbose = true; // or set('-v'); 782cd('dir/'); 783rm('-rf', 'foo.txt', 'bar.txt'); 784exec('echo hello'); 785``` 786 787Will print each command as follows: 788 789``` 790cd dir/ 791rm -rf foo.txt bar.txt 792exec echo hello 793``` 794 795### config.globOptions 796 797Example: 798 799```javascript 800config.globOptions = {nodir: true}; 801``` 802 803Use this value for calls to `glob.sync()` instead of the default options. 804 805### config.reset() 806 807Example: 808 809```javascript 810var shell = require('shelljs'); 811// Make changes to shell.config, and do stuff... 812/* ... */ 813shell.config.reset(); // reset to original state 814// Do more stuff, but with original settings 815/* ... */ 816``` 817 818Reset `shell.config` to the defaults: 819 820```javascript 821{ 822 fatal: false, 823 globOptions: {}, 824 maxdepth: 255, 825 noglob: false, 826 silent: false, 827 verbose: false, 828} 829``` 830 831## Team 832 833| [![Nate Fischer](https://avatars.githubusercontent.com/u/5801521?s=130)](https://github.com/nfischer) | [![Brandon Freitag](https://avatars1.githubusercontent.com/u/5988055?v=3&s=130)](http://github.com/freitagbr) | 834|:---:|:---:| 835| [Nate Fischer](https://github.com/nfischer) | [Brandon Freitag](http://github.com/freitagbr) | 836