From charlesreid1

Bash Quick Reference: Bash/Quick

Bash Quick Prototype: Bash/Prototype

Bash Guide

Using Bash

Setting the default shell: http://www.unix.com/shell-programming-scripting/32664-how-change-default-shell-linux.html

You can set the default shell in Mac's Terminal by going to Terminal > Preferences and specifying the login shell.

Startup Process

Dot Files

Bash Scripting

Looping

Looping in bash is really simple, and convenient - you can use other unix commands (most obviously "ls") to create lists, and then loop over each element of those lists. For example, if I want to print out the name of every file in my home directory (granted, not very useful, but this is just an example), I can do this:


for i in `/bin/ls -1 $HOME`; do
    echo $i
done

To do some serious bash looping kung-fu, check out Xargs.

Bash Loop One-Liner: Seq and Destroy

Sometimes, you want to do a bash loop, but you want to do it on the fly, so you don't have to fire up your text editor and make a bash script.

Seq to the rescue!

The seq command is a way of creating very simple numerical sequences:

$ seq 1 10
1
2
3
4
5
6
7
8
9
10

But seq has some other features that make it very handy (see the Seq page for a full list of examples).

Seq can be very easily combined with bash to create one-line for loops.

Let's pose a simple scenario: say you've imported a bunch of photos from your camera, and their naming convention looks like this:got a lit of photos like this:

IMG_0000.jpg
IMG_0001.jpg
IMG_0002.jpg
IMG_0003.jpg
IMG_0004.jpg
IMG_0005.jpg
IMG_0006.jpg
IMG_0007.jpg
IMG_0008.jpg
IMG_0009.jpg
IMG_0010.jpg
IMG_0011.jpg
IMG_0012.jpg
IMG_0013.jpg
IMG_0014.jpg
IMG_0015.jpg
IMG_0016.jpg
IMG_0017.jpg
IMG_0018.jpg
IMG_0019.jpg
IMG_0020.jpg
IMG_0021.jpg
IMG_0022.jpg
IMG_0023.jpg
IMG_0024.jpg
IMG_0025.jpg
IMG_0026.jpg
IMG_0027.jpg
IMG_0028.jpg
IMG_0029.jpg
IMG_0030.jpg
IMG_0031.jpg
IMG_0032.jpg
IMG_0033.jpg
IMG_0034.jpg
IMG_0035.jpg
IMG_0036.jpg
IMG_0037.jpg
IMG_0038.jpg
IMG_0039.jpg
IMG_0040.jpg
IMG_0041.jpg
IMG_0042.jpg
IMG_0043.jpg
IMG_0044.jpg
IMG_0045.jpg
IMG_0046.jpg
IMG_0047.jpg
IMG_0048.jpg
IMG_0049.jpg
IMG_0050.jpg

Now, let's say we want to move some photos to another folder, but we only want to move the odd-numbered photographs starting at 10 and ending at 40.

We can whip up a quick seq command that'll give us even numbers from 10 to 40:

$ seq 10 2 40

But we need the whole thing to be formatted into a comma-separated list, so we can feed it to a bash command, like mv {this,that} the_other/.. So let's use the -s flag:

$ seq -s, 10 2 40
10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,

Now we can use bash's backticks to plop that command in the middle of another mv command:

$ mv directory1/IMG_00{`seq -s, 10 2 40`}.jpg directory2/.

which unfolds to:

mv directory1/IMG_0010.jpg directory2/.
mv directory1/IMG_0012.jpg directory2/.
mv directory1/IMG_0014.jpg directory2/.
mv directory1/IMG_0016.jpg directory2/.
mv directory1/IMG_0018.jpg directory2/.
mv directory1/IMG_0020.jpg directory2/.
mv directory1/IMG_0022.jpg directory2/.
mv directory1/IMG_0024.jpg directory2/.
mv directory1/IMG_0026.jpg directory2/.
mv directory1/IMG_0028.jpg directory2/.
mv directory1/IMG_0030.jpg directory2/.
mv directory1/IMG_0032.jpg directory2/.
mv directory1/IMG_0034.jpg directory2/.
mv directory1/IMG_0036.jpg directory2/.
mv directory1/IMG_0038.jpg directory2/.
mv directory1/IMG_0040.jpg directory2/.

Voila!

But what if the range of file numbers strides 100? If you wanted to go from 80 to 120, you would end up with mismatching zero-padding. To get around this, you could either force seq to output all numbers with a fixed width (using the -w flag), or you could get more fancy by specifying a printf (print format) string for seq to use when printing each number (via the -f flag in seq).

