Chapter 7
Input/Output and Command-Line Processing
I/O Redirectors
The redirector <> is mainly meant for use with device files (in the /dev directory), i.e., files that correspond to hardware devices such as terminals and communication lines. Low-level systems programmers can use it to test device drivers; otherwise, it’s not very useful.
Redirector |
Function |
cmd1 | cmd2 | Pipe; take standard output of cmd1 as standard input to cmd2. |
> file | Direct standard output to file. |
< file | Take standard input from file. |
>> file | Direct standard output to file; append to file if it already exists. |
>| file | Force standard output to file even if noclobber is set. |
n>| file | Force output to file from file descriptor n even if noclobber is set. |
<> file | Use file as both standard input and standard output. |
n<> file | Use file as both input and output for file descriptor n. |
<< label | Here-document; see text. |
n> file | Direct file descriptor n to file. |
n< file | Take file descriptor n from file. |
n>> file | Direct file descriptor n to file; append to file if it already exists. |
n>& | Duplicate standard output to file descriptor n. |
n<& | Duplicate standard input from file descriptor n. |
n>&m | File descriptor n is made to be a copy of the output file descriptor. |
n<&m | File descriptor n is made to be a copy of the input file descriptor. |
&>file | Directs standard output and standard error to file. |
<&- | Close the standard input. |
>&- | Close the standard output. |
n>&- | Close the output from file descriptor n. |
n<&- | Close the input from file descriptor n. |
n>&word |
If n is not specified, the standard output (file descriptor 1) is used. If the digits in word do not specify a file descriptor open for output, a redirection error occurs. As a special case, if n is omitted, and word does not expand to one or more digits, the standard output and standard error are redirected as described previously. |
n<&word |
If word expands to one or more digits, the file descriptor denoted by n is made to be a copy of that file descriptor. If the digits in word do not specify a file descriptor open for input, a redirection error occurs. If word evaluates to -, file descriptor n is closed. If n is not specified, the standard input (file descriptor 0) is used. |
n>&digit- |
Moves the file descriptor digit to file descriptor n, or the standard output (file descriptor 1) if n is not specified. |
n<&digit- |
Moves the file descriptor digit to file descriptor n, or the standard input (file descriptor 0) if n is not specified. digit is closed after being duplicated to n. |
Here-documents
The << label redirector essentially forces the input to a command to be the shell’s standard input, which is read until there is a line that contains only label. The input in between is called a here-document. Here-documents aren’t very interesting when used from the command prompt. In fact, it’s the same as the normal use of standard input except for the label. We could use a here-document to simulate the mail facility. When you send a message to someone with the mail utility, you end the message with a dot (.). The body of the message is saved in a file, msgfile:
$ cat >> msgfile << . > this is the text of > our message. > .
bashbug script
MACHINE="i586" OS="linux-gnu" CC="gcc" CFLAGS=" -DPROGRAM='bash' -DHOSTTYPE='i586' -DOSTYPE='linux-gnu' -DMACHTYPE='i586-pc-linux-gnu' -DSHELL -DHAVE_CONFIG_H -I. -I. -I./lib -g -O2" RELEASE="2.01" PATCHLEVEL="0" RELSTATUS="release" MACHTYPE="i586-pc-linux-gnu" TEMP=/tmp/bbug.$$ case "$RELSTATUS" in alpha*|beta*) BUGBASH=chet@po.cwru.edu ;; *) BUGBASH=bug-bash@prep.ai.mit.edu ;; esac BUGADDR="${1-$BUGBASH}" UN= if (uname) >/dev/null 2>&1; then UN=`uname -a` fi cat > $TEMP <<EOF From: ${USER} To: ${BUGADDR} Subject: [50 character or so descriptive subject here (for reference)] Configuration Information [Automatically generated, do not change]: Machine: $MACHINE OS: $OS Compiler: $CC Compilation CFLAGS: $CFLAGS name output: $UN Machine Type: $MACHTYPE bash Version: $RELEASE Patch Level: $PATCHLEVEL Release Status: $RELSTATUS Description: [Detailed description of the problem, suggestion, or complaint.] Repeat-By: [Describe the sequence of events that causes the problem to occur.] Fix: [Description of how to fix the problem. If you don't know a fix for the problem, don't include this section.] EOF vi $TEMP mail $BUGADDR < $TEMP
File Descriptors
Task 7-2 |
You want to start a long job in the background (so that your terminal is freed up) and |
We’ll call this script start. The code is very terse:
"$@" > logfile 2>&1 &
This line executes whatever command and parameters follow start. (The command cannot contain pipes or output redirectors.) It sends the command’s standard output to logfile.Then, the redirector 2>&1 says, “send standard error (file descriptor 2) to the same place as standard output (file descriptor 1).” Since standard output is redirected to
logfile, standard error will go there too. The final & puts the job in the background so that you get your shell prompt back.
As a small variation on this theme, we can send both standard output and standard error into a pipe instead of a file: command 2>&1 | ... does this. (Make sure you understand why.) Here is a script that sends both standard output and standard error to the logfile (as above) and to the terminal:
"$@" 2>&1 | tee logfile &
These scripts have one shortcoming: you must remain logged in until the job completes. Although you can always type jobs (see Chapter 1) to check on progress, you can’t leave your terminal until the job finishes, unless you want to risk a breach of security. We’ll see how to solve this problem in the next chapter.
Before we leave this topic, we should just note that 1> is the same as >, and 0< is the same as <. If you understand this, then you probably know all you need to know about file descriptors.
String I/O
echo|printf|read
Option | Function |
-e | Turns on the interpretation of backslash-escaped characters |
-E | Turns off the interpretation of backslash-escaped characters on systems where this mode is the default |
-n | Omits the final newline (same as the c escape sequence) |
You may ask why this is any better than echo. The printf command has two parts, which is what makes it so powerful
printf format-string [arguments]
The first part is a string that describes the format specifications; this is best supplied as a string constant in quotes. The second part is an argument list, such as a list of strings or variable values that correspond to the format specifications. (The format is reused as necessary to use up all of the arguments. If the format requires more arguments than are supplied, the extra format specifications behave as if a zero value or null string, as appropriate, had been supplied). A format specification is preceded by a percent sign (%), and the specifier is one of the characters described below. Two of the main format specifiers are %s for strings and %d for decimal integers.
cor@debian:~/shell/mar9$ printf "%s %s " hello world hello world cor@debian:~/shell/mar9$ printf "hello %s " world hello world cor@debian:~/shell/mar9$ printf "|%10s| " hello | hello| cor@debian:~/shell/mar9$ printf "|%-10s| " hello |hello |
The allowed specifiers are shown in Table 7-4
Specifier | Description |
%c | ASCII character (prints first character of corresponding argument) |
%d | Decimal integer |
%i | Same as %d |
%e | Floating-point format ([-]d.precisione[+-]dd) (see following text for meaning of precision) |
%E | Floating-point format ([-]d.precisionE[+-]dd) |
%f | Floating-point format ([-]ddd.precision) |
%g | %e or %f conversion, whichever is shorter, with trailing zeros removed |
%G | %E or %f conversion, whichever is shortest, with trailing zeros removed |
%o | Unsigned octal value |
%s | String |
%u | Unsigned decimal value |
%x | Unsigned hexadecimal number; uses a-f for 10 to 15 |
%X | Unsigned hexadecimal number; uses A-F for 10 to 15 |
%% | Literal % |
The precision modifier, used for decimal or floating-point values, controls the number of digits that appear in the result. For string values, it controls the maximum number of characters from the string that will be printed.
You can specify both the width and precision dynamically, via values in the printf argument list. You do this by specifying asterisks, instead of literal values.
cor@debian:~/shell/mar9$ myvar=42.123456 cor@debian:~/shell/mar9$ printf "|%*.*G| " 5 6 $myvar |42.1235|
cor@debian:~/shell/mar9$ printf "|%*.*G|
" 5 5 $myvar
|42.123|
cor@debian:~/shell/mar9$ printf "|%*.*G|
" 5 4 $myvar
|42.12|
cor@debian:~/shell/mar9$ printf "|%*.*G|
" 5 3 $myvar
| 42.1|
cor@debian:~/shell/mar9$ printf "|%*.*G|
" 4 6 $myvar
|42.1235|
cor@debian:~/shell/mar9$ printf "|%*.*G|
" 3 6 $myvar
|42.1235|
cor@debian:~/shell/mar9$ printf "|%*.*G|
" 2 6 $myvar
|42.1235|
In this example, the width is 5, the precision is 6, and the value to print comes from the value of myvar.
Additional bash printf specifiers
Besides the standard specifiers just described, the bash shell (and other POSIX compliant shells) accepts two additional specifiers. These provide useful features at the expense of nonportability to versions of the printf command found in some other shells and in other places in UNIX:
%b
When used instead of %s, expands echo-style escape sequences in the argument string. For example:
cor@debian:~/shell/mar9$ printf "%s " 'hello world' hello world cor@debian:~/shell/mar9$ printf "%b " 'hello world' hello world
%q
When used instead of %s, prints the string argument in such a way that it can be used for shell input. For example:
cor@debian:~/shell/mar9$ printf "%q " "greetings to the world" greetings to the world
read
The other half of the shell’s string I/O facilities is the read command, which allows you to read values into shell variables. The basic syntax is:
read var1 var2...
This statement takes a line from the standard input and breaks it down into words delimited by any of the characters in the value of the environment variable IFS (see Chapter 4; these are usually a space, a TAB, and NEWLINE). The words are assigned to variables var1, var2, etc. For example:
If there are more words than variables, then excess words are assigned to the last variable. If you omit the variables altogether, the entire line of input is assigned to the variable REPLY.
Reading user input
echo 'Select a directory:' done=false while [ $done = false ]; do do=true num=1 for direc in $DIR_STACK; do echo $num) $direc num=$((num+1)) done echo -n 'directory? ' read REPLY if [ $REPLY -lt $num ] && [ $REPLY -gt 0 ]; then set - $DIR_STACK #statements that manipulate the stack... break else echo 'invalid selection.' fi done
Before leaving read, we should note that it has eight options: -a, -d, -e, -n, -p, -r, -t, and -s.
The first of these options allows you to read values into an array. Each successive item read in is assigned to the given array starting at index 0. For example:
cor@debian:~/shell/mar9$ read -a people alice duchess dodo cor@debian:~/shell/mar9$ echo ${people[2]} dodoalice