Skip to content

Instantly share code, notes, and snippets.

@ndelage
Created December 4, 2013 15:34
Show Gist options
  • Save ndelage/7789608 to your computer and use it in GitHub Desktop.
Save ndelage/7789608 to your computer and use it in GitHub Desktop.
Command Line Fu

Unix Command Line Fu

Basics

tab completion

Nearly all shells have support for tab completion. At a minimum for completing file & directory names, though some even tab-complete based on which commands you're running. Some examples:

$ ls [TAB]
Gemfile        app/           connectors.rb  public/
Gemfile.lock   bin/           db/            spec/
README.rdoc    config/        lib/           tmp/
Rakefile       config.ru      log/           vendor/

Modern shells (like zsh) will tab complete with more appropriate choices. For example if you're trying to cd instead of ls, only show directories:

$ cd [TAB]
app/     config/  lib/     public/  tmp/
bin/     db/      log/     spec/    vendor/

So don't manually type out file names or long paths. There's a better way. Another plug for zsh: fuzzy matching!

$ vi db/migrate/speaker[TAB]
$ vi db/migrate/20131117003047_create_speakers.rb

With the right shell setup tab completion even works great for non-shell commands (like git):

$ git checkout [TAB]
FETCH_HEAD                                origin/inquiry-seeds-data
HEAD                                      origin/jma_program
ORIG_HEAD                                 origin/last-question-next
add-admire-people-text                    origin/layout-template

Scripting & Variables

If you didn't know it yet, your shell supports its own language! You can write scripts in Bash/sh (GNU Bourne-Again Shell/Bourne Shell). There are books on this of course. Shell scripting remains popular because of its ubiquity. You can expect nearly every *nix system to support sh scripts.

Here's a sample script to whet your appetite:

for k in `git branch | sed s/^..//`;
do
    echo `git log -1 --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k --`\\t"$k";
done | sort

Any idea what the above script does? Something with git branches, right? Well it actually prints your local branches in order of most recent commit! HOW NEAT IS THAT?? Here's some sample output:

2013-10-16 21:19:37 -0500 7 weeks ago	jma_program
2013-11-11 17:17:51 -0600 3 weeks ago	layout-template
2013-11-12 09:59:37 -0600 3 weeks ago	current-program-fixes
2013-11-15 21:45:06 -0600 3 weeks ago	program-fixes
2013-11-16 16:12:09 -0600 3 weeks ago	workzone_fix
2013-11-23 10:55:00 -0600 11 days ago	master

If you review the above script carefully, you'll notice it uses 3! different programs, git, sed and sort (echo is a built in shell command, so it doesn't count). You'll find that shell scripts tend to be built this way, glueing together a number of separate programs to make something more useful.

You might have noticed the for in loop in the sample script. See that k variable being re-used inside the loop as $k? Thats an example of using a variable in the shell. You can set your own right on the command line:

$ foo=bar
$ echo $foo
bar

If you want your variable to be available to any other programs you run within your shell (a subprocess) export the variable:

$ export foo
$ export another="this is a longer value"

You're probably familiar with exporting variables for things like API keys that you don't want to store in your source code.

In fact, if you haven't realized yet, when you open a new terminal...YOU'RE IN A REPL! (read–eval–print loop -- remember, irb that's also a REPL)

Pipes

Redirection & pipes are the connectors of shell scripting. Since we tend to rely separate programs we need a way to connect them up and pass data from one to the other.

The easiest way to do that is with the | (pipe) character. The | chains two commands together by connecting the STDOUT of the first to the STDIN of the second. For example:

ls -l | sed -e "s/[aeio]/u/g"

In the above the output of ls -l is fed into the sed program. sed is a "stream editor", allowing you to modify a stream of characters as it passes through. In the above example, we're replacing aeio with the character u. Here's some sample output with and without piping to sed:

$ ls -l
total 3216
-rw-r--r--   1 nate  staff     1279 Sep 16 20:26 Gemfile
-rw-r--r--   1 nate  staff     4183 Sep  4 21:34 Gemfile.lock
-rw-r--r--   1 nate  staff     2031 Aug  9 14:31 README.md
-rw-r--r--   1 nate  staff      258 Aug  5 10:16 Rakefile
drwxr-xr-x   9 nate  staff      306 Sep  4 19:09 app
drwxr-xr-x   5 nate  staff      170 Sep 10 14:26 bin
-rw-r--r--   1 nate  staff      134 Dec  4 08:43 branches.sh

$ ls -l | sed -e "s/[aeio]/u/g
-rw-r--r--   1 nutu  stuff     1279 Sup 16 20:26 Gumfulu
-rw-r--r--   1 nutu  stuff     4183 Sup  4 21:34 Gumfulu.luck
-rw-r--r--   1 nutu  stuff     2031 Aug  9 14:31 README.md
-rw-r--r--   1 nutu  stuff      258 Aug  5 10:16 Rukufulu
drwxr-xr-x   9 nutu  stuff      306 Sup  4 19:09 upp
drwxr-xr-x   5 nutu  stuff      170 Sup 10 14:26 bun
-rw-r--r--   1 nutu  stuff      134 Duc  4 08:43 brunchus.sh

Redirection

Redirection can sometimes serve as a stdin for a pipe, but most often it's used to redirect output to a file, instead of another program.

$ ls /Users/*ael > users_probably_called_michael.txt

Instead of printing to stdout in the above example, we're redirecting the output to a file.

Redirection with a single > overwrites any existing destination, a double >> appends to the file:

$ echo "Decorative Doggie Doors" >> ~/ideas.txt

Remember stdin, stderr and stdout from the *nix challenge? You redirect each one as you wish.

# redirect stdout to file
ls /Users/*ael 1> list.txt

# redirect stderr to file
ls /Users/*ael 2> errors.txt

# redirect both stdout and stderr
ls /Users/*ael &> errors.txt

If you don't specify a number or &, the default is to redirect stdout. You can even redirect from one output to another!

# redirect stderr to stdout
ls 2>&1 /Users/*ael

You can also redirect to the stdin of a process. Here we redirect a subshell's output to the wc command:

$ wc <(curl http://www.google.com 2> /dev/null)
16     304   10866 /dev/fd/11

Now we know how many words are in google.com's source: 304.

Chaining Programs

You can run multiple programs on a single line by separating them with a colon character:

$ echo "Hi"; ls /Users
Hi
Shared nate

You can conditionally run the next program if the previous one doesn't return an error status with &&

$ ls /Users/nonexistent && echo "You won a million dollars"
ls: /Users/nonexistent: No such file or directory

|| is also available, which runs the second command only if the first fails:

$ ls /Users/new_guy || mkdir /Users/new_guy

Demotime

  • diff <(curl http://www.google.com | tidy) <(curl http://www.google.fr |tidy)

  • git log -p

  • git diff branch..master

  • cd -

  • cd ~/Documents

  • git log --pretty=format:"%h %s" --graph

  • tail -f

  • grep

  • awk

  • git branch -v

  • -h --help -v -vv --version

  • tree -L 2

  • xargs

  • wc -l

  • ls **/jpg > jpg.txt

  • vi **/database.yml

  • !ls or !vi

  • man ls

  • grep -i -V -C -r

  • Symbolic links

  • Esc .

  • ps aux

  • hover in chrome JS debugger

  • JSON extension for Chrome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment