一个 包装器 是一个 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. |