Let's repeat the example, but this time using the printf approach. First, we want to print a 4-digit number that is zero-padded, meaning our printf string is going to be %04g. Now the seq command looks like:

$ seq -s, -f'%04g' 80 2 120
0080,0082,0084,0086,0088,0090,0092,0094,0096,0098,0100,0102,0104,0106,0108,0110,0112,0114,0116,0118,0120,

(Note we could have also used the printf string 'IMG_%04g.jpg' to make things a little more compact.)

Now we can feed that directly to the mv command:

$ mv directory1/IMG_{`seq -s, -f'%04g' 80 2 120`}.jpg directory2/.

which unfolds into the set of bash commands:

mv directory1/IMG_0080.jpg directory2/.
mv directory1/IMG_0082.jpg directory2/.
mv directory1/IMG_0084.jpg directory2/.
mv directory1/IMG_0086.jpg directory2/.
mv directory1/IMG_0088.jpg directory2/.
mv directory1/IMG_0090.jpg directory2/.
mv directory1/IMG_0092.jpg directory2/.
mv directory1/IMG_0094.jpg directory2/.
mv directory1/IMG_0096.jpg directory2/.
mv directory1/IMG_0098.jpg directory2/.
mv directory1/IMG_0100.jpg directory2/.
mv directory1/IMG_0102.jpg directory2/.
mv directory1/IMG_0104.jpg directory2/.
mv directory1/IMG_0106.jpg directory2/.
mv directory1/IMG_0108.jpg directory2/.
mv directory1/IMG_0110.jpg directory2/.
mv directory1/IMG_0112.jpg directory2/.
mv directory1/IMG_0114.jpg directory2/.
mv directory1/IMG_0116.jpg directory2/.
mv directory1/IMG_0118.jpg directory2/.
mv directory1/IMG_0120.jpg directory2/.

Voila!

You can find more information about seq, and how to couple it with other Unix utilities, at the Seq page on my wiki.

Logical Operators

NOTE: the use of double brackets for logical condition checks is highly recommended.

There are several logical operators available for checking conditions in bash.

These can be tested using a simple "if" statement. In bash, if statements are of the form:

if [[ condition ]]; then
    cmd
else
    cmd2
fi

The spaces between the brackets and the condition are essential.

A comprehensive list of logical condition checks is here: http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html

Integer Operators

Operator Meaning Example
-eq returns true if arguments are equal
$ a=1; b=1; if [[ "$a" -eq "$b" ]]; then echo "true"; else echo "false"; fi
true

$ a=1; b=2; if [[ "$a" -eq "$b" ]]; then echo "true"; else echo "false"; fi
false
-ne returns true if arguments are not equal
$ a=1; b=1; if [[ "$a" -ne "$b" ]]; then echo "true"; else echo "false"; fi
false

$ a=1; b=2; if [[ "$a" -ne "$b" ]]; then echo "true"; else echo "false"; fi
true
-gt returns true if argument 1 is greater than argument 2
$ a=5; b=10; if [[ "$a" -gt "$b" ]]; then echo "true"; else echo "false"; fi
false
-ge returns true if argument 1 is greater than or equal to argument 2
$ a=15; b=10; if [[ "$a" -ge "$b" ]]; then echo "true"; else echo "false"; fi
true

$ a=10; b=10; if [[ "$a" -ge "$b" ]]; then echo "true"; else echo "false"; fi
true
-lt returns true if argument 1 less than argument 2
$ a=5; b=10; if [[ "$a" -lt "$b" ]]; then echo "true"; else echo "false"; fi
true
-le returns true if argument 1 less than or equal to argument 2
$ a=15; b=10; if [[ "$a" -le "$b" ]]; then echo "true"; else echo "false"; fi
false

$ a=10; b=10; if [[ "$a" -le "$b" ]]; then echo "true"; else echo "false"; fi
true
\< returns true if argument 1 less than argument 2

this needs to be escaped with a \ if it occurs inside single brackets; otherwise it can appear between double bracket or between double parentheses

$ a=10; b=10; if [ "$a" \< "$b" ]; then echo "true"; else echo "false"; fi
false

$ a=10; b=10; if [[ "$a" < "$b" ]]; then echo "true"; else echo "false"; fi
false

$ a=10; b=10; if (( "$a" < "$b" )); then echo "true"; else echo "false"; fi
false
<= returns true if argument 1 less than or equal to argument 2

this operator needs to go inside double parentheses

$ a=10; b=10; if (( "$a" <= "$b" )); then echo "true"; else echo "false"; fi
true
> returns true if argument 1 greater than argument 2

