Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Cooper M.Advanced bash-scripting guide.2002.pdf
Скачиваний:
13
Добавлен:
23.08.2013
Размер:
916.67 Кб
Скачать

Advanced Bash−Scripting Guide

Appendix C. Exit Codes With Special Meanings

Table C−1. "Reserved" Exit Codes

Exit Code Number

Meaning

Example

Comments

 

 

 

 

1

catchall for general errors

let "var1 = 1/0"

miscellaneous errors, such

 

 

 

as "divide by zero"

 

 

 

 

2

misuse of shell builtins,

 

Seldom seen, usually

 

according to Bash

 

defaults to exit code 1

 

documentation

 

 

 

 

 

 

126

command invoked cannot

 

permission problem or

 

execute

 

command is not an

 

 

 

executable

 

 

 

 

127

"command not found"

 

possible problem with

 

 

 

$PATH or a typo

 

 

 

 

128

invalid argument to exit

exit 3.14159

exit takes only integer args

 

 

 

in the range 0 − 255

 

 

 

 

128+n

fatal error signal "n"

kill −9 $PPIDof script

$? returns 137 (128 + 9)

 

 

 

 

130

script terminated by

 

Control−C is fatal error

 

Control−C

 

signal 2, (130 = 128 + 2,

 

 

 

see above)

 

 

 

 

255

exit status out of range

exit −1

exit takes only integer args

 

 

 

in the range 0 − 255

 

 

 

 

According to the table, exit codes 1 − 2, 126 − 165, and 255 have special meanings, and should therefore be avoided as user−specified exit parameters. Ending a script with exit 127 would certainly cause confusion when troubleshooting (is the error a "command not found" or a user−defined one?). However, many scripts use an exit 1 as a general bailout upon error. Since exit code 1 signifies so many possible errors, this might not add any additional ambiguity, but, on the other hand, it probably would not be very informative either.

There has been an attempt to systematize exit status numbers (see /usr/include/sysexits.h), but this is intended mostly for C and C++ programmers. It would be well to support a similar standard for scripts. The author of this document proposes restricting user−defined exit codes to the range 64 − 113 (in addition to 0, for success), to conform with the C/C++ standard. This would still leave 50 valid codes, and make troubleshooting scripts more straightforward.

All user−defined exit codes in the accompanying examples to this document now conform to this standard, except where overriding circumstances exist, as in Example 9−2.

Issuing a $? from the command line after a shell script exits gives results consistent with the table above only from the Bash or sh prompt. Running the C−shell or tcsh may give different values in some cases.

Appendix C. Exit Codes With Special Meanings

341

Advanced Bash−Scripting Guide

Appendix D. A Detailed Introduction to I/O and I/O Redirection

written by Stephane Chazelas, and revised by the document author

A command expects the first three file descriptors to be available. The first, fd 0 (standard input, stdin), is for reading. The other two (fd 1, stdout and fd 2, stderr) are for writing.

There is a stdin, stdout, and a stderr associated with each command. ls 2>&1 means temporarily connecting the stderr of the ls command to the same "resource" as the shell's stdout.

By convention, a command reads its input from fd 0 (stdin), prints normal output to fd 1 (stdout), and error ouput to fd 2 (stderr). If one of those three fd's is not open, you may encounter problems:

bash$ cat /etc/passwd >&−

cat: standard output: Bad file descriptor

For example, when xterm runs, it first initializes itself. Before running the user's shell, xterm opens the terminal device (/dev/pts/<n> or something similar) three times.

At this point, Bash inherits these three file descriptors, and each command (child process) run by Bash inherits them in turn, except when you redirect the command. Redirection means reassigning one of the file descriptors to another file (or a pipe, or anything permissable). File descriptors may be reassigned locally (for a command, a command group, a subshell, a while or if or case or for loop...), or globally, for the remainder of the shell (using exec).

ls > /dev/null means

running ls with its fd 1 connected to /dev/null.

 

 

 

 

 

bash$ lsof −a −p $$ −d0,1,2

 

 

 

 

COMMAND PID

USER

FD

TYPE DEVICE SIZE NODE NAME

bash

363

bozo

 

 

0u

CHR

136,1

3

/dev/pts/1

bash

363

bozo

 

 

1u

CHR

136,1

3

/dev/pts/1

bash

363

bozo

 

 

2u

CHR

136,1

3

/dev/pts/1

bash$ exec 2> /dev/null

 

 

 

 

 

 

bash$ lsof −a −p $$ −d0,1,2

 

 

 

 

COMMAND PID

USER

FD

TYPE DEVICE SIZE NODE NAME

bash

371

bozo

 

 

0u

CHR

136,1

3

/dev/pts/1

bash

371

bozo

 

 

1u

CHR

136,1

3

/dev/pts/1

bash

371

bozo

 

 

2w

CHR

1,3

120

/dev/null

bash$ bash −c 'lsof −a −p $$ −d0,1,2' | cat

 

COMMAND PID USER

FD

TYPE DEVICE SIZE NODE NAME

 

lsof

379

root

0u

 

CHR

136,1

3 /dev/pts/1

lsof

379

root

1w

 

FIFO

 

0,0

7118 pipe

 

lsof

379

root

2u

 

CHR

136,1

3 /dev/pts/1

bash$ echo "$(bash −c 'lsof −a −p $$ −d0,1,2' 2>&1)"

Appendix D. A Detailed Introduction to I/O and I/O Redirection

342

Advanced Bash−Scripting Guide

COMMAND PID USER

FD

TYPE DEVICE SIZE NODE NAME

lsof

426

root

0u

CHR

136,1

3

/dev/pts/1

lsof

426

root

1w

FIFO

0,0

7520

pipe

lsof

426

root

2w

FIFO

0,0

7520

pipe

 

 

 

 

 

 

 

 

This works for different types of redirection.

Exercise: Analyze the following script.

#! /usr/bin/env bash

mkfifo /tmp/fifo1 /tmp/fifo2

while read a; do echo "FIFO1: $a"; done < /tmp/fifo1 & exec 7> /tmp/fifo1

exec 8> >(while read a; do echo "FD8: $a, to fd7"; done >&7)

exec 3>&1

(

(

(

while read a; do echo "FIFO2: $a"; done < /tmp/fifo2 | tee /dev/stderr | tee /dev/fd/4 | tee / exec 3> /tmp/fifo2

echo 1st, to stdout sleep 1

echo 2nd, to stderr >&2 sleep 1

echo 3rd, to fd 3 >&3 sleep 1

echo 4th, to fd 4 >&4 sleep 1

echo 5th, to fd 5 >&5 sleep 1

echo 6th, through a pipe | sed 's/.*/PIPE: &, to fd 5/' >&5 sleep 1

echo 7th, to fd 6 >&6 sleep 1

echo 8th, to fd 7 >&7 sleep 1

echo 9th, to fd 8 >&8

) 4>&1 >&3 3>&− | while read a; do echo "FD4: $a"; done 1>&3 5>&− 6>&− ) 5>&1 >&3 | while read a; do echo "FD5: $a"; done 1>&3 6>&−

) 6>&1 >&3 | while read a; do echo "FD6: $a"; done 3>&−

rm −f /tmp/fifo1 /tmp/fifo2

# For each command and subshell, figure out which fd points to what.

exit 0

Appendix E. Localization

Localization is an undocumented Bash feature.

Appendix E. Localization

343

Advanced Bash−Scripting Guide

A localized shell script echoes its text output in the language defined as the system's locale. A Linux user in Berlin, Germany, would get script output in German, whereas his cousin in Berlin, Maryland, would get output from the same script in English.

To create a localized script, use the following template to write all messages to the user (error messages, prompts, etc.).

#!/bin/bash

# localized.sh

E_CDERROR=65

error()

{

printf "$@" >&2 exit $E_CDERROR

}

cd $var || error $"Can't cd to %s." "$var" read −p $"Enter the value: " var

# ...

bash$ bash −D localized.sh

"Can't cd to %s." "Enter the value: "

This lists all the localized text. (The −D option lists double−quoted strings prefixed by a $, without executing the script.)

bash$ bash −−dump−po−strings localized.sh

#: a:6

msgid "Can't cd to %s." msgstr ""

#: a:7

msgid "Enter the value: " msgstr ""

The −−dump−po−strings option to Bash resembles the −D option, but uses gettext "po" format.

Now, build a language.po file for each language that the script will be translated into, specifying the msgstr. As an example:

fr.po:

#: a:6

msgid "Can't cd to %s."

msgstr "Impossible de se positionner dans le répertoire %s." #: a:7

msgid "Enter the value: " msgstr "Entrez la valeur : "

Then, run msgfmt.

msgfmt −o localized.sh.mo fr.po

Place the resulting localized.sh.mo file in the /usr/local/share/locale/fr/LC_MESSAGES directory, and at the beginning of the script, insert

Appendix E. Localization

344