I thought I'd share my favorite regex of all time:

[ -~]

Any ideas what this regexp matches?

It matches all ASCII characters from the space to the tilde. What are those characters? They're all printable characters!

Take a look at the ASCII table. The printable characters start at the space and end at the tilde:


[ -~] matches all printable ascii characters (light blue background)

I love this.

Update:

Would you like to wear this regular expression? I just had an idea to make it a t-shirt as this blog post is getting so popular. Check out this t-shirt that I made:

This shirt is available through Teespring. Teespring is like Kickstarter for t-shirts. If 50 people buy the shirt, everyone gets their shirt. Otherwise no one gets charged and no one gets the shirt.

This month I'm presenting the limited-edition node.js duplex stream tees designed by the stream master and node.js wizard SubStack.

This shirt is done in collaboration with Teespring. Teespring is basically Kickstarter for t-shirts. If enough people commit to buying a shirt, everyone gets their shirt. Otherwise nobody gets a shirt (and nobody gets charged).

This tee illustrates the duplex stream pattern a.pipe(b).pipe(a) that sends the messages around like a telephone.

The shirt is available on Hanes tees and is priced at $15.

Get Your Limited Edition Duplex Stream T-Shirt Now!

Enjoy!

Do you love this shirt and want to get more geek shirts in the future? Subscribe to my blog and follow me on twitter for updates!

I just remembered that I had forgotten the ASCII trick to convert lowercase to uppercase and back. The trick is super simple, you xor the 6th bit (or 5th depending on how you count bits) and that changes the case!

Check this out:

a = 01100001
A = 01000001

See, just the 6th bit changed.

Why is it this way? Simply because the people who invented ASCII thought it was a great idea. If you look at characters a..z, you'll see that all of them have the 6th bit set to 1. The ASCII inventors though, hey, let's set 6th bit to 0 for upper case letters A..Z, then it will be super easy to change case. So they did.

a = 01100001    A = 01000001 
b = 01100010    B = 01000010 
c = 01100011    C = 01000011 
d = 01100100    D = 01000100 
e = 01100101    E = 01000101 
f = 01100110    F = 01000110 
g = 01100111    G = 01000111 
h = 01101000    H = 01001000 
i = 01101001    I = 01001001 
j = 01101010    J = 01001010 
k = 01101011    K = 01001011 
l = 01101100    L = 01001100 
m = 01101101    M = 01001101 
n = 01101110    N = 01001110 
o = 01101111    O = 01001111 
p = 01110000    P = 01010000 
q = 01110001    Q = 01010001 
r = 01110010    R = 01010010 
s = 01110011    S = 01010011 
t = 01110100    T = 01010100 
u = 01110101    U = 01010101 
v = 01110110    V = 01010110 
w = 01110111    W = 01010111 
x = 01111000    X = 01011000 
y = 01111001    Y = 01011001 
z = 01111010    Z = 01011010 

Also check this out, if you xor a character with a space, you invert the case:

$ perl -le 'print "a"^" "'   
A
$ perl -le 'print "A"^" "' 
a

Why is that? Because a space ' ' has the value 32, which is 1<<5, which is the 6th bit, which swaps the case!

Bonus

Here are the commands I used for this blog post:

$ perl -e 'printf "%08b\n", ord("K")'
$ perl -e 'printf "%s = %08b    %s = %08b\n", $_, ord, uc, ord uc for a..z' 

Simple articles best articles.

Amazing news everyone! Browserling just reached 200 paying customers! It took us 2 years to get to 100 customers, and now it took just 5 months to get to 200 customers. We're looking forward to doubling our customer base again soon.

Our new customers include companies such as:


VirtualNerd, Fluent Concepts, BabyList, MapBox, Helicoid, DabApps, Next Social, My Bank Tracker

As well as many individuals.

Thank you all!

Browserling is an interactive cross-browser testing tool. It lets you test your website in older Internet Explorers such as IE 6, IE 7, IE 8 and IE 9, Firefox, Chrome, Opera and Safari!


Try Browserling!

This article is part of the article series "Bash One-Liners Explained."
<- previous article next article ->

This is the fourth part of the Bash One-Liners Explained article series. In this part I'll teach you how to work with bash history. I'll use only the best bash practices, various bash idioms and tricks. I want to illustrate how to get various tasks done with just bash built-in commands and bash programming language constructs.

I'll break this part into several sub-parts as it's very tiring to write long articles, and I'd rather publish many short articles and make quick progress.

See the first part of the series for introduction. After I'm done with the series I'll release an ebook (similar to my ebooks on awk, sed, and perl), and also bash1line.txt (similar to my perl1line.txt).

Also see my other articles about working fast in bash from 2007 and 2008:

Parts of this post are based on my earlier post Definitive Guide to Bash Command Line History. Check it out, too!

Let's start.

Part IV: Working with history

1. Erase all shell history

$ rm ~/.bash_history

Bash keeps the shell history in a hidden file called .bash_history. This file is located in your home directory. To get rid of the history, just delete it.

Note that if you logout after erasing the shell history, this last rm ~/.bash_history command will be logged. If you want to hide that you erased shell history, see the next one-liner.

2. Stop logging history for this session

$ unset HISTFILE

The HISTFILE special bash variable points to the file where the shell history should be saved. If you unset it, bash won't save the history.

Alternatively you can point it to /dev/null,

$ HISTFILE=/dev/null

3. Don't log the current command to history

Just start the command with an extra space:

$  command

If the command starts with an extra space, it's not logged to history.

Note that this only works if the HISTIGNORE variable is properly configured. This variable contains : separated values of command prefixes that shouldn't be logged.

For example to ignore spaces set it to this:

HISTIGNORE="[ ]*"

My HISTIGNORE looks like this:

HISTIGNORE="&:[ ]*"

The ampersand has a special meaning - don't log repeated commands.

4. Change the file where bash logs command history

$ HISTFILE=~/docs/shell_history.txt

Here we simply change the HISTFILE special bash variable and point it to ~/docs/shell_history.txt. From now on bash will save the command history in that file.

5. Add timestamps to history log

$ HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S"

If you set the HISTTIMEFORMAT special bash variable to a valid date format (see man 3 strftime) then bash will log the timestamps to the history log. It will also display them when you call the history command (see the next one-liner).

6. Show the history

$ history

The history command displays the history list with line numbers. If HISTTIMEFORMAT is set, it also displays the timestamps.

7. Show the last 50 commands from the history

$ history 50

If you specify a numeric argument, such as 50, to history, it prints the last 50 commands from the history.

7. Show the top 10 most used commands from the bash history

$ history |
    sed 's/^ \+//;s/  / /' |
    cut -d' ' -f2- |
    awk '{ count[$0]++ } END { for (i in count) print count[i], i }' |
    sort -rn |
    head -10

This one-liner combines bash with sed, cut, awk, sort and head. The perfect combination. Let's walk through this to understand what happens. Let's say the output of history is:

$ history
    1  rm .bash_history 
    2  dmesg
    3  su -
    4  man cryptsetup
    5  dmesg

First we use the sed command to remove the leading spaces and convert the double space after the history command number to a single space:

$ history | sed 's/^ \+//;s/  / /'
1 rm .bash_history 
2 dmesg
3 su -
4 man cryptsetup
5 dmesg

Next we use cut to remove the first column (the history numbers):

$ history |
    sed 's/^ \+//;s/  / /' |
    cut -d' ' -f2-

rm .bash_history 
dmesg
su -
man cryptsetup
dmesg

Next we use awk to record how many times each command has been seen:

$ history |
    sed 's/^ \+//;s/  / /' |
    cut -d' ' -f2- |
    awk '{ count[$0]++ } END { for (i in count) print count[i], i }'

1 rm .bash_history 
2 dmesg
1 su -
1 man cryptsetup

Then we sort the output numerically and reverse it:

$ history |
    sed 's/^ \+//;s/  / /' |
    cut -d' ' -f2- |
    awk '{ count[$0]++ } END { for (i in count) print count[i], i }' |
    sort -rn

2 dmesg
1 rm .bash_history 
1 su -
1 man cryptsetup

Finally we take the first 10 lines that correspond to 10 most frequently used commands:

$ history |
    sed 's/^ \+//;s/  / /' |
    cut -d' ' -f2- |
    awk '{ count[$0]++ } END { for (i in count) print count[i], i }' |
    sort -rn |
    head -10

Here is what my 10 most frequently used commands look like:

2172 ls
1610 gs
252 cd ..
215 gp
213 ls -las
197 cd projects
155 gpu
151 cd
119 gl
119 cd tests/

Here I've gs that's an alias for git status, gp is git push, gpu is git pull and gl is git log.

8. Execute the previous command quickly

$ !!

That's right. Type two bangs. The first bang starts history substitution, and the second one refers to the last command. Here is an example:

$ echo foo
foo
$ !!
foo

Here the echo foo command was repeated.

It's especially useful if you wanted to execute a command through sudo but forgot. Then all you've to do is run:

$ rm /var/log/something
rm: cannot remove `/var/log/something': Permission denied
$
$ sudo !!   # executes `sudo rm /var/log/something`

9. Execute the most recent command starting with the given string

$ !foo

The first bang starts history substitution, and the second one refers to the most recent command starting with foo.

For example,

$ echo foo
foo
$ ls /
/bin /boot /home /dev /proc /root /tmp
$ awk -F: '{print $2}' /etc/passwd
...
$ !ls
/bin /boot /home /dev /proc /root /tmp

Here we executed commands echo, ls, awk, and then used !ls to refer to the ls / command.

10. Open the previous command you executed in a text editor

$ fc

Fc opens the previous command in a text editor. It's useful if you've a longer, more complex command and want to edit it.

For example, let's say you've written a one-liner that has an error, such as:

$ for wav in wav/*; do mp3=$(sed 's/\.wav/\.mp3/' <<< "$wav"); ffmpeg -i "$wav" "$m3p"; done

And you can't see what's going on because you've to scroll around, then you can simply type fc to load it in your text editor, and then quickly find that you mistyped mp3 at the end.

Enjoy!

Enjoy the article and let me know in the comments what you think about it. In the next sub-part I'll continue discussing history keyboard shortcuts, history shell options and more.