Wednesday, February 10, 2010

Prompt Tricks

Kind book reader Bruce Fowler sent me a note shortly after the book was released providing some feedback on Chapter 14, "Customizing The Prompt."  He pointed out (quite rightly) that there are many practical things you can do with the shell prompt that can make your command line experience easier and more enjoyable.  One of his favorites was simply coloring the shell prompt so that it is easier to see in a long stream of text output.


Your shell prompt is defined by the contents of a shell variable named PS1.  The variable contains a combination of literal text and special codes that expand into various elements of the prompt when it is displayed.  We can easily modify our prompt to give it the color green.

First, we'll save a copy of our current prompt string in a new variable named PS1_OLD:

me@linuxbox:~$ PS1_OLD="$PS1"

Later, if you want to revert to the original prompt settings, you can do this:

me@linuxbox:~$ PS1="$PS1_OLD"

Next, we'll add ANSI escape sequences to the beginning and end of our prompt string to set the color to green and back to its original color:

me@linuxbox:~$  PS1="\[\033[01;32m\]$PS1\[\033[00m\]"

Now, we have a green prompt!

me@linuxbox:~$

Let's break this prompt string down:

Element Meaning
\[ Beginning of non-printing sequence. You need to tell bash that the sequence that follows does not actually print characters on the screen (it only sends control instructions to your terminal emulator setting the color). bash would otherwise count the characters and this would mess up bash's calculation of the cursor position which it does to support command line editing.
\033[01;32m ANSI sequence to set foreground text green.
\] End of non-printing sequence.
$PS1 Original prompt string. We're embedding the original prompt string in the middle to retain its design.
\[ Beginning of non-printing sequence. Again, the sequence that follows to reset the colors does not print characters on the screen.
\033[00m Sequence to reset attributes and color to previous settings.
\] End of non-printing sequence.

To make this change permanent, add this line to your .bashrc file:

PS1="\[\033[01;32m\]$PS1\[\033[00m\]"

Bruce also suggested making the prompt for the root account (if your system is so equipped) a different color (like red) to remind you that you are operating as the superuser.

Finally, Bruce included a short script that he picked up from a USENET group which displays all the possible color and attributes supported by ANSI:

#!/bin/sh
############################################################
# Nico Golde (nico(at)ngolde.de) Homepage: http://www.ngolde.de
# Last change: Mon Feb 16 16:24:41 CET 2004
############################################################

for attr in 0 1 4 5 7 ; do
    echo "----------------------------------------------------------------"
    printf "ESC[%s;Foreground;Background - \n" $attr
    for fore in 30 31 32 33 34 35 36 37; do
        for back in 40 41 42 43 44 45 46 47; do
            printf '\033[%s;%s;%sm %02s;%02s  ' $attr $fore $back $fore $back
        done
        printf '\n'
    done
    printf '\033[0m'
done

When executed, the results look like this:



Further Reading

Friday, February 5, 2010

Project: Building An All-Text Linux Workstation - Part 10

In this installment, we'll finish up our look at email.

Now that we have mutt talking to the outside world, it would be handy if we could also send messages from the command line as we did in Part 8 using the mail command. Fortunately, mutt supports the same technique.

Using mutt On The Command Line

We can send the output of a command to a remote email recipient via our POP3 configuration using a command such as this:

me@linuxbox:~$ ls -l | mutt-p -s "test message" someone@somewhere.com

Here we used the alias "mutt-p" described in the previous installment. Please note that if such an alias were used in a shell script, it would most likely fail because the .bashrc file where the alias is defined is not sourced by the copy of the shell executing the script. In such a case, we would need to spell the command out fully:

mutt -F ~/.muttrc-pop3 -s "test message" someone@somewhere.com

Another Mail Client

While Debian installs mutt by default, it's not the only full-featured text-based email client available. Another popular choice is Alpine, the successor to the popular PINE email client from the University of Washington. Alpine is similar to mutt in most respects though I think it has an easier user interface:


In addition to the usual email functions, Alpine also sports its own address book and Alpine's configuration is adjustable from within the user interface so that editing the configuration files is not strictly necessary, but its configuration is as complicated as mutt's.


Alpine configuration, top-level


Alpine configuration, down deep

Summing Up

Text-based email clients have a long and storied history in the Unix world and remain the tools of choice for serious email users. As you dig deeper into the documentation of mutt and Alpine, you will find that nothing compares to the configurability of either of these programs.

Further Reading

More support resources for mutt:
For Alpine:
Other installments in this series: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

Wednesday, February 3, 2010

Project: Building An All-Text Linux Workstation - Part 9

In our previous installment we saw how our Debian workstation supports email between users on the system.  This time we're going to add the ability to send and receive email over the Internet.

One of the reasons that email is such a difficult subject to cover is that there are so many different kinds of email tools and configurations.  For this lesson, we are going to create a really simple configuration designed to satisfy the basic needs of a residential user.  It is certainly possible to create a much more sophisticated configuration.  In fact, with Linux, almost any kind of email setup is possible, including huge enterprise-class solutions.

As we saw last time, our email client, mutt, reads mail messages that it finds in a mailbox file located in /var/mail.  To send mail, mutt passes a composed message to the exim mail transport agent (MTA) for delivery.  So how do we send mail to the outside world?

The Traditional Way

The traditional way is to configure the MTA to communicate with a smarthost, a remote server that can determine where the remote recipient's mailbox is located and pass the messages to it.  Such a configuration can be easily created in Debian by telling the package installer to reconfigure the exim4 package using a script built into the package.  This technique is good if your workstation is on a corporate network and you have a mail server through which mail from all users is sent and received.  You can find a complete description of this configuration process here.

Receiving mail is traditionally done by either configuring the MTA to receive incoming connections from other mail servers, or by running a mail delivery agent program (such as fetchhmail or getmail) that copies the contents of remote mailboxes to the local mailbox.

However, the traditional approach is not well suited to our residential workstation because each user may have different email providers, so we need a solution that is potentially different for each user.  Of course, with enough configuration, the traditional approach can be made to work, but it wouldn't be pretty.

The Client Centric Way

For those of you who have used GUI-based email clients like Evolution or Thunderbird, the traditional way probably seems very alien and complex.  You have likely used a single email client program that performs all the functions of the multi-program traditional method.  That's the approach that we'll try to take.  Too bad mutt makes it so hard.

The designers of mutt have taken the fairly stern view that a mail user agent (MUA) should be a mail user agent and nothing more.  In recent years however, they have softened their stance on this issue somewhat and now offer optional support for SMTP (Simple Mail Transport Protocol) to communicate with smarthosts and POP and IMAP support for reading mail on remote servers.  Fortunately, the version of mutt supplied with Debian has these optional features compiled in.

In the exercise that follows, we are going to configure mutt to use a POP3 server and an external smarthost, and an IMAP server and an external smarthost.  By leaving the configuration of exim unchanged, we will continue to send and receive local mail.  Note that you will need to adjust the configuration files listed below to fit your ISP's specific requirements.

POP3 Configuration

The Post Office Protocol (POP) is an older and less sophisticated mail delivery system.  It is common among residential ISPs.  To configure mutt to download messages from a remote POP3 (POP version 3, the version most often used today) and to send messages via a remote SMTP server acting as the smarthost, we will create a mutt configuration file and name it ~/.muttrc-pop3:


### POP3 setup for incoming mail

# File where incoming messages will be kept
set spoolfile=~/mailbox-pop3

# Your user name as understood by your ISP
set pop_user = "username"

# Your password as understood by your ISP
set pop_pass = "password"

# Host name of ISP's POP3 server
set pop_host = "mail.your_isp.com"

# Do not delete messages from POP3 server after downloading.
# Change to "yes" after testing.
set pop_delete = no


### SMTP setup for outgoing mail

# URL of ISP's SMTP server
set smtp_url = "smtp://username@mail.your_isp.com/"

# Password for SMTP server
set smtp_pass = "password"

# Your email address as understood by your ISP
set from = "username@your_isp.com"

# How you want your name to appear in email messages
set realname = "Your Name"

To execute this configuration, we invoke mutt this way:

me@linuxbox:~$ mutt -F ~/.muttrc-pop3

IMAP Configuration

If you have a good ISP, they will offer Internet Message Access Protocol (IMAP) on their mail server.  IMAP keeps your mail on the server and allows you to maintain multiple folders and has a host of other features lacking in the POP system.  In the configuration below, we will communicate with a remote IMAP server using SSL for encryption.  We will call this configuration file ~/.muttrc-imap.

Note: In order to use SSL authentication, make sure you have the libsasl2-modules package installed on your system.


### IMAP setup for incoming mail

# Your email address as understood by your ISP
set imap_user = "username@your_isp.com"

# Your account password
set imap_pass = "password"

# Name of your ISP's IMAP server and folder locations
set folder = "imaps://mail.your_isp.com:993"
set spoolfile = "+INBOX"
set postponed="+/Drafts"

### SMTP setup for outgoing mail (using SSL)

# URL of ISP's SMTP server including SSL (smtps://) and port
# number (:465) as needed.
set smtp_url = "smtps://username@mail.your_isp.com:465/"

# Password for SMTP server
set smtp_pass = "password"

# Your email address as understood by your ISP
set from = "username@your_isp.com"

# How you want your name to appear in email messages
set realname = "Your Name"

### Files needed to store IMAP cache and SSL certificates

set header_cache=~/.mutt/cache/headers
set message_cachedir=~/.mutt/cache/bodies
set certificate_file=~/.mutt/certificates


To use this configuration, we need to create the directories for the IMAP cache and for SSL certificate storage.  We can create them with the following command:

me@linuxbox:~$ mkdir -p ~/.mutt/cache/bodies

To execute this configuration, we invoke mutt like this:

me@linuxbox:~$ mutt -F ~/.muttrc-imap


Using Aliases To Support Multiple Configurations

We can simplify the invocation of mutt by adding these two lines to our ~/.bashrc file:

alias mutt-p='mutt -F ~/.muttrc-pop3'
alias mutt-i='mutt -F ~/.muttrc-imap'


There you have it.  We now have mutt commands for handling local mail (mutt), POP3 mail (mutt-p) and IMAP mail (mutt-i).

Further Reading

Background on mail protocols:
Using mutt with Gmail via IMAP:
Mutt configuration samples:
Other installments in this series: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

Friday, January 29, 2010

Some Site News

  • I neglected to mention back in December that LinuxCommand.org reached the 3 million visitors mark. Thanks everyone!
  • The book has been downloaded over 4000 times so far. That comes to about 100 downloads a day. That's pretty good, but I think it could do better. If you have enjoyed the book, why not help support the effort to train new Linux users? Mention the book on blogs and forums, post a review somewhere, announce its availability on news sites, and just generally talk it up. Let's all help make the world a more Linux-friendly place! For those of you who already have, thank you!

Wednesday, January 27, 2010

Project: Building An All-Text Linux Workstation - Part 8

After a long hiatus, we resume work on our all-text workstation.  There are two reasons I waited so long to resume this series: 1. I wanted to devote my full efforts to finishing the book, and 2. I knew this next installment would be hard to write.

The topic for the next two installments is one that should be fairly simple -- email.  Only it's not simple.  In fact, it's rather hellish.

Our Debian system already has a working email system installed and running on it.  In its default form, it allows the users of the machine to send email messages to each other.  This is a traditional Unix idea, since all Unix-like systems (such as Linux) are intrinsically multi-user.

While our small workstation might not seem like a good candidate for such a configuration, it is actually quite useful.  The reason being that you can send email from scripts and other programs.  This is good for programs that run automatically and need to report problems to an operator.  They can just send email.

Before we demonstrate this capability, I need to explain (a little) how email works.

Email systems consist of two (and sometimes three) different components.  The most familiar component is called the Mail User Agent (MUA).  This is a program like Evolution or Thunderbird that is used to read and compose email messages.

The real work of an email system however is performed by another component called a Mail Transport Agent (MTA) which is tasked with moving the email message from one machine to the next and delivering incoming messages into the addressee's mailbox for reading by the MUA.  On some systems a third component, called a Mail Delivery Agent is used by the MTA to perform operations on received email messages before they are delivered to the user's mailbox.  These operations can include things as sorting messages into mail folders and spam filtering.

Our Debian system has an MTA program called exim4 and several MUAs.  The most sophisticated of the default MUAs is a program called mutt.

Let's fire up mutt and send ourselves some email:

me@linuxbox:~$ mutt

As mutt starts up, it prompts you to create a directory called Mail in your home directory:

/home/me/Mail does not exist. Create it? ([yes]/no):

Press the Enter key to select the default (yes).

Next we will see the mutt screen:


The mutt screen has a menu along the top, a message list in the middle, and a status line at the bottom.  We also see an error message at the very bottom of the screen because we don't yet have a mailbox file.  More about that in a minute.

Next, we compose a new mail message.  We do this by pressing the m key.  At the very bottom of the screen we are prompted for the address of the recipient.  Since we are sending it to ourselves, we enter "me".


Next, we are prompted for a message subject.  Let's enter "Test Message" at the prompt.  mutt then brings up the nano text editor so we can compose our message:


We'll type in our message and then type Ctrl-o then Enter to save our message and then Ctrl-x to exit nano.

mutt then displays a summary of the message:


(Ignore the "twin7" in the hostname.  It's an artifact of my network configuration.)

Again, we have a menu at the top of the screen.  We use the "y" selection to actually send the message, so go ahead and type y.

mutt passes the message to exim4 for delivery.  Type "q" to exit mutt.  When the shell prompt returns, we should see a message like this:

You have mail in /var/mail/me

The shell periodically checks for updates to a mailbox file.  The name of this file is stored in the environment variable MAIL and the interval for the update check is set by the shell variable MAILCHECK.

We can easily examine the contents of these variables like this:

me@linuxbox:~$ set | grep MAIL
MAIL=/var/mail/me
MAILCHECK=60

So we see that our mailbox is a file named /var/mail/me and that the shell checks for updates every 60 seconds.

Since email is always plain text we can look into our mailbox to see what email really looks like:

me@linuxbox:~$ less /var/mail/me

The contents of the mailbox will be something like this:

From me@linuxbox.localdomain Wed Jan 27 11:03:04 2010
Return-path:
Envelope-to: me@linuxbox.localdomain
Delivery-date: Wed, 27 Jan 2010 11:03:04 -0500
Received: from me by linuxbox.localdomain with local (Exim 4.69)
    (envelope-from )
    id 1NaAMC-0000fi-FF
    for me@linuxbox.localdomain; Wed, 27 Jan 2010 11:03:04 -0500
Date: Wed, 27 Jan 2010 11:03:04 -0500
From: Linux User
To: Linux User
Subject: Test Message
Message-ID: <20100127160304.GA2578@linuxbox.localdomain>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
User-Agent: Mutt/1.5.18 (2008-05-17)

This is a test message.

As we can see, the message is mostly header followed by the actual message content.

If we launch mutt again, the message will show up in the message list pane of its main screen:

me@linuxbox:~$ mutt


Pressing the Enter key will display the contents of the message.
Pressing either i or q will return you to the message list and q will exit mutt.

mutt is not the only MUA installed by default.  There are some other programs that you can use on the command line to send mail.  mail is one such program.  The mail program is actually a full MUA like mutt but much older.  You can think of it as mutt's stone-age cousin.  It's rarely used as interactive mail reader but it finds a lot of use as command line email sender.  It can accept standard input on the command line to create messages.  Here's an example using mail to send the results of a command to a user:

me@linuxbox:~$ ls -l /usr/bin | mail -s "Listing of /usr/bin" me

This command sends the results of ls to user me.  Using the -s option sets the message subject line.

Running mutt again proves that we now have a second message in our mailbox:


That's all for now.  In the next episode, we'll look at how to deal with email from the outside world.

Further Reading

Here are some Wikipedia articles that provide some background and additional details:
Some documentation on mutt:
Other installments in this series: 1 2 3 4 5 6 7 8 9 10 11 12 13 14