一个 包装器 是一个 shell 脚本,它嵌入一个系统命令或实用程序,接受并将一组参数传递给该命令。 [1] 将脚本包装在复杂的命令行周围简化了调用它。 这对于 sed 和 awk 尤其有用。
一个 sed 或 awk 脚本通常通过以下方式从命令行调用:sed -e'commands'或awk'commands'。 将这样的脚本嵌入到 Bash 脚本中可以更简单地调用它,并使其 可重用。 这也使得能够结合 sed 和 awk 的功能,例如将一组 sed 命令的输出 管道传输 到 awk。 作为已保存的可执行文件,您可以重复地以其原始形式或修改后的形式调用它,而无需在命令行中重新键入它的不便。
示例 36-1. shell 包装器
#!/bin/bash # This simple script removes blank lines from a file. # No argument checking. # # You might wish to add something like: # # E_NOARGS=85 # if [ -z "$1" ] # then # echo "Usage: `basename $0` target-file" # exit $E_NOARGS # fi sed -e /^$/d "$1" # Same as # sed -e '/^$/d' filename # invoked from the command-line. # The '-e' means an "editing" command follows (optional here). # '^' indicates the beginning of line, '$' the end. # This matches 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. # Note that this script doesn't actually change the target file. # If you need to do that, redirect its output. exit |
示例 36-2. 一个稍微复杂的 shell 包装器
#!/bin/bash
# subst.sh: a script that substitutes one pattern for
#+ another in a file,
#+ i.e., "sh subst.sh Smith Jones letter.txt".
# Jones replaces Smith.
ARGS=3 # Script requires 3 arguments.
E_BADARGS=85 # Wrong number of arguments passed to script.
if [ $# -ne "$ARGS" ]
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 'sed' docs for an in-depth explanation.
exit $? # Redirect the output of this script to write to a file. |
示例 36-3. 一个通用的 shell 包装器,它写入日志文件
#!/bin/bash # logging-wrapper.sh # Generic shell wrapper that performs an operation #+ and logs it. DEFAULT_LOGFILE=logfile.txt # Set the following two variables. OPERATION= # Can be a complex chain of commands, #+ for example an awk script or a pipe . . . LOGFILE= if [ -z "$LOGFILE" ] then # If not set, default to ... LOGFILE="$DEFAULT_LOGFILE" fi # Command-line arguments, if any, for the operation. OPTIONS="$@" # Log it. echo "`date` + `whoami` + $OPERATION "$@"" >> $LOGFILE # Now, do it. exec $OPERATION "$@" # It's necessary to do the logging before the operation. # Why? |
示例 36-4. 一个围绕 awk 脚本的 shell 包装器
#!/bin/bash
# pr-ascii.sh: Prints a table of ASCII characters.
START=33 # Range of printable ASCII characters (decimal).
END=127 # Will not work for unprintable characters (> 127).
echo " Decimal Hex Character" # Header.
echo " ------- --- ---------"
for ((i=START; i<=END; i++))
do
echo $i | awk '{printf(" %3d %2x %c\n", $1, $1, $1)}'
# The Bash printf builtin will not work in this context:
# printf "%c" "$i"
done
exit 0
# Decimal Hex Character
# ------- --- ---------
# 33 21 !
# 34 22 "
# 35 23 #
# 36 24 $
#
# . . .
#
# 122 7a z
# 123 7b {
# 124 7c |
# 125 7d }
# Redirect the output of this script to a file
#+ or pipe it to "more": sh pr-asc.sh | more |
示例 36-5. 一个围绕另一个 awk 脚本的 shell 包装器
#!/bin/bash
# Adds up a specified column (of numbers) in the target file.
# Floating-point (decimal) numbers okay, because awk can handle them.
ARGS=2
E_WRONGARGS=85
if [ $# -ne "$ARGS" ] # Check for proper number of command-line args.
then
echo "Usage: `basename $0` filename column-number"
exit $E_WRONGARGS
fi
filename=$1
column_number=$2
# Passing shell variables to the awk part of the script is a bit tricky.
# One method is to strong-quote the Bash-script variable
#+ within the awk script.
# $'$BASH_SCRIPT_VAR'
# ^ ^
# This is done in the embedded awk script below.
# See the awk documentation for more details.
# A multi-line awk script is here 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 |
对于那些需要一个多功能工具(瑞士军刀)的脚本来说,有 Perl。 Perl 结合了 sed 和 awk 的功能,并加入了 C 的一个大型子集。 它是模块化的,并包含从面向对象编程到应有尽有的一切支持。 短小的 Perl 脚本适合嵌入到 shell 脚本中,并且 Perl 可以完全取代 shell 脚本的说法可能有一些道理(尽管《ABS 指南》的作者仍然持怀疑态度)。
示例 36-6. 嵌入在 Bash 脚本中的 Perl
#!/bin/bash # Shell commands may precede the Perl script. echo "This precedes the embedded Perl script within \"$0\"." echo "===============================================================" perl -e 'print "This line prints from 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 |
甚至可以将 Bash 脚本和 Perl 脚本组合在同一个文件中。 根据脚本的调用方式,Bash 部分或 Perl 部分将执行。
示例 36-7. Bash 和 Perl 脚本的组合
#!/bin/bash # bashandperl.sh echo "Greetings from the Bash part of the script, $0." # More Bash commands may follow here. exit # End of Bash part of the script. # ======================================================= #!/usr/bin/perl # This part of the script must be invoked with # perl -x bashandperl.sh print "Greetings from the Perl part of the script, $0.\n"; # Perl doesn't seem to like "echo" ... # More Perl commands may follow here. # End of Perl part of the script. |
bash$ bash bashandperl.sh Greetings from the Bash part of the script. bash$ perl -x bashandperl.sh Greetings from the Perl part of the script. |
当然,也可以在 shell 包装器中嵌入更具异国情调的脚本语言。 例如,Python ...
示例 36-8. 嵌入在 Bash 脚本中的 Python
#!/bin/bash # ex56py.sh # Shell commands may precede the Python script. echo "This precedes the embedded Python script within \"$0.\"" echo "===============================================================" python -c 'print "This line prints from an embedded Python script.\n";' # Unlike sed and perl, Python uses the "-c" option. python -c 'k = raw_input( "Hit a key to exit to outer script. " )' echo "===============================================================" echo "However, the script may also contain shell and system commands." exit 0 |
将脚本包装在 mplayer 和 Google 的翻译服务器周围,您可以创建可以与您对话的东西。
示例 36-9. 一个会说话的脚本
#!/bin/bash
# Courtesy of:
# http://elinux.org/RPi_Text_to_Speech_(Speech_Synthesis)
# You must be on-line for this script to work,
#+ so you can access the Google translation server.
# Of course, mplayer must be present on your computer.
speak()
{
local IFS=+
# Invoke mplayer, then connect to Google translation server.
/usr/bin/mplayer -ao alsa -really-quiet -noconsolecontrols \
"http://translate.google.com/translate_tts?tl=en&q="$*""
# Google translates, but can also speak.
}
LINES=4
spk=$(tail -$LINES $0) # Tail end of same script!
speak "$spk"
exit
# Browns. Nice talking to you. |
一个关于复杂 shell 包装器的有趣例子是 Martin Matusiak 的 undvd 脚本,它为复杂的 mencoder 实用程序提供了一个易于使用的命令行界面。 另一个例子是 Itzchak Rehberg 的 Ext3Undel,这是一组用于恢复 ext3 文件系统上已删除文件的脚本。
| [1] | 实际上,相当多的 Linux 实用程序是 shell 包装器。 一些例子是:/usr/bin/pdf2ps, /usr/bin/batch,以及/usr/bin/xmkmf. |