this needs to be escaped with a \ if it occurs inside single brackets; otherwise it can appear between double bracket or between double parentheses

$ z=10; zz=1000; 

$ if [ zz \> z ]; then echo "true"; else echo "false"; fi
true

$ if [[ zz > z ]]; then echo "true"; else echo "false"; fi
true
>= returns true if argument 1 greater than or equal to argument 2

this operator needs to go inside double parentheses

$ a=50; b=10; if (( $a >= $b )); then echo "true"; else echo "false"; fi
true

String Operators

Operator Meaning Example
= returns true if two strings are equal
$ a=foo; b=foo; if [[ "$a" = "$b" ]]; then echo "true"; else echo "false"; fi
true

$ a=foo; b=bar; if [[ "$a" = "$b" ]]; then echo "true"; else echo "false"; fi
false
== returns true if two strings are equal; equivalent to =
$ a=foo; b=foo; if [[ "$a" == "$b" ]]; then echo "true"; else echo "false"; fi
true

$ a=foo; b=bar; if [[ "$a" == "$b" ]]; then echo "true"; else echo "false"; fi
false

Note the difference between quoting a string and not quoting a string:

[[ $a ==  b*  ]] # returns true if $a starts with b (pattern matching)
[[ $a == "b*" ]] # returns true if $a is literally equal to b* (literal matching)
!= returns true if strings are not equal
$ a=foo; b=bar; if [[ "$a" != "$b" ]]; then echo "true"; else echo "false"; fi
true
< returns true if argument 1 is less than (alphabetically) argument 2; capitals come before non-capitals
$ a=foo; b=bar; if [[ "$a" < "$b" ]]; then echo "true"; else echo "false"; fi
false

$ a=Foo; b=bar; if [[ "$a" < "$b" ]]; then echo "true"; else echo "false"; fi
true

$ a=bar; b=foo; if [[ "$a" < "$b" ]]; then echo "true"; else echo "false"; fi
true
> returns true if argument 1 is greater than (alphabetically) argument 2; capitals come before non-capitals
$ a=foo; b=bar; if [[ "$a" > "$b" ]]; then echo "true"; else echo "false"; fi
true

$ a=Foo; b=bar; if [[ "$a" > "$b" ]]; then echo "true"; else echo "false"; fi
false
-z returns true if the string is null
$ a=""; b="foobar"; if [ -z "$a" ]; then echo "true"; else echo "false"; fi
true

$ a=""; b="foobar"; if [ -z "$b" ]; then echo "true"; else echo "false"; fi
false
-n returns true if the string is NOT null
$ a=""; b="foobar"; if [ -n "$a" ]; then echo "true"; else echo "false"; fi
false

$ a=""; b="foobar"; if [ -n "$b" ]; then echo "true"; else echo "false"; fi
true


Compound Operators

You can combine logical operators using and/or operators:

Operator Meaning Example
-a Logical and (both conditions must be true for condition to be true)
if [ "$expr1" -a "$expr2" ]
then
  echo "Both expr1 and expr2 are true."
else
  echo "Either expr1 or expr2 is false."
fi
-o Logical or (either condition must be true for condition to be true)
if [ "$expr1" -o "$expr2" ]
then
  echo "Either expr1 or expr2 is true."
else
  echo "Both expr1 and expr2 are false."
fi

Basic Math

Complex Math

Math Operators

Bash Scripts for Heavy Bash Users

See https://github.com/alexanderepstein/Bash-Snippets

  • weather
  • geoip info
  • currency
  • encryption/decryption
  • movies
  • cheat - finding command options and code pieces
  • taste - "find similar things to X"

Bash Keyboard Shortcuts

Editing Shortcuts

The following are keyboard shortcuts for easy navigation/editing of commands that are on the Bash command line.

Movement

  • C-a - Move to beginning of line
  • C-e - Move to end of line


  • M-f - Move forward 1 word
  • M-b - Move backward 1 word


  • C-f - Move forward 1 character
  • C-b - Move backward 1 character

Cutting and Pasting

  • C-k - kill (cut) from cursor to end of line
  • C-w - kill (cut) from cursor to previous whitespace
  • C-y - yank (paste) previously killed text at cursor

Uncategorized

  • C-L - clear screen, reprint current line at top
  • C-u - undo last edit
  • C-d - delete character under cursor

References

Floating point math in the shell:

http://www.novell.com/coolsolutions/tools/17043.html

Fast bash math:

http://www.bytemycode.com/snippets/snippet/350/

Bash script iterate through array of values

http://www.tech-recipes.com/rx/636/bash-shell-script-iterate-through-array-values/


Flags