One powerful technique when writing shell scripts is to take advantage of the terminal emulation features of your terminal application (whether it is Terminal, an xterm, or some other application) to display formatted content.
You can use the printf command to easily create columnar layouts without any special tricks. For more visually exciting presentation, you can add color or text formatting such as boldface or underlined display using ANSI (VT100/VT220) escape sequences.
In addition, you can use ANSI escape sequences to show or hide the cursor, set the cursor position anywhere on the screen, and set various text attributes, including boldface, inverse, underline, and foreground and background color.
Using the printf Command for Tabular Layout
Truncating Strings
Using ANSI Escape Sequences
ANSI Escape Sequence Tables
Much like C and other languages, most operating systems that support shell scripts also provide a command-line version of printf. This command differs from the C printf function in a number of ways. These differences include the following:
The %c directive does not perform integer-to-character conversion. The only way to convert an integer to a character with the shell version is to first convert the integer into octal and then print it by using the octal value as a switch. For example, printf "\144" prints the lowercase letter d.
The command-line version supports a much smaller set of placeholders. For example, %p (pointers) does not exist in the shell version.
The command-line version does not have a notion of long or double-precision numbers. Although flags with these modifiers are allowed (%lld, for example), the modifiers are ignored. Thus, there is no difference between %d, %ld, and %lld.
Large integers may be truncated to 32-bit signed values.
Double-precision floating-point values may be reduced to single-precision values.
Floating point precision is not guaranteed (even for single-precision values) because some imprecision is inherent in the conversion between strings and floating-point numbers.
Much like the printf statement in other languages, the shell script printf syntax is as follows:
printf "format string" argument ... |
Like the C printf function, the command-line printf format string contains some combination of text, switches (\n and \t, for example), and placeholders (%d, for example).
The most important feature of printf for tabular layouts is the padding feature. Between the percent sign and the type letter, you can place a number to indicate the width to which the field should be padded. For a floating-point placeholder (%f), you can optionally specify two numbers separated by a decimal point. The leftmost value indicates the total field width, while the rightmost value indicates the number of decimal places that should be included. For example, you can print pi to three digits of precision in an 8-character-wide field by typing printf "%8.3f" 3.14159265.
In addition to the width of the padding, you can add certain prefixes before the field width to indicate special padding requirements. They are:
Minus sign (-)—indicates the field should be left justified. (Fields are right justified by default.)
Plus sign (+)—indicates that a sign should be prepended to a numerical argument even if it has a positive value.
Space—indicates that a space should be added to a numerical argument in place of the sign if the value is positive. (A plus sign takes precedence over a space.)
Zero (0)—indicates that numerical arguments should be padded with leading zeroes instead of spaces. (A minus sign takes precedence over a zero.)
For example, if you want to create a four-column table of name, address, phone number, and GPA, you might write a statement like this:
Listing 7-6 Columnar printing with printf
#/bin/sh |
NAME="John Doe" |
ADDRESS="1 Fictitious Rd, Bucksnort, TN" |
PHONE="(555) 555-5555" |
GPA="3.885" |
printf "%20s | %30s | %14s | %5s\n" "Name" "Address" "Phone Number" "GPA" |
printf "%20s | %30s | %14s | %5.2f\n" "$NAME" "$ADDRESS" "$PHONE" "$GPA" |
The printf statement pads the fields into neat columns and truncates the GPA to two decimal places, leaving room for three additional characters (the decimal point itself, the ones place, and a leading space). You should notice that the additional arguments are all surrounded by quotation marks. If you do not do this, you will get incorrect behavior because of the spaces in the arguments.
Note: The printf command, like its C function sibling, does not truncate values to fit within the specified field width. For examples of how to truncate strings, see “Truncating Strings.”
The next sample shows number formatting:
#!/bin/sh |
GPA="3.885" |
printf "%f | whatever\n" "$GPA" |
printf "%20f | whatever\n" "$GPA" |
printf "%+20f | whatever\n" "$GPA" |
printf "%+020f | whatever\n" "$GPA" |
printf "%-20f | whatever\n" "$GPA" |
printf "%- 20f | whatever\n" "$GPA" |
This prints the following output:
3.885000 | whatever |
3.885000 | whatever |
+3.885000 | whatever |
+000000000003.885000 | whatever |
3.885000 | whatever |
3.885000 | whatever |
Most of the same formatting options apply to %s and %d (including, surprisingly, zero-padding of string arguments). For more information, see the manual page for printf.
To truncate a value to a given width, you can use a simple regular expression to keep only the first few characters. For example, the following snippet copies the first seven characters of a string:
STRING="whatever you want it to be" |
TRUNCSTRING="`echo "$STRING" | sed 's/^\(.......\).*$/\1/'`" |
echo "$TRUNCSTRING" |
As an alternative, you can use a more general-purpose routine such as the one in Listing 7-7, which truncates a string to an arbitrary length by building up a regular expression.
Listing 7-7 Truncating text to column width
function trunc_field()
{
local STR=$1
local CHARS=$2
local EXP=""
local COUNT=0
while [ $COUNT -lt $CHARS ] ; do
EXP="$EXP."
COUNT=`expr $COUNT + 1`
done
echo $STR | sed "s/^\($EXP\).*$/\1/"
}
printf "%10s | something\n" "`trunc_field "$TEXT" 20`" |
Of course, you can do this much faster by either caching these strings or replacing most of the function with a single line of Perl:
echo "$STR" | perl -e "$/=undef; print substr(<STDIN>, 0, $CHARS);" |
Finally, if you are willing to write code that is extremely nonportable (using a syntax that does not even work in zsh), you can use Bash-specific substring expansion:
echo "${STR:0:8}" |
You can learn about similar operations in the manual page for bash under the “Parameter Expansion” heading. As a general rule, however, you should avoid such shell-specific tricks.
You can use ANSI escape sequences to add color or formatting to text displayed in the terminal, reposition the cursor, set tab stops, clear portions of the display, change scrolling behavior, and more. This section includes a partial list of many commonly used escape sequences, along with examples of how to use them.
Important: For the purposes of this section, the Esc (escape) key is represented by the notation ^[ because the ASCII character for the Esc key is the same as the ASCII character for Control-bracket (character 27). Thus, when you see ^[[, it means Esc followed by a bracket. (Nearly all ANSI escape sequences begin with Esc-bracket, though there are a few exceptions.)
There are two ways to generate escape sequences: direct printing and using the terminfo database. Printing the sequences directly has significant performance advantages but is less portable because it assumes that all terminals are ANSI/VT100/VT220-compliant. A good compromise is to combine these two approaches by caching the values generated with a terminfo command such as tput at the beginning of your script and then printing the values directly elsewhere in the script.
Generating escape sequences with the terminfo database is relatively straightforward once you know what terminal capabilities to request. You can find several tables containing capability information, along with the standard ANSI/VT220 values for each capability, in “ANSI Escape Sequence Tables.” (Note that not all ANSI escape sequences have equivalent terminfo capabilities, and vice versa.)
Once you know what capability to request (along with any additional arguments that you must specify), you can use the tput command to output the escape sequence (or capture the output of tput into a variable so you can use it later). For example, you can clear the screen with the following command:
tput cl |
Some terminfo database entries contain placeholders for numeric values, such as row and column information. The easiest way to use these is to specify those numeric values on the command line when calling tput. However, for performance, it may be faster to substitute the values yourself. For example, the capability cup sets the cursor position to a row and column value. The following command sets the position to row 3, column 7:
tput cup 3 7 |
You can, however, obtain the unsubstituted string by requesting the capability without specifying row and column parameters. For example:
tput cup | less |
By piping the data to less, you can see precisely what the tput tool is providing, and you can look up the parameters in the manual page for terminfo. This particular example prints the following string:
^[[%i%p1%d;%p2%dH |
The %i notation means that the first two (and only the first two) values are one greater than you might otherwise expect. (For ANSI terminals, columns and rows number from 1 rather than from 0). The %p1%d means to push parameter 1 onto the stack and then print it immediately. The parameter %p2%d is the equivalent for parameter 2.
As you can see from even this relatively simple example, the language used for terminfo is quite complex. Thus, while it may be acceptable to perform the substitution for simple terminals such as VT100 yourself, you may still be trading performance for portability. In general, it is best to let tput perform the substitutions on your behalf.
To use an ANSI escape sequence without using tput, you must first be able to print an escape character from your script. There are three ways to do this:
Use printf to print the escape sequence. In a string, the \e switch prints an escape character. This is the easiest way to print escape sequences.
For example, the following snippet shows how to print the reset sequence (^[c):
printf "\ec" # resets the screen |
Embed the escape character in your script. The method of doing this varies widely from one editor to another. In most text-based editors and on the command line itself, you do this by pressing Control-V followed by the Esc key. Although this is the fastest way to print an escape sequence, it has the disadvantage of making your script harder to edit.
For example, you might write a snippet like this one:
echo "^[c" # Read the note below!!! |
Note: You must enter this escape character manually; copying and pasting the text in this example will not work.
To enter the above escape sequence, type echo followed by a space and double-quote mark. Then press Control-V followed by the Esc key to add the escape character. Next, type a lowercase c. Finally, close the double-quote mark and press Return.
Use printf to store an escape character into a variable. This is the recommended technique because it is nearly as fast as embedding the escape character but does not make the code hard to read and edit.
For example, the following code sends a terminal reset command (^[c):
#!/bin/sh |
ESC=`printf "\e"` # store an escape character |
# into the variable ESC |
echo "$ESC""c" # Echo a terminal reset command. |
Because the terminal reset command is one of only a handful of escape sequences that do not start with a left square bracket, it is worth pointing out the two sets of double-quote marks after the variable in the above example. Without those, the shell would try to print the value of the variable ESCc, which does not exist.
There are four basic categories of escape codes:
Cursor manipulation routines (described in Table 7-2) allow you to move the cursor around on the screen, show or hide the cursor, and limit scrolling to only a portion of the screen.
Attribute manipulation sequences (described in “Attribute and Color Escape Sequences”) allow you to set or clear text attributes such as underlining, boldface display, and inverse display.
Color manipulation sequences (described in “Attribute and Color Escape Sequences”) allow you to change the foreground and background color of text.
Other escape codes (described in Table 7-5) support clearing the screen, clearing portions of the screen, resetting the terminal, and setting tab stops.
The terminal window is divided into a series of rows and columns. The upper-left corner is row 1, column 1. The lower-right corner varies depending on the size of the terminal window.
You can obtain the current number of rows and columns on the screen by examining the values of the shell variables LINES and COLUMNS. Thus, the screen coordinates range from (1, 1) to ($LINES, $COLUMNS). In most modern Bourne shells, the values for LINES and COLUMNS are automatically updated when the window size changes. This is true for both bash and zsh.
However, in bash, these variables are set only for interactive instances of the shell. This presents a small problem for shell scripts that care about window size. As a result, in versions of Mac OS X where the default shell is bash (Mac OS X v10.3 and newer), these variables are not defined in shell scripts that start with #!/bin/sh.
Of course, you could request zsh as the interpreter by changing the first line of your script to #!/bin/zsh, but this is not particularly portable. Fortunately, without changing shells, you can easily obtain the current row and column count with the code in Listing 7-8.
Listing 7-8 Obtaining terminal size using stty or tput
# If tput is available, this is the easy way: |
MYLINES=`tput lines` # ROWS |
MYCOLUMNS=`tput cols` # COLUMNS |
# If not, you can do it the hard way. This usually works. |
MYLINES=`stty -a | grep rows | sed 's/^.*;\(.*\)rows\(.*\);.*$/\1\2/' | sed 's/;.*$//' | sed 's/[^0-9]//g'` # ROWS |
MYCOLUMNS=`stty -a | grep columns | sed 's/^.*;\(.*\)columns\(.*\);.*$/\1\2/' | sed 's/;.*$//' | sed 's/[^0-9]//g'` # COLUMNS |
If you want to be particularly clever, you can also trap the SIGWINCH signal and update your script’s notion of lines and columns when it occurs. See “Trapping Signals” for more information.
Once you know the number of rows and columns on your screen, you can move the cursor around with the escape sequences listed in Table 7-2. For example, to set the cursor position to row 4, column 5, you could issue the following command:
printf "\e[4;5H" |
For other, faster ways to print escape sequences, see “Generating Escape Sequences Directly.”
Terminfo capability | Escape sequence | Description |
|---|---|---|
Note: The terminfo entry for Terminal does not support this option. |
| Hides the cursor. |
Note: The terminfo entry for Terminal does not support this option. |
| Shows the cursor. |
|
| Sets cursor position to row r, column c. |
(no equivalent) |
| Reports current cursor position as though typed from the keyboard (reported as |
|
| Saves current cursor position and style. |
|
| Restores previously saved cursor position and style. |
|
| Moves cursor up r rows. |
|
| Moves cursor down r rows. |
|
| Moves cursor right c columns. |
|
| Moves cursor left c columns. |
(no equivalent) |
| Disables automatic line wrapping when the cursor reaches the right edge of the screen. |
(no equivalent) |
| Enables line wrapping (on by default). |
(no equivalent) |
| Enables whole-screen scrolling (on by default). |
(no equivalent) |
| Enables partial-screen scrolling from row S to row E and moves the cursor to the top of this region. |
|
| Moves the cursor down by one line. |
|
| Moves the cursor up by one line. |
Attribute and color escape sequences allow you to change the attributes or color for text that you have not yet drawn. No escape sequence (scrolling notwithstanding) changes anything that has already been drawn on the screen. Escape sequences apply only to subsequent text.
For example, to draw a red “W” character, first send the escape sequence to set the foreground color to red (^[[31m), then print a “W” character, then send an attribute reset sequence (^[[m), if desired.
The attribute and color escape codes can be combined with other attribute and color escape codes in the form ^[[#;#;#;...#m. For example, you can combine the escape sequences ^[[1m (bold) and ^[[32m green text) into the sequence ^[[1;32m. Listing 7-9 prints a familiar phrase in multiple colors.
Listing 7-9 Using ANSI color
#!/bin/sh |
printf '\e[41mH\e[42me\e[43ml\e[44;32ml\e[45mo\e[m \e[46;33m' |
printf 'W\e[47;30mo\e[40;37mr\e[49;39ml\e[41md\e[42m!\e[m\n' |
Note: For consistent formatting, you may add a leading zero to any single-digit attribute escape sequences, if desired. For example, ^[[1m is equivalent to ^[[01m.
Table 7-3 contains a list of capabilities and escape sequences that control text style.
Terminfo capability | Escape sequence | Description |
|---|---|---|
Resetting attributes | ||
|
| Resets all attributes to their default values. |
Setting attributes | ||
|
| Enables “bright” display. This is basically boldface text. This code and code #2 ( |
|
| Enables “dim” display. This code and code #1 ( |
Note: In the terminfo database entry for Terminal, this is mapped to inverse because the VT100 “standout” mode is not supported. |
| Enables “standout” display. Not supported in Terminal. |
|
| Enables underlined display. |
Note: The terminfo entry for Terminal does not support this option. |
| <blink>. |
(No equivalent.) |
| Fast blink or strike-through. (Not supported in Terminal; behavior inconsistent elsewhere.) |
|
| Enables reversed (inverse) display. |
Note: The terminfo entry for Terminal does not support this option. |
| Enables hidden (background-on-background) display. |
| Unused. | |
Codes | Font selection codes. Unsupported in most terminal applications, including Terminal. | |
Clearing attributes | ||
(No equivalent.) |
| “Fraktur” typeface. Unsupported almost universally, and Terminal is no exception. |
| Unused. | |
Note: Technically, this capability is supposed to end standout mode, but it is overloaded to disable bold bright/dim mode as well. |
| Disables “bright” or “dim” display. This disables either code |
|
| Disables “standout” display. Not supported in Terminal. |
|
| Disables underlined display. |
(No equivalent. Use |
| </blink>. Also disables slow blink or strike-through ( |
| Unused. | |
(No equivalent. Use |
| Disables reversed (inverse) display. |
(No equivalent. Use |
| Disables hidden (background-on-background) display. |
| Unused. |
Table 7-4 contains a list of capabilities and escape sequences that control text and background colors.
Terminfo capability | Escape sequence | Description |
|---|---|---|
Foreground colors | ||
|
| Sets foreground color to black. |
|
| Sets foreground color to red. |
|
| Sets foreground color to green. |
|
| Sets foreground color to yellow. |
|
| Sets foreground color to blue. |
|
| Sets foreground color to magenta. |
|
| Sets foreground color to cyan. |
|
| Sets foreground color to white. |
| Unused. | |
|
| Sets foreground color to the default. |
Background colors | ||
|
| Sets background color to black. |
|
| Sets background color to red. |
|
| Sets background color to green. |
|
| Sets background color to yellow. |
|
| Sets background color to blue. |
|
| Sets background color to magenta. |
|
| Sets background color to cyan. |
|
| Sets background color to white. |
| Unused. | |
|
| Sets background color to the default. |
In addition to providing text formatting, ANSi escape sequences provide the ability to reset the terminal, clear the screen (or portions thereof), clear a line (or portions thereof), and set or clear tab stops.
For example, to clear all existing tab stops and set a single tab stop at column 20, you could use the snippet show in Listing 7-10.
Listing 7-10 Setting tab stops
#!/bin/sh |
echo # Start on a new line |
printf "\e[19C" # move right 19 columns to column 20 |
printf "\e[3g" # clear all tab stops |
printf "\e[W" # set a new tab stop |
printf "\e[19D" # move back to the left |
printf "Tab test\tThis starts at column 20." |
Table 7-5 contains a list of capabilities and escape sequences that perform other miscellaneous tasks such as cursor control, tab stop manipulation, and clearing the screen or portions thereof.
Terminfo capability | Escape sequence | Description |
|---|---|---|
Resetting the terminal | ||
Note: This resets many more things than |
| Resets the background and foreground colors to their default values, clears the screen, and moves the cursor to the home position. |
Clearing the screen | ||
|
| Clears to the bottom of the screen using the current background color. |
(no equivalent) |
| Clears to the top of the screen using the current background color. |
|
| Clears the screen to the current background color. On some terminals, the cursor is reset to the home position. |
Clearing the current line | ||
|
| Clears to the end of the current line. |
|
| Clears to the beginning of the current line. |
(no equivalent) |
| Clears the current line. |
Tab stops | ||
|
| Set horizontal tab at cursor position. |
(no equivalent) |
| Set vertical tab at current line. (Not supported in Terminal.) |
Codes | Redundant codes equivalent to codes | |
(no equivalent) |
| Clear horizontal tab at cursor position. |
(no equivalent) | ^[[1g | Clear vertical tab at current line. (Not supported in Terminal.) |
(no equivalent) | ^[[2g | Clear horizontal and vertical tab stops for current line only. (Not supported in Terminal.) |
| ^[[3g | Clear all horizontal tabs. |
Note: You can also set tab stops with the command-line utility tabs.
The tables in this chapter provide only some of the more commonly used escape sequences and terminfo capabilities. You can find an exhaustive list of ANSI escape sequences at http://www.inwap.com/pdp10/ansicode.txt and an exhaustive list of terminfo capabilities in the manual page for terminfo.
Before using capabilities or escape sequences not in this chapter, however, you should be aware that most terminal software (including Terminal in Mac OS X) does not support the complete set of ANSI escape sequences or terminfo capabilities.
Last updated: 2008-04-08