String Constants and Terminal Output

Although Unix shells make no distinction between commands entered from the keyboard and those input from a script, there are certain shell features that are meant for scripting and not convenient or useful to use interactively. Many of these features will be familiar to anyone who has done computer programming. They include constructs such as comments, conditionals (e.g. if commands) and loops. The following sections provide a very brief introduction to shell constructs that are used in scripting, but generally not used on the command line.

A string constant in a shell script is anything enclosed in single quotes ('this is a string') or double quotes ("this is also a string").

Unlike most programming languages, text in a shell scripts that is not enclosed in quotes and does not begin with a '$' or other special character is also interpreted as a string constant. Hence, all of the following are the same:

shell-prompt: ls /etc
shell-prompt: ls "/etc"
shell-prompt: ls '/etc'
        

Most programming languages would interpret the '/' as a division operator and etc as a variable name. As you can see, Unix shell languages differ from general purpose languages significantly.

If a string contains white space (spaces or tabs), then it will be seen as multiple separate strings, unless we explicitly state otherwise by enclosing the string in quotes or escaping every white space character. The last example below will not work properly, since 'Program' and 'Files' are seen as separate arguments:

shell-prompt: cd 'Program Files'    # One directory name
shell-prompt: cd "Program Files"    # One directory name
shell-prompt: cd Program\ Files     # One directory name
shell-prompt: cd Program Files      # Two directory names
        

Note

Special sequences such as '\n', which represents the newline character, must be enclosed in quotes or escaped. Otherwise, the '\' is seen as escaping the 'n', which has no effect. Remember that '\' tells the shell to not to interpret the next character as special. This is useful behind a special character such as '*' or '[', but 'n' has no special meaning in the first place, so a '\' before it will not alter its interpretation.

#!/bin/sh -e

printf Hello\n
printf "Hello\n"
printf Hello\\n

HellonHello
Hello
        

Output commands are only occasionally useful at the interactive command line. We may sometimes use them to take a quick look at a variable such as $PATH. Output commands are far more useful in scripts, and are used in the same ways as output statements in any programming language.

The echo command is commonly used to output text to the terminal:

shell-prompt: echo 'Hello!'
Hello!
shell-prompt: echo $PATH
/usr/local/bin:/home/bacon/scripts:/home/bacon/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/sbin
        

However, echo should be avoided, since it is not portable across different shells and even the same shell on different Unix systems. There are many different implementations of echo commands, some internal to the shell and some external programs. Different implementations of echo use different command-line flags and special characters to control output formatting. In addition, the output formatting capabilities of echo commands are extremely limited.

The printf command supersedes echo. It has a rich set of capabilities similar to the printf() function in the standard C library and in Java. The printf command is specified in the POSIX.2 standard, so its behavior is largely the same on all Unix systems. Printf is an external command, so it is independent of which shell you are using. Unlike echo, it does not add a newline character by default.

shell-prompt: printf 'Hello!\n'
Hello!
        

The general syntax of a printf command is as follows:

printf format-string [arguments]
        

The format-string contains literal text and a format specifier to match each of the arguments that follows. Each format specifier begins with a '%' and is followed by symbols indicating the format in which to print the argument.

The format string can be the sole argument to printf, as long as it does not contain any specifiers. Otherwise, there must be exactly one argument following the format string to match each specifier.

Table 4.3. Printf Format Specifiers

SpecifierOutput
%sString
%20sString right-justified in a 20-character space
%-20sString left-justified in a 20-character space

The printf command also recognizes most of the same special character sequences as the C printf() function:

Table 4.4. Special Character Sequences

SequenceMeaning
\nNewline (move down to next line)
\rCarriage Return (go to beginning of current line)
\tTab (go to next tab stop)

#!/bin/sh -e

printf '%s.\n' 'This is default format'
printf '%50s.\n' 'This is right-justified'
printf '%-50s.\n' 'This is left-justified'
        
This is default format.
                           This is right-justified.
This is left-justified                            .
        

There are many other format specifiers and special character sequences. For complete information, run man printf.

Any program that prints error messages should send them to the standard error, not the standard output. The printf command always outputs to the standard output, but we can work around it using redirection to /dev/stderr.

        printf 'Error: Input must be a number.\n' >> /dev/stderr
        

Practice Break

Write a shell script containing the printf command above and run it. Write the same script using two different shells, such as Bourne shell and C shell. What is the difference between the two scripts?

Practice

Note

Be sure to thoroughly review the instructions in Section 2, “Practice Problem Instructions” before doing the practice problems below.
  1. How does the Unix shell interpret the character sequence firstname? How would it be interpreted by most general purpose programming languages?

  2. Show three ways to make the shell see the character sequence Alfred E. Neumann as a single string rather than three strings. Note that there are two spaces after the period.

  3. What is the output of the following statement? How do we make it interpret the \n as a newline?

    #!/bin/sh -e
    
    printf \$15*3=\$45\n
            
  4. What are some of the problems with the echo command? What is the solution?

  5. Show a printf statement that prints the number 32,767, right-justified in a field of 10 columns.

  6. Show a printf statement that prints the error message "Error: Invalid data found in column 2 of input." to the standard error.