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

Chapter 34. Miscellany

Nobody really knows what the Bourne shell's grammar is. Even examination of the source code is little help.

Tom Duff

34.1. Interactive and non−interactive shells and scripts

An interactive shell reads commands from user input on a tty. Among other things, such a shell reads startup files on activation, displays a prompt, and enables job control by default. The user can interact with the shell.

A shell running a script is always a non−interactive shell. All the same, the script can still access its tty. It is even possible to emulate an interactive shell in a script.

#!/bin/bash MY_PROMPT='$ ' while :

do

echo −n "$MY_PROMPT" read line

eval "$line" done

exit 0

#This example script, and much of the above explanation supplied by

#Stephane Chazelas (thanks again).

Let us consider an interactive script to be one that requires input from the user, usually with read statements (see Example 11−2). "Real life" is actually a bit messier than that. For now, assume an interactive script is bound to a tty, a script that a user has invoked from the console or an xterm.

Init and startup scripts are necessarily non−interactive, since they must run without human intervention.

Many administrative and system maintenance scripts are likewise non−interactive. Unvarying repetitive tasks cry out for automation by non−interactive scripts.

Non−interactive scripts can run in the background, but interactive ones hang, waiting for input that never comes. Handle that difficulty by having an expect script or embedded here document feed input to an interactive script running as a background job. In the simplest case, redirect a file to supply input to a read statement (read variable <file). These particular workarounds make possible general purpose scripts that run in either interactive or non−interactive modes.

If a script needs to test whether it is running in an interactive shell, it is simply a matter of finding whether the prompt variable, $PS1 is set. (If the user is being prompted for input, then the script needs to display a prompt.)

if [ −z $PS1 ] # no prompt? then

Chapter 34. Miscellany

299

Advanced Bash−Scripting Guide

# non−interactive

...

else

# interactive

...

fi

Alternatively, the script can test for the presence of option "i" in the $− flag. case $− in

*i*)

#

interactive shell

;;

 

 

*)

#

non−interactive shell

;;

# (Thanks to "UNIX F.A.Q.", 1993)

Scripts may be forced to run in interactive mode with the −i option or with a #!/bin/bash −i header. Be aware that this can cause erratic script behavior or show error messages even when no error is present.

34.2. Shell Wrappers

A "wrapper" is a shell script that embeds a system command or utility, that saves a set of parameters passed to to that command. Wrapping a script around a complex command line simplifies invoking it. This is expecially useful with sed and awk.

A sed or awk script would normally be invoked from the command line by a sed −e 'commands' or awk 'commands'. Embedding such a script in a Bash script permits calling it more simply, and makes it "reusable". This also enables combining the functionality of sed and awk, for example piping the output of a set of sed commands to awk. As a saved executable file, you can then repeatedly invoke it in its original form or modified, without the inconvenience of retyping it on the command line.

Example 34−1. shell wrapper

#!/bin/bash

#This is a simple script that removes blank lines from a file.

#No argument checking.

#Same as

#sed −e '/^$/d' filename

#invoked from the command line.

sed −e /^$/d "$1"

#The '−e' means an "editing" command follows (optional here).

#'^' is the beginning of line, '$' is the end.

#This match lines with nothing between the beginning and the end,

#blank lines.

#The 'd' is the delete command.

#Quoting the command−line arg permits

#whitespace and special characters in the filename.

34.2. Shell Wrappers

300

Advanced Bash−Scripting Guide

exit 0

Example 34−2. A slightly more complex shell wrapper

#!/bin/bash

#"subst", a script that substitutes one pattern for

#another in a file,

#i.e., "subst Smith Jones letter.txt".

ARGS=3

E_BADARGS=65 # Wrong number of arguments passed to script.

if [ $# −ne "$ARGS" ]

# Test number of arguments to script (always a good idea). then

echo "Usage: `basename $0` old−pattern new−pattern filename" exit $E_BADARGS

fi

old_pattern=$1 new_pattern=$2

if [ −f "$3" ] then

file_name=$3

else

echo "File \"$3\" does not exist." exit $E_BADARGS

fi

# Here is where the heavy work gets done.

sed −e "s/$old_pattern/$new_pattern/g" $file_name

#'s' is, of course, the substitute command in sed,

#and /pattern/ invokes address matching.

#The "g", or global flag causes substitution for *every*

#occurence of $old_pattern on each line, not just the first.

#Read the literature on 'sed' for a more in−depth explanation.

exit 0 # Successful invocation of the script returns 0.

Example 34−3. A shell wrapper around an awk script

#!/bin/bash

# Adds up a specified column (of numbers) in the target file.

ARGS=2

E_WRONGARGS=65

if [ $# −ne "$ARGS" ] # Check for proper no. of command line args. then

echo "Usage: `basename $0` filename column−number" exit $E_WRONGARGS

fi

filename=$1 column_number=$2

34.2. Shell Wrappers

301

Advanced Bash−Scripting Guide

#Passing shell variables to the awk part of the script is a bit tricky.

#See the awk documentation for more details.

# A multi−line awk script is invoked by awk ' ..... '

#Begin awk script.

#−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

awk '

{ total += $'"${column_number}"'

}

END {

print total

}

' "$filename"

#−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

#End awk script.

#It may not be safe to pass shell variables to an embedded awk script,

#so Stephane Chazelas proposes the following alternative:

#−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

#awk −v column_number="$column_number" '

#{ total += $column_number

#}

#END {

#print total

#}' "$filename"

#−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

exit 0

For those scripts needing a single do−it−all tool, a Swiss army knife, there is Perl. Perl combines the capabilities of sed and awk, and throws in a large subset of C, to boot. It is modular and contains support for everything ranging from object−oriented programming up to and including the kitchen sink. Short Perl scripts lend themselves to embedding in shell scripts, and there may even be some substance to the claim that Perl can totally replace shell scripting (though the author of this document remains skeptical).

Example 34−4. Perl embedded in a Bash script

#!/bin/bash

# Shell commands may precede the Perl script.

echo "This precedes the embedded Perl script within \"$0\"."

echo "==============================================================="

perl −e 'print "This is an embedded Perl script.\n";'

# Like sed, Perl also uses the "−e" option.

echo "===============================================================" echo "However, the script may also contain shell and system commands."

exit 0

34.2. Shell Wrappers

302