330pics-shell scripts_third

***@@@ex36.sh@@@!!!************************************************************************************
#!/bin/bash
# "Reading" 变量.

echo -n "Enter the value of variable 'var1': "
# -n 选项, 阻止换行.

read var1
# 注意: 在var1前面没有'$', 因为变量正在被设置.

echo "var1 = $var1"


echo

# 一个单独的'read'语句可以设置多个变量.
echo -n "Enter the values of variables 'var2' and 'var3' (separated by a space or tab): "
read var2 var3
echo "var2 = $var2      var3 = $var3"
# 如果你只输入了一个值, 那么其他的变量还是处于未设置状态(null).

exit 0
%%%&&&ex36.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex37.sh@@@!!!************************************************************************************
#!/bin/bash

dir1=/usr/local
dir2=/var/spool

pushd $dir1
# 将自动运行一个 'dirs' (把目录栈的内容列到stdout上).
echo "Now in directory `pwd`." # 使用后置引用的 'pwd'.

# 现在对'dir1'做一些操作.
pushd $dir2
echo "Now in directory `pwd`."

# 现在对'dir2'做一些操作.
echo "The top entry in the DIRSTACK array is $DIRSTACK."
popd
echo "Now back in directory `pwd`."

# 现在, 对'dir1'做更多的操作.
popd
echo "Now back in original working directory `pwd`."

exit 0

# 如果你不使用 'popd' 将会发生什么 -- 然后退出这个脚本?
# 你最后将落在哪个目录中? 为什么?
%%%&&&ex37.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex38.sh@@@!!!************************************************************************************
#!/bin/bash

. data-file    # 加载一个数据文件.
# 与"source data-file"效果相同, 但是更具可移植性.

#  文件"data-file"必须存在于当前工作目录,
#+ 因为这个文件是使用'basename'来引用的.

# 现在, 引用这个文件中的一些数据.

echo "variable1 (from data-file) = $variable1"
echo "variable3 (from data-file) = $variable3"

let "sum = $variable2 + $variable4"
echo "Sum of variable2 + variable4 (from data-file) = $sum"
echo "message1 (from data-file) is /"$message1/""
# 注意:                             将双引号转义

print_message This is the message-print function in the data-file.


exit 0
%%%&&&ex38.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex39.sh@@@!!!************************************************************************************
#!/bin/bash

ROOT_UID=0   # 只有$UID为0的用户才拥有root权限.
E_NOTROOT=65
E_NOPARAMS=66

if [ "$UID" -ne "$ROOT_UID" ]
then
  echo "Must be root to run this script."
  # "Run along kid, it's past your bedtime."
  exit $E_NOTROOT
fi 

if [ -z "$1" ]
then
  echo "Usage: `basename $0` find-string"
  exit $E_NOPARAMS
fi


echo "Updating 'locate' database..."
echo "This may take a while."
updatedb /usr &     # 必须使用root身份来运行.

wait
# 将不会继续向下运行, 除非'updatedb'命令执行完成.
# 你希望在查找文件名之前更新database.

locate $1

#  如果没有'wait'命令的话, 而且在比较糟的情况下,
#+ 脚本可能在'updatedb'命令还在运行的时候退出,
#+ 这将会导致'updatedb'成为一个孤儿进程.

exit 0
%%%&&&ex39.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex3.sh@@@!!!************************************************************************************
#!/bin/bash

# 这个简单的脚本可以把文件中所有的空行删除.
# 没做参数检查.
#
# 你或许想添加如下代码:
#
# E_NOARGS=65
# if [ -z "$1" ]
# then
#  echo "Usage: `basename $0` target-file"
#  exit $E_NOARGS
# fi


# 这个脚本调用起来的效果,
# 等价于从命令行上调用:
# sed -e '/^$/d' filename.

sed -e /^$/d "$1"
#  '-e'意味着后边跟的是"编辑"命令. (在这里是可选的).
#  '^'匹配行首, '$'匹配行尾.
#  这条语句用来匹配行首与行尾之间什么都没有的行,
#+ 即空白行.
#  'd'为删除命令.

#  将命令行参数引用起来,
#+ 就意味着可以在文件名中使用空白字符或者特殊字符.

#  注意, 这个脚本其实并不能真正的修改目标文件.
#  如果你想保存修改, 可以将它的输出重定向.

exit 0
%%%&&&ex3.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex40.sh@@@!!!************************************************************************************
#!/bin/bash
# ex40.sh (burn-cd.sh)
# 自动刻录CDR的脚本.


SPEED=2          # 如果你的硬件支持的话, 你可以选用更高的速度.
IMAGEFILE=cdimage.iso
CONTENTSFILE=contents
DEVICE=cdrom
# DEVICE="0,0"     为了是用老版本的CDR
DEFAULTDIR=/opt  # 这是包含需要被刻录内容的目录.
                 # 必须保证目录存在.
                 # 小练习: 测试一下目录是否存在.

# 使用 Joerg Schilling 的 "cdrecord" 包:
# http://www.fokus.fhg.de/usr/schilling/cdrecord.html

#  如果一般用户调用这个脚本的话, 可能需要root身份
#+ chmod u+s /usr/bin/cdrecord
#  当然, 这会产生安全漏洞, 虽然这是一个比较小的安全漏洞.

if [ -z "$1" ]
then
  IMAGE_DIRECTORY=$DEFAULTDIR
  # 如果命令行没指定的话, 那么这个就是默认目录.
else
    IMAGE_DIRECTORY=$1
fi

# 创建一个"内容列表"文件.
ls -lRF $IMAGE_DIRECTORY > $IMAGE_DIRECTORY/$CONTENTSFILE
# "l" 选项将给出一个"长"文件列表.
# "R" 选项将使这个列表递归.
# "F" 选项将标记出文件类型 (比如: 目录是以 /结尾, 而可执行文件以 *结尾).
echo "Creating table of contents."

# 在烧录到CDR之前创建一个镜像文件.
mkisofs -r -o $IMAGEFILE $IMAGE_DIRECTORY
echo "Creating ISO9660 file system image ($IMAGEFILE)."

# 烧录CDR.
echo "Burning the disk."
echo "Please be patient, this will take a while."
cdrecord -v -isosize speed=$SPEED dev=$DEVICE $IMAGEFILE

exit $?
%%%&&&ex40.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex41.sh@@@!!!************************************************************************************
#!/bin/bash

# 从/var/log/messagesGenerates的尾部开始
# 产生当前目录下的一个lof文件.

# 注意: 如果这个脚本被一个一般用户调用的话,
# /var/log/messages 必须是全部可读的.
#         #root chmod 644 /var/log/messages

LINES=5

( date; uname -a ) >>logfile
# 时间和机器名
echo --------------------------------------------------------------------- >>logfile
tail -$LINES /var/log/messages | xargs |  fmt -s >>logfile
echo >>logfile
echo >>logfile

exit 0

#  注意:
#  -----
#  像 Frank Wang 所指出,
#+ 在原文件中的任何不匹配的引号(包括单引号和双引号)
#+ 都会给xargs造成麻烦.
#                                                                            
#  他建议使用下边的这行来替换上边的第15行:
#     tail -$LINES /var/log/messages | tr -d "/"'" | xargs | fmt -s >>logfile

 

#  练习:
#  -----
#  修改这个脚本, 使得这个脚本每个20分钟
#+ 就跟踪一下 /var/log/messages 的修改记录.
#  提示: 使用 "watch" 命令.
%%%&&&ex41.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex42.sh@@@!!!************************************************************************************
#!/bin/bash
# copydir.sh

#  将当前目录下($PWD)的所有文件都拷贝到
#+ 命令行所指定的另一个目录中去.

E_NOARGS=65

if [ -z "$1" ]   # 如果没有参数传递进来那就退出.
then
  echo "Usage: `basename $0` directory-to-copy-to"
  exit $E_NOARGS
fi 

ls . | xargs -i -t cp ./{} $1
#            ^^ ^^      ^^
#  -t 是 "verbose" (输出命令行到stderr) 选项.
#  -i 是"替换字符串"选项.
#  {} 是输出文本的替换点.
#  这与在"find"命令中使用{}的情况很相像.
#                                                      
#  列出当前目录下的所有文件(ls .),
#+ 将 "ls" 的输出作为参数传递到 "xargs"(-i -t 选项) 中,
#+ 然后拷贝(cp)这些参数({})到一个新目录中($1).
#                                                      
#  最终的结果和下边的命令等价,
#+   cp * $1
#+ 除非有文件名中嵌入了"空白"字符.

exit 0
%%%&&&ex42.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex43.sh@@@!!!************************************************************************************
#!/bin/bash

y=`eval ls -l`  #  与 y=`ls -l` 很相似
echo $y         #+ 但是换行符将会被删除, 因为"echo"的变量未被""引用.
echo
echo "$y"       #  用""将变量引用起来, 换行符就不会被空格替换了.

echo; echo

y=`eval df`     #  与 y=`df` 很相似
echo $y         #+ 换行符又被空格替换了.

#  当没有LF(换行符)出现时, 如果使用"awk"这样的工具来分析输出的结果,
#+ 应该能更容易一些.

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

# 现在,来看一下怎么用"eval"命令来"扩展"一个变量 . . .

for i in 1 2 3 4 5; do
  eval value=$i
  #  value=$i 具有相同的效果, 在这里并不是非要使用"eval"不可.
  #  一个缺乏特殊含义的变量将被评价为自身 -- 也就是说,
  #+ 这个变量除了能够被扩展成自身所表示的字符外, 不能被扩展成任何其他的含义.
  echo $value
done

echo
echo "---"
echo

for i in ls df; do
  value=eval $i
  #  value=$i 在这里就与上边这句有了本质上的区别.
  #  "eval" 将会评价命令 "ls" 和 "df" . . .
  #  术语 "ls" 和 "df" 就具有特殊含义,
  #+ 因为它们被解释成命令,
  #+ 而不是字符串本身.
  echo $value
done


exit 0
%%%&&&ex43.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex44.sh@@@!!!************************************************************************************
#!/bin/bash
# 结束ppp进程来强制登出log-off.

# 本脚本应该以root用户的身份来运行.

killppp="eval kill -9 `ps ax | awk '/ppp/ { print $1 }'`"
#                     -------- ppp的进程ID ------- 

$killppp                  # 这个变量现在成为了一个命令.


# 下边的命令必须以root用户的身份来运行.

chmod 666 /dev/ttyS3      # 恢复读写权限,否则什么?
#  因为在ppp上执行一个SIGKILL将会修改串口的权限,
#+ 我们把权限恢复到之前的状态.

rm /var/lock/LCK..ttyS3   # 删除串口琐文件.为什么?

exit 0

# 练习:
# -----
# 1) 编写一个脚本来验证是否root用户正在运行它.
# 2) 做一个检查, 在杀掉某个进程之前,
#+   检查一下这个将要被杀掉的进程是否正在运行.
# 3) 基于'fuser'来编写达到这个目的的另一个版本的脚本
#+      if [ fuser -s /dev/modem ]; then . . .
%%%&&&ex44.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex45a.sh@@@!!!************************************************************************************
#!/bin/bash

echo
echo "String operations using /"expr /$string : /" construct"
echo "==================================================="
echo

a=1234zipper5FLIPPER43231

echo "The string being operated upon is /"`expr "$a" : '/(.*/)'`/"."
#     转义括号对的操作.                  ==  ==

#       ***************************
#+    转义括号对
#+           用来匹配一个子串
#       ***************************


#  如果不转义括号的话...
#+ 那么'expr'将把string操作转换为一个整数.

echo "Length of /"$a/" is `expr "$a" : '.*'`."   # 字符串长度

echo "Number of digits at the beginning of /"$a/" is `expr "$a" : '[0-9]*'`."

# ------------------------------------------------------------------------- #

echo

echo "The digits at the beginning of /"$a/" are `expr "$a" : '/([0-9]*/)'`."
#                                                             ==      ==
echo "The first 7 characters of /"$a/" are `expr "$a" : '/(......./)'`."
#         =====                                          ==       ==
# 再来一个, 转义括号对强制一个子串匹配.
#
echo "The last 7 characters of /"$a/" are `expr "$a" : '.*/(......./)'`."
#         ====                  字符串操作的结尾  ^^
#  (最后这个模式的意思是忽略前边的任何字符,直到最后7个字符,
#+  最后7个点就是需要匹配的任意7个字符的字串)

echo

exit 0
%%%&&&ex45a.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex45.sh@@@!!!************************************************************************************
#!/bin/bash

# 展示一些使用'expr'的例子
# ========================

echo

# 算术 操作
# ---- ----

echo "Arithmetic Operators"
echo
a=`expr 5 + 3`
echo "5 + 3 = $a"

a=`expr $a + 1`
echo
echo "a + 1 = $a"
echo "(incrementing a variable)"

a=`expr 5 % 3`
# 取模操作
echo
echo "5 mod 3 = $a"

echo
echo

# 逻辑 操作
# ---- ----

#  true返回1, false返回0,
#+ 而Bash的使用惯例则相反.

echo "Logical Operators"
echo

x=24
y=25
b=`expr $x = $y`         # 测试相等.
echo "b = $b"            # 0  ( $x -ne $y )
echo

a=3
b=`expr $a /> 10`
echo 'b=`expr $a /> 10`, therefore...'
echo "If a > 10, b = 0 (false)"
echo "b = $b"            # 0  ( 3 ! -gt 10 )
echo

b=`expr $a /< 10`
echo "If a < 10, b = 1 (true)"
echo "b = $b"            # 1  ( 3 -lt 10 )
echo
# 注意转义操作.

b=`expr $a /<= 3`
echo "If a <= 3, b = 1 (true)"
echo "b = $b"            # 1  ( 3 -le 3 )
# 也有 "/>=" 操作 (大于等于).


echo
echo

 

# 字符串 操作
# ------ ----

echo "String Operators"
echo

a=1234zipper43231
echo "The string being operated upon is /"$a/"."

# 长度: 字符串长度
b=`expr length $a`
echo "Length of /"$a/" is $b."

# 索引: 从字符串的开头查找匹配的子串,
#       并取得第一个匹配子串的位置.
b=`expr index $a 23`
echo "Numerical position of first /"2/" in /"$a/" is /"$b/"."

# substr: 从指定位置提取指定长度的字串.
b=`expr substr $a 2 6`
echo "Substring of /"$a/", starting at position 2,/
and 6 chars long is /"$b/"."


#  'match' 操作的默认行为就是从字符串的开始进行搜索,
#+  并匹配第一个匹配的字符串.
#
#        使用正则表达式
b=`expr match "$a" '[0-9]*'`               #  数字的个数.
echo Number of digits at the beginning of /"$a/" is $b.
b=`expr match "$a" '/([0-9]*/)'`           #  注意, 需要转义括号
#                   ==      ==              + 这样才能触发子串的匹配.
echo "The digits at the beginning of /"$a/" are /"$b/"."

echo

exit 0
%%%&&&ex45.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex46.sh@@@!!!************************************************************************************
#!/bin/bash

echo

let a=11            # 与 'a=11' 相同
let a=a+5           # 等价于 let "a = a + 5"
                    # (双引号和空格是这句话更具可读性.)
echo "11 + 5 = $a"  # 16

let "a <<= 3"       # 等价于 let "a = a << 3"
echo "/"/$a/" (=16) left-shifted 3 places = $a"
                    # 128

let "a /= 4"        # 等价于 let "a = a / 4"
echo "128 / 4 = $a" # 32

let "a -= 5"        # 等价于 let "a = a - 5"
echo "32 - 5 = $a"  # 27

let "a *=  10"      # 等价于 let "a = a * 10"
echo "27 * 10 = $a" # 270

let "a %= 8"        # 等价于 let "a = a % 8"
echo "270 modulo 8 = $a  (270 / 8 = 33, remainder $a)"
                    # 6

echo

exit 0
%%%&&&ex46.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex47.sh@@@!!!************************************************************************************
#!/bin/bash
# printf 示例

PI=3.14159265358979
DecimalConstant=31373
Message1="Greetings,"
Message2="Earthling."

echo

printf "Pi to 2 decimal places = %1.2f" $PI
echo
printf "Pi to 9 decimal places = %1.9f" $PI  # 都能够正确的结束.

printf "/n"                                  # 打印一个换行,
                                             # 等价于 'echo' . . .

printf "Constant = /t%d/n" $DecimalConstant  # 插入一个 tab (/t).

printf "%s %s /n" $Message1 $Message2

echo

# ==========================================#
# 模拟C函数, sprintf().
# 使用一个格式化的字符串来加载一个变量.

echo

Pi12=$(printf "%1.12f" $PI)
echo "Pi to 12 decimal places = $Pi12"

Msg=`printf "%s %s /n" $Message1 $Message2`
echo $Msg; echo $Msg

#  像我们所看到的一样, 现在'sprintf'可以
#+ 作为一个可被加载的模块,
#+ 但是不具可移植性.

exit 0
%%%&&&ex47.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex48.sh@@@!!!************************************************************************************
#!/bin/bash

# 使用cpio来拷贝目录树.

# 使用'cpio'的优点:
#   加速拷贝. 比通过管道使用'tar'命令快一些.
#   很适合拷贝一些'cp'命令
#+  搞不定的的特殊文件(比如名字叫pipes的文件, 等等)

ARGS=2
E_BADARGS=65

if [ $# -ne "$ARGS" ]
then
  echo "Usage: `basename $0` source destination"
  exit $E_BADARGS
fi 

source=$1
destination=$2


find "$source" -depth | cpio -admvp "$destination"
#               ^^^^^         ^^^^^
# 阅读'find'和'cpio'的man页来了解这些选项的意义.
                                                        
                                                        
# 练习:
# -----
                                                        
#  添加一些代码来检查'find | cpio'管道命令的退出码($?)
#+ 并且如果出现错误的时候输出合适的错误码.

exit 0
%%%&&&ex48.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex49.sh@@@!!!************************************************************************************
#!/bin/bash
# 把一个文件的内容全部转换为大写.

E_BADARGS=65

if [ -z "$1" ]  # 检查命令行参数.
then
  echo "Usage: `basename $0` filename"
  exit $E_BADARGS
fi 

tr a-z A-Z <"$1"

# 与上边的作用相同, 但是使用了POSIX字符集标记方法:
#        tr '[:lower:]' '[:upper:]' <"$1"
# 感谢, S.C.

exit 0

#  练习:
#  重写这个脚本, 通过选项可以控制脚本或者
#+ 转换为大写或者转换为小写.
%%%&&&ex49.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex4.sh@@@!!!************************************************************************************
#!/bin/bash

#  "替换", 这个脚本的用途:
#+ 将一个文件中的某个字符串(或匹配模式), 替换为另一个字符串(或匹配模式),
#+ 比如, "subst Smith Jones letter.txt".

ARGS=3         # 这个脚本需要3个参数.
E_BADARGS=65   # 传递给脚本的参数个数不对.

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


#  下面是实现功能的代码.

# -----------------------------------------------
sed -e "s/$old_pattern/$new_pattern/g" $file_name
# -----------------------------------------------

#  's'在sed中是替换命令,
#+ /pattern/表示匹配模式.
#  "g", 即全局标志, 用来自动替换掉每行中
#+ 出现的全部$old_pattern模式, 而不仅仅替换掉第一个匹配.
#  如果想深入了解, 可以参考'sed'命令的相关书籍.

exit 0    # 成功调用脚本, 将会返回0.
%%%&&&ex4.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex50.sh@@@!!!************************************************************************************
#!/bin/bash

WIDTH=40                    # 设为40列宽.

b=`ls /usr/local/bin`       # 取得文件列表...

echo $b | fmt -w $WIDTH

# 也可以使用如下方法, 作用是相同的.
#    echo $b | fold - -s -w $WIDTH
 
exit 0
%%%&&&ex50.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex51.sh@@@!!!************************************************************************************
#!/bin/bash
# 练习'date'命令

echo "The number of days since the year's beginning is `date +%j`."
# 需要在调用格式的前边加上一个'+'号.
# %j用来给出今天是本年度的第几天.

echo "The number of seconds elapsed since 01/01/1970 is `date +%s`."
#  %s将产生从"UNIX 元年"到现在为止的秒数,
#+ 但是这东西现在还有用么?

prefix=temp
suffix=$(date +%s)  # 'date'命令的"+%s"选项是GNU特性.
filename=$prefix.$suffix
echo $filename
#  这是一种非常好的产生"唯一"临时文件的办法,
#+ 甚至比使用$$都强.

# 如果想了解'date'命令的更多选项, 请查阅这个命令的man页.

exit 0
%%%&&&ex51.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex52.sh@@@!!!************************************************************************************
#!/bin/bash
# 在当前目录下用Uudecode解码所有用uuencode编码的文件.

lines=35        # 允许读头部的35行(范围很宽).

for File in *   # 测试所有$PWD下的文件.
do
  search1=`head -$lines $File | grep begin | wc -w`
  search2=`tail -$lines $File | grep end | wc -w`
  #  用Uuencode编码过的文件在文件开始的地方都有个"begin",
  #+ 在文件结尾的地方都有"end".
  if [ "$search1" -gt 0 ]
  then
    if [ "$search2" -gt 0 ]
    then
      echo "uudecoding - $File -"
      uudecode $File
    fi 
  fi
done 

#  小心不要让这个脚本运行自己,
#+ 因为它也会把自身也认为是一个经过uuencode编码过的文件,
#+ 这都是因为这个脚本自身也包含"begin"和"end".

#  练习:
#  -----
#  修改这个脚本, 让它可以检查一个新闻组的每个文件,
#+ 并且如果下一个没找到的话就跳过.

exit 0
%%%&&&ex52.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex53.sh@@@!!!************************************************************************************
#!/bin/bash
# 使用"seq"

echo

for a in `seq 80`  # or   for a in $( seq 80 )
# 与 for a in 1 2 3 4 5 ... 80   相同(少敲了好多字!).
# 也可以使用'jot'(如果系统上有的话).
do
  echo -n "$a "
done      # 1 2 3 4 5 ... 80
# 这也是一个通过使用命令输出
# 来产生"for"循环中[list]列表的例子.

echo; echo


COUNT=80  # 当然, 'seq'也可以使用一个可替换的参数.

for a in `seq $COUNT`  # 或者   for a in $( seq $COUNT )
do
  echo -n "$a "
done      # 1 2 3 4 5 ... 80

echo; echo

BEGIN=75
END=80

for a in `seq $BEGIN $END`
#  传给"seq"两个参数, 从第一个参数开始增长,
#+ 一直增长到第二个参数为止.
do
  echo -n "$a "
done      # 75 76 77 78 79 80

echo; echo

BEGIN=45
INTERVAL=5
END=80

for a in `seq $BEGIN $INTERVAL $END`
#  传给"seq"三个参数, 从第一个参数开始增长,
#+ 并以第二个参数作为增量,
#+ 一直增长到第三个参数为止.
do
  echo -n "$a "
done      # 45 50 55 60 65 70 75 80

echo; echo

exit 0
%%%&&&ex53.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex54.sh@@@!!!************************************************************************************
#!/bin/bash

exec echo "Exiting /"$0/"."   # 脚本应该在这里退出.

# ----------------------------------
# The following lines never execute.

echo "This echo will never echo."

exit 99                       #  脚本是不会在这里退出的.
                              #  脚本退出后会使用'echo $?'
                              #+ 来检查一下退出码.
                              #  一定 *不是* 99.
%%%&&&ex54.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex55.sh@@@!!!************************************************************************************
#!/bin/sh

# -->  本书作者所做的注释全部以"# -->"开头.

# --> 这是由Miquel van Smoorenburg所编写的
# --> 'rc'脚本包的一部分, &lt;miquels@drinkel.nl.mugnet.org>.

# --> 这个特殊的脚本看起来是Red Hat/FC专用的,
# --> (在其它的发行版中可能不会出现).

#  停止所有正在运行的不必要的服务
#+ (不会有多少, 所以这是个合理性检查)

for i in /var/lock/subsys/*; do
        # --> 标准的for/in循环, 但是由于"do"在同一行上,
        # --> 所以必须添加";".
        # 检查脚本是否在那里.
        [ ! -f $i ] && continue
        # --> 这是一种使用"与列表"的聪明方法, 等价于:
        # --> if [ ! -f "$i" ]; then continue

        # 取得子系统的名字.
        subsys=${i#/var/lock/subsys/}
        # --> 匹配变量名, 在这里就是文件名.
        # --> 与subsys=`basename $i`完全等价.
 
        # -->  从锁定文件名中获得
        # -->+ (如果那里有锁定文件的话,
        # -->+ 那就证明进程正在运行).
        # -->  参考一下上边所讲的"锁定文件"的内容.


        # 终止子系统.
        if [ -f /etc/rc.d/init.d/$subsys.init ]; then
           /etc/rc.d/init.d/$subsys.init stop
        else
           /etc/rc.d/init.d/$subsys stop
        # -->  挂起运行的作业和幽灵进程.
        # -->  注意"stop"只是一个位置参数,
        # -->+ 并不是shell内建命令.
        fi
done
%%%&&&ex55.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex56.sh@@@!!!************************************************************************************
#!/bin/bash

# Shell命令可以放到Perl脚本的前面.
echo "This precedes the embedded Perl script within /"$0/"."
echo "==============================================================="

perl -e 'print "This is an embedded Perl script./n";'
# 类似于sed, Perl也可以使用"-e"选项.

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

exit 0
%%%&&&ex56.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex57.sh@@@!!!************************************************************************************
#!/bin/bash
# badname.sh
# 删除当前目录下文件名中包含一些特殊字符的文件.(这些特殊字符指的是不应该出现在文件名中的字符)

for filename in *
do
  badname=`echo "$filename" | sed -n /[/+/{/;/"///=/?~/(/)/</>/&/*/|/$]/p`
# badname=`echo "$filename" | sed -n '/[+{;"/=?~()&lt;&gt;&*|$]/p'`  这句也行.
# 删除文件名包含这些字符的文件:     + { ; " / = ? ~ ( ) < > & * | $
#
  rm $badname 2>/dev/null
#             ^^^^^^^^^^^ 错误消息将被抛弃.
done

# 现在, 处理文件名中以任何方式包含空白的文件.
find . -name "* *" -exec rm -f {} /;
# "find"命令匹配到的目录名将替换到"{}"的位置.
# '/'是为了保证';'被正确的转义, 并且放到命令的结尾.

exit 0

#---------------------------------------------------------------------
# 这行下边的命令将不会运行, 因为有 "exit" 命令.

# 下边这句可以用来替换上边的脚本:
find . -name '*[+{;"//=?~()&lt;&gt;&*|$ ]*' -exec rm -f '{}' /;
# (感谢, S.C.)
%%%&&&ex57.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex58.sh@@@!!!************************************************************************************
#!/bin/bash

#  在一个"tarball"中(经过tar和gzip处理过的文件)
#+ 备份最后24小时当前目录下d所有修改的文件.

BACKUPFILE=backup-$(date +%m-%d-%Y)
#                 在备份文件中嵌入时间.
#                 Thanks, Joshua Tschida, for the idea.
archive=${1:-$BACKUPFILE}
#  如果在命令行中没有指定备份文件的文件名,
#+ 那么将默认使用"backup-MM-DD-YYYY.tar.gz".

tar cvf - `find . -mtime -1 -type f -print` > $archive.tar
gzip $archive.tar
echo "Directory $PWD backed up in archive file /"$archive.tar.gz/"."


#  Stephane Chazelas指出上边代码,
#+ 如果在发现太多的文件的时候, 或者是如果文件
#+ 名包括空格的时候, 将执行失败.

# Stephane Chazelas建议使用下边的两种代码之一:
# -------------------------------------------------------------------
#   find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar"
#      使用gnu版本的"find".


#   find . -mtime -1 -type f -exec tar rvf "$archive.tar" '{}' /;
#         对于其他风格的UNIX便于移植, 但是比较慢.
# -------------------------------------------------------------------


exit 0
%%%&&&ex58.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex59.sh@@@!!!************************************************************************************
#!/bin/bash

JUST_A_SECOND=1

funky ()
{ # 这是一个最简单的函数.
  echo "This is a funky function."
  echo "Now exiting funky function."
} # 函数必须在调用前声明.


fun ()
{ # 一个稍微复杂一些的函数.
  i=0
  REPEATS=30

  echo
  echo "And now the fun really begins."
  echo

  sleep $JUST_A_SECOND    # 嘿, 暂停一秒!
  while [ $i -lt $REPEATS ]
  do
    echo "----------FUNCTIONS---------->"
    echo "<------------ARE-------------"
    echo "<------------FUN------------>"
    echo
    let "i+=1"
  done
}

  # 现在, 调用这两个函数.

funky
fun

exit 0
%%%&&&ex59.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex5.sh@@@!!!************************************************************************************
#!/bin/bash

echo hello
echo $?    # 退出状态为0, 因为命令执行成功.

lskdf      # 无效命令.
echo $?    # 非零的退出状态, 因为命令执行失败.

echo

exit 113   # 返回113退出状态给shell.
           # 为了验证这个结果, 可以在脚本结束的地方使用"echo $?".

#  一般的, 'exit 0' 表示成功,
#+ 而一个非零的退出码表示一个错误, 或者是反常的条件.
%%%&&&ex5.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex60.sh@@@!!!************************************************************************************
#!/bin/bash
# 函数和参数

DEFAULT=default                             # 默认参数值.

func2 () {
   if [ -z "$1" ]                           # 第一个参数是否长度为零?
   then
     echo "-Parameter #1 is zero length.-"  # 或者没有参数被传递进来.
   else
     echo "-Param #1 is /"$1/".-"
   fi

   variable=${1-$DEFAULT}                   #  这里的参数替换
   echo "variable = $variable"              #+ 表示什么?
                                            #  ---------------------------
                                            #  为了区分没有参数的情况,
                                            #+ 和只有一个null参数的情况.

   if [ "$2" ]
   then
     echo "-Parameter #2 is /"$2/".-"
   fi

   return 0
}

echo
  
echo "Nothing passed."  
func2                          # 不带参数调用
echo


echo "Zero-length parameter passed."
func2 ""                       # 使用0长度的参数进行调用
echo

echo "Null parameter passed."
func2 "$uninitialized_param"   # 使用未初始化的参数进行调用
echo

echo "One parameter passed."  
func2 first           # 带一个参数调用
echo

echo "Two parameters passed."  
func2 first second    # 带两个参数调用
echo

echo "/"/" /"second/" passed."
func2 "" second       # 带两个参数调用,
echo                  # 第一个参数长度为0, 第二个参数是由ASCII码组成的字符串.

exit 0
%%%&&&ex60.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex61.sh@@@!!!************************************************************************************
#!/bin/bash

# 将阿拉伯数字转化为罗马数字
# 范围: 0 - 200
# 比较粗糙, 但可以正常工作.

# 扩展范围, 并且完善这个脚本, 作为练习.

# 用法: roman number-to-convert

LIMIT=200
E_ARG_ERR=65
E_OUT_OF_RANGE=66

if [ -z "$1" ]
then
  echo "Usage: `basename $0` number-to-convert"
  exit $E_ARG_ERR
fi 

num=$1
if [ "$num" -gt $LIMIT ]
then
  echo "Out of range!"
  exit $E_OUT_OF_RANGE
fi 

to_roman ()   # 在第一次调用函数前必须先定义它.
{
number=$1
factor=$2
rchar=$3
let "remainder = number - factor"
while [ "$remainder" -ge 0 ]
do
  echo -n $rchar
  let "number -= factor"
  let "remainder = number - factor"
done 

return $number
       # 练习:
       # -----
       # 解释这个函数是如何工作的.
       # 提示: 依靠不断的除, 来分割数字.
}
  

to_roman $num 100 C
num=$?
to_roman $num 90 LXXXX
num=$?
to_roman $num 50 L
num=$?
to_roman $num 40 XL
num=$?
to_roman $num 10 X
num=$?
to_roman $num 9 IX
num=$?
to_roman $num 5 V
num=$?
to_roman $num 4 IV
num=$?
to_roman $num 1 I

echo

exit 0
%%%&&&ex61.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex62.sh@@@!!!************************************************************************************
#!/bin/bash
# 函数内部的局部变量与全局变量.

func ()
{
  local loc_var=23       # 声明为局部变量.
  echo                   # 使用'local'内建命令.
  echo "/"loc_var/" in function = $loc_var"
  global_var=999         # 没有声明为局部变量.
                         # 默认为全局变量. 
  echo "/"global_var/" in function = $global_var"

func

# 现在, 来看看局部变量"loc_var"在函数外部是否可见.

echo
echo "/"loc_var/" outside function = $loc_var"
                                      # $loc_var outside function =
                                      # 不行, $loc_var不是全局可见的.
echo "/"global_var/" outside function = $global_var"
                                      # 在函数外部$global_var = 999
                                      # $global_var是全局可见的.
echo         

exit 0
#  与C语言相比, 在函数内声明的Bash变量
#+ 除非它被明确声明为local时, 它才是局部的.
%%%&&&ex62.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex63.sh@@@!!!************************************************************************************
#!/bin/bash

#               阶乘
#               ----


# bash允许递归吗?
# 嗯, 允许, 但是...
# 他太慢了, 所以恐怕你难以忍受.


MAX_ARG=5
E_WRONG_ARGS=65
E_RANGE_ERR=66


if [ -z "$1" ]
then
  echo "Usage: `basename $0` number"
  exit $E_WRONG_ARGS
fi

if [ "$1" -gt $MAX_ARG ]
then
  echo "Out of range (5 is maximum)."
  #  现在让我们来了解一些实际情况.
  #  如果你想计算比这个更大的范围的阶乘,
  #+ 应该用真正的编程语言来重写它.
  exit $E_RANGE_ERR
fi 

fact ()
{
  local number=$1
  #  变量"number"必须声明为局部变量,
  #+ 否则不能正常工作.
  if [ "$number" -eq 0 ]
  then
    factorial=1    # 0的阶乘为1.
  else
    let "decrnum = number - 1"
    fact $decrnum  # 递归的函数调用(就是函数调用自己).
    let "factorial = $number * $?"
  fi

  return $factorial
}

fact $1
echo "Factorial of $1 is $?."

exit 0
%%%&&&ex63.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex64.sh@@@!!!************************************************************************************
#!/bin/bash
# "与列表"

if [ ! -z "$1" ] && echo "Argument #1 = $1" && [ ! -z "$2" ] && echo "Argument #2 = $2"
then
  echo "At least 2 arguments passed to script."
  # 所有连接起来的命令都返回true.
else
  echo "Less than 2 arguments passed to script."
  # 整个命令列表中至少有一个命令返回false.
fi 
# 注意, "if [ ! -z $1 ]"也可以, 但它是有所假定的等价物.
#   if [ -n $1 ] 这个不行.
#     然而, 如果加了引用就行了.
#  if [ -n "$1" ] 这样就行了.
#     小心!
# 最好将你要测试的变量引用起来, 这么做是非常好的习惯.


# 下面这段代码与上面代码是等价的, 不过下面这段代码使用的是"纯粹"的if/then结构.
if [ ! -z "$1" ]
then
  echo "Argument #1 = $1"
fi
if [ ! -z "$2" ]
then
  echo "Argument #2 = $2"
  echo "At least 2 arguments passed to script."
else
  echo "Less than 2 arguments passed to script."
fi
# 这么写的话, 行数太多了, 没有"与列表"来的精简.


exit 0
%%%&&&ex64.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex65.sh@@@!!!************************************************************************************
#!/bin/bash

#  delete.sh, 不是很聪明的文件删除方法.
#  Usage: delete filename

E_BADARGS=65

if [ -z "$1" ]
then
  echo "Usage: `basename $0` filename"
  exit $E_BADARGS  # 没有参数? 退出脚本.
else 
  file=$1          # 设置文件名.
fi 


[ ! -f "$file" ] && echo "File /"$file/" not found. /
Cowardly refusing to delete a nonexistent file."
# 与列表, 在文件不存在时将会给出错误信息.
# 注意echo命令使用了一个续行符, 这样下一行的内容, 也会作为echo命令的参数.

[ ! -f "$file" ] || (rm -f $file; echo "File /"$file/" deleted.")
# 或列表, 如果文件存在, 那就删除此文件.

# 注意, 上边的两个逻辑相反.
# 与列表在true的情况下才执行, 或列表在false的时候才执行.

exit 0
%%%&&&ex65.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex66.sh@@@!!!************************************************************************************
#!/bin/bash


area[11]=23
area[13]=37
area[51]=UFOs

#  数组成员不一定非得是相邻或连续的.

#  数组的部分成员可以不被初始化.
#  数组中允许空缺元素.
#  实际上, 保存着稀疏数据的数组("稀疏数组")
#+ 在电子表格处理软件中是非常有用的.


echo -n "area[11] = "
echo ${area[11]}    #  需要{大括号}.

echo -n "area[13] = "
echo ${area[13]}

echo "Contents of area[51] are ${area[51]}."

# 没被初始化的数组成员打印为空值(null变量).
echo -n "area[43] = "
echo ${area[43]}
echo "(area[43] unassigned)"

echo

# 两个数组元素的和被赋值给另一个数组元素
area[5]=`expr ${area[11]} + ${area[13]}`
echo "area[5] = area[11] + area[13]"
echo -n "area[5] = "
echo ${area[5]}

area[6]=`expr ${area[11]} + ${area[51]}`
echo "area[6] = area[11] + area[51]"
echo -n "area[6] = "
echo ${area[6]}
# 这里会失败, 是因为不允许整数与字符串相加.

echo; echo; echo

# -----------------------------------------------------------------
# 另一个数组, "area2".
# 另一种给数组变量赋值的方法...
# array_name=( XXX YYY ZZZ ... )

area2=( zero one two three four )

echo -n "area2[0] = "
echo ${area2[0]}
# 阿哈, 从0开始计算数组下标(也就是数组的第一个元素为[0], 而不是[1]).

echo -n "area2[1] = "
echo ${area2[1]}    # [1]是数组的第2个元素.
# -----------------------------------------------------------------

echo; echo; echo

# -----------------------------------------------
# 第3个数组, "area3".
# 第3种给数组元素赋值的方法...
# array_name=([xx]=XXX [yy]=YYY ...)

area3=([17]=seventeen [24]=twenty-four)

echo -n "area3[17] = "
echo ${area3[17]}

echo -n "area3[24] = "
echo ${area3[24]}
# -----------------------------------------------

exit 0
%%%&&&ex66.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex67.sh@@@!!!************************************************************************************
#!/bin/bash

declare -a colors
#  脚本中所有的后续命令都会把
#+ 变量"colors"看作数组.

echo "Enter your favorite colors (separated from each other by a space)."

read -a colors    # 至少需要键入3种颜色, 以便于后边的演示.
#  'read'命令的特殊选项,
#+ 允许给数组元素赋值.

echo

element_count=${#colors[@]}
# 提取数组元素个数的特殊语法.
#     用element_count=${#colors[*]}也一样.
#
#  "@"变量允许在引用中存在单词分割(word splitting)
#+ (依靠空白字符来分隔变量).
#
#  这就好像"$@"和"$*"
#+ 在位置参数中的所表现出来的行为一样.

index=0

while [ "$index" -lt "$element_count" ]
do    # 列出数组中的所有元素.
  echo ${colors[$index]}
  let "index = $index + 1"
  # 或:
  #    index+=1
  # 如果你运行的Bash版本是3.1以后的话, 才支持这种语法.
done
# 每个数组元素被列为单独的一行.
# 如果没有这种要求的话, 可以使用echo -n "${colors[$index]} "
#
# 也可以使用"for"循环来做:
#   for i in "${colors[@]}"
#   do
#     echo "$i"
#   done
# (感谢, S.C.)

echo

# 再次列出数组中的所有元素, 不过这次的做法更优雅.
  echo ${colors[@]}          # 用echo ${colors[*]}也行.

echo

# "unset"命令即可以删除数组数据, 也可以删除整个数组.
unset colors[1]              # 删除数组的第2个元素.
                             # 作用等效于   colors[1]=
echo  ${colors[@]}           # 再次列出数组内容, 第2个元素没了.

unset colors                 # 删除整个数组.
                             #  unset colors[*] 或
                             #+ unset colors[@] 都可以.
echo; echo -n "Colors gone."     
echo ${colors[@]}            # 再次列出数组内容, 内容为空.

exit 0
%%%&&&ex67.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex68.sh@@@!!!************************************************************************************
#!/bin/bash
# sieve.sh (ex68.sh)

# 埃拉托色尼素数筛子
# 找素数的经典算法.

#  在同等数值的范围内,
#+ 这个脚本运行的速度比C版本慢的多.

LOWER_LIMIT=1       # 从1开始.
UPPER_LIMIT=1000    # 到1000.
# (如果你时间很多的话 . . . 你可以将这个数值调的很高.)

PRIME=1
NON_PRIME=0

let SPLIT=UPPER_LIMIT/2
# 优化:
# 只需要测试中间到最大的值(为什么?).
# (译者注: 这个变量在脚本正文并没有被使用, 仅仅在107行之后的优化部分才使用.)

declare -a Primes
# Primes[]是个数组.


initialize ()
{
# 初始化数组.

i=$LOWER_LIMIT
until [ "$i" -gt "$UPPER_LIMIT" ]
do
  Primes[i]=$PRIME
  let "i += 1"
done
#  假定所有数组成员都是需要检查的(素数)
#+ 直到检查完成.
}

print_primes ()
{
# 打印出所有数组Primes[]中被标记为素数的元素.

i=$LOWER_LIMIT

until [ "$i" -gt "$UPPER_LIMIT" ]
do

  if [ "${Primes[i]}" -eq "$PRIME" ]
  then
    printf "%8d" $i
    # 每个数字打印前先打印8个空格, 在偶数列才打印.
  fi
 
  let "i += 1"
 
done

}

sift () # 查出非素数.
{

let i=$LOWER_LIMIT+1
# 我们都知道1是素数, 所以我们从2开始.
# (译者注: 从2开始并不是由于1是素数, 而是因为要去掉以后每个数的倍数, 感谢网友KevinChen.)
until [ "$i" -gt "$UPPER_LIMIT" ]
do

if [ "${Primes[i]}" -eq "$PRIME" ]
# 不要处理已经过滤过的数字(被标识为非素数).
then

  t=$i

  while [ "$t" -le "$UPPER_LIMIT" ]
  do
    let "t += $i "
    Primes[t]=$NON_PRIME
    # 标识为非素数.
  done

fi 

  let "i += 1"
done 


}


# ==============================================
# main ()
# 继续调用函数.
initialize
sift
print_primes
# 这里就是被称为结构化编程的东西.
# ==============================================

echo

exit 0

 

# -------------------------------------------------------- #
# 因为前面的'exit'语句, 所以后边的代码不会运行.

#  下边的代码, 是由Stephane Chazelas所编写的埃拉托色尼素数筛子的改进版本,
#+ 这个版本可以运行的快一些.

# 必须在命令行上指定参数(这个参数就是: 寻找素数的限制范围).

UPPER_LIMIT=$1                  # 来自于命令行.
let SPLIT=UPPER_LIMIT/2         # 从中间值到最大值.

Primes=( '' $(seq $UPPER_LIMIT) )

i=1
until (( ( i += 1 ) > SPLIT ))  # 仅需要从中间值检查.
do
  if [[ -n $Primes[i] ]]
  then
    t=$i
    until (( ( t += i ) > UPPER_LIMIT ))
    do
      Primes[t]=
    done
  fi 
done 
echo ${Primes[*]}

exit 0
%%%&&&ex68.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex69.sh@@@!!!************************************************************************************
#!/bin/bash

# 用非交互的方式来使用'vi'编辑一个文件.
# 模仿'sed'.

E_BADARGS=65

if [ -z "$1" ]
then
  echo "Usage: `basename $0` filename"
  exit $E_BADARGS
fi

TARGETFILE=$1

# 在文件中插入两行, 然后保存.
#--------Begin here document-----------#
vi $TARGETFILE &lt;&lt;x23LimitStringx23
i
This is line 1 of the example file.
This is line 2 of the example file.
^[
ZZ
x23LimitStringx23
#----------End here document-----------#

#  注意上边^[是一个转义符, 键入Ctrl+v &lt;Esc&gt;就行,
#+ 事实上它是&lt;Esc&gt键;.

#  Bram Moolenaar指出这种方法不能使用在'vim'上, (译者注: Bram Moolenaar是vim作者)
#+ 因为可能会存在终端相互影响的问题.

exit 0
%%%&&&ex69.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex6.sh@@@!!!************************************************************************************
#!/bin/bash

#  检查一些系统环境变量.
#  这是一种可以做一些预防性保护措施的好习惯.
#  比如, 如果$USER(用户在控制台上中的名字)没有被设置的话,
#+ 那么系统就会不认你.

: ${HOSTNAME?} ${USER?} ${HOME?} ${MAIL?}
  echo
  echo "Name of the machine is $HOSTNAME."
  echo "You are $USER."
  echo "Your home directory is $HOME."
  echo "Your mail INBOX is located in $MAIL."
  echo
  echo "If you are reading this message,"
  echo "critical environmental variables have been set."
  echo
  echo

# ------------------------------------------------------

#  ${variablename?}结构
#+ 也能够检查脚本中变量的设置情况.

ThisVariable=Value-of-ThisVariable
#  注意, 顺便提一下,
#+ 这个字符串变量可能会被设置一些非法字符.
: ${ThisVariable?}
echo "Value of ThisVariable is $ThisVariable".
echo
echo


: ${ZZXy23AB?"ZZXy23AB has not been set."}
#  如果变量ZZXy23AB没有被设置的话,
#+ 那么这个脚本会打印一个错误信息, 然后结束.

# 你可以自己指定错误消息.
# : ${variablename?"ERROR MESSAGE"}


# 等价于:    dummy_variable=${ZZXy23AB?}
#            dummy_variable=${ZZXy23AB?"ZXy23AB has not been set."}
#
#            echo ${ZZXy23AB?} >/dev/null

#  使用命令"set -u"来比较这些检查变量是否被设置的方法.
#

 

echo "You will not see this message, because script already terminated."

HERE=0
exit $HERE   # 不会在这里退出.

# 事实上, 这个脚本将会以返回值1作为退出状态(echo $?).
%%%&&&ex6.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex70.sh@@@!!!************************************************************************************
#!/bin/bash

wall &lt;&lt;zzz23EndOfMessagezzz23
E-mail your noontime orders for pizza to the system administrator.
    (Add an extra dollar for anchovy or mushroom topping.)
# 附加的消息文本放在这里.
# 注意: 'wall'命令会把注释行也打印出来.
zzz23EndOfMessagezzz23

# 当然, 更有效率的做法是:
#         wall &lt;message-file
#  然而, 将消息模版嵌入到脚本中
#+ 只是一种"小吃店"(译者注: 方便但是不卫生)的做法, 而且这种做法是一次性的.

exit 0
%%%&&&ex70.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex71a.sh@@@!!!************************************************************************************
#!/bin/bash
# 与之前的例子相同, 但是...

#  - 选项对于here docutment来说,
#+ <<-可以抑制文档体前边的tab,
#+ 而*不*是空格.

cat &lt;&lt;-ENDOFMESSAGE
 This is line 1 of the message.
 This is line 2 of the message.
 This is line 3 of the message.
 This is line 4 of the message.
 This is the last line of the message.
ENDOFMESSAGE
# 脚本在输出的时候左边将被刷掉.
# 就是说每行前边的tab将不会显示.

# 上边5行"消息"的前边都是tab, 而不是空格.
# 空格是不受<<-影响的.

# 注意, 这个选项对于*嵌在*中间的tab没作用.

exit 0
%%%&&&ex71a.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex71b.sh@@@!!!************************************************************************************
#!/bin/bash
# 一个使用'cat'命令的here document, 使用了参数替换.

# 不传命令行参数给它,   ./scriptname
# 传一个命令行参数给它,   ./scriptname Mortimer
# 传一个包含2个单词(用引号括起来)的命令行参数给它,
#                           ./scriptname "Mortimer Jones"

CMDLINEPARAM=1     #  所期望的最少的命令行参数个数.

if [ $# -ge $CMDLINEPARAM ]
then
  NAME=$1          #  如果命令行参数超过1个,
                   #+ 那么就只取第一个参数.
else
  NAME="John Doe"  #  默认情况下, 如果没有命令行参数的话.
fi 

RESPONDENT="the author of this fine script" 
 

cat &lt;&lt;Endofmessage

Hello, there, $NAME.
Greetings to you, $NAME, from $RESPONDENT.

# This comment shows up in the output (why?).

Endofmessage

# 注意上边的空行也打印输出,
# 而上边那行"注释"当然也会打印到输出.
# (译者注: 这就是为什么不翻译那行注释的原因, 尽量保持代码的原样)
exit 0
%%%&&&ex71b.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex71c.sh@@@!!!************************************************************************************
#!/bin/bash
#  一个使用'cat'的here document, 但是禁用了参数替换.

NAME="John Doe"
RESPONDENT="the author of this fine script" 

cat &lt;&lt;'Endofmessage'

Hello, there, $NAME.
Greetings to you, $NAME, from $RESPONDENT.

Endofmessage

#  如果"limit string"被引用或转义的话, 那么就禁用了参数替换.
#  下边的两种方式具有相同的效果.
#  cat &lt;&lt;"Endofmessage"
#  cat &lt;&lt;/Endofmessage

exit 0
%%%&&&ex71c.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex71.sh@@@!!!************************************************************************************
#!/bin/bash

#  'echo'对于打印单行消息来说是非常好用的,
#+  但是在打印消息块时可能就有点问题了.
#   'cat' here document可以解决这个限制.

cat &lt;&lt;End-of-message
-------------------------------------
This is line 1 of the message.
This is line 2 of the message.
This is line 3 of the message.
This is line 4 of the message.
This is the last line of the message.
-------------------------------------
End-of-message

#  用下边这行代替上边的第7行,
#+   cat &gt; $Newfile &lt;&lt;End-of-message
#+       ^^^^^^^^^^
#+ 那么就会把输出写到文件$Newfile中, 而不是stdout.

exit 0


#--------------------------------------------
# 下边的代码不会运行, 因为上边有"exit 0".

# S.C. 指出下边代码也能够达到相同目的.
echo "-------------------------------------
This is line 1 of the message.
This is line 2 of the message.
This is line 3 of the message.
This is line 4 of the message.
This is the last line of the message.
-------------------------------------"
# 然而, 文本中可能不允许包含双引号, 除非它们被转义.
%%%&&&ex71.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex72.sh@@@!!!************************************************************************************
#!/bin/bash
# upload.sh

#  上传这一对文件(Filename.lsm, Filename.tar.gz)
#+ 到Sunsite/UNC (ibiblio.org)的incoming目录.
#  Filename.tar.gz是自身的tar包.
#  Filename.lsm是描述文件.
#  Sunsite需要"lsm"文件, 否则就拒绝上传.


E_ARGERROR=65

if [ -z "$1" ]
then
  echo "Usage: `basename $0` Filename-to-upload"
  exit $E_ARGERROR
fi 


Filename=`basename $1`           # 从文件名中去掉目录字符串.

Server="ibiblio.org"
Directory="/incoming/Linux"
#  在这里也不一定非得将上边的参数写死在这个脚本中,
#+ 可以使用命令行参数的方法来替换.

Password="your.e-mail.address"   # 可以修改成相匹配的密码.

ftp -n $Server &lt;&lt;End-Of-Session
# -n选项禁用自动登录.

user anonymous "$Password"
binary
bell                             # 在每个文件传输后, 响铃.
cd $Directory
put "$Filename.lsm"
put "$Filename.tar.gz"
bye
End-Of-Session

exit 0
%%%&&&ex72.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex73.sh@@@!!!************************************************************************************
#!/bin/bash
# 创建一个交换文件.

ROOT_UID=0         # Root用户的$UID为0.
E_WRONG_USER=65    # 不是root?

FILE=/swap
BLOCKSIZE=1024
MINBLOCKS=40
SUCCESS=0


# 这个脚本必须以root身份来运行.
if [ "$UID" -ne "$ROOT_UID" ]
then
  echo; echo "You must be root to run this script."; echo
  exit $E_WRONG_USER
fi 
 

blocks=${1:-$MINBLOCKS}          #  如果没在命令行上指定,
                                 #+ 默认设置为40块.
# 上边这句等价于下面这个命令块.
# --------------------------------------------------
# if [ -n "$1" ]
# then
#   blocks=$1
# else
#   blocks=$MINBLOCKS
# fi
# --------------------------------------------------


if [ "$blocks" -lt $MINBLOCKS ]
then
  blocks=$MINBLOCKS              # 至少要有40块.
fi 


echo "Creating swap file of size $blocks blocks (KB)."
dd if=/dev/zero of=$FILE bs=$BLOCKSIZE count=$blocks  # 用零填充文件.

mkswap $FILE $blocks             # 将其指定为交换文件(译者注: 或称为交换分区).
swapon $FILE                     # 激活交换文件.

echo "Swap file created and activated."

exit $SUCCESS
%%%&&&ex73.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex74.sh@@@!!!************************************************************************************
#!/bin/bash
# ex74.sh

# 这是一个错误脚本.
# 哪里出了错?

a=37

if [$a -gt 27 ]
then
  echo $a
fi 

exit 0
%%%&&&ex74.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex75.sh@@@!!!************************************************************************************
#!/bin/bash

#  这个脚本的目的是删除当前目录下的某些文件,
#+ 这些文件特指那些文件名包含空格的文件.
#  但是不能如我们所愿的那样工作.
#  为什么?


badname=`ls | grep ' '`

# 试试这个:
# echo "$badname"

rm "$badname"

exit 0
%%%&&&ex75.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex76.sh@@@!!!************************************************************************************
#!/bin/bash
# 使用trap来捕捉变量值.

trap 'echo Variable Listing --- a = $a  b = $b' EXIT
#  EXIT是脚本中exit命令所产生信号的名字.
#
#  "trap"所指定的命令并不会马上执行,
#+ 只有接收到合适的信号, 这些命令才会执行.

echo "This prints before the /"trap/" --"
echo "even though the script sees the /"trap/" first."
echo

a=39

b=36

exit 0
#  注意, 即使注释掉上面的这行'exit'命令, 也不会产生什么不同的结果,
#+ 这是因为所有命令都执行完毕后, 不管怎么样, 脚本都会退出的.
%%%&&&ex76.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex77.sh@@@!!!************************************************************************************
#!/bin/bash

# 字符串扩展.
# Bash版本2中引入的特性.

#  $'xxx'格式的字符串
#+ 具备解释里面标准转义字符的能力.

echo $'Ringing bell 3 times /a /a /a'
     # 可能在某些终端中, 只会响一次铃.
echo $'Three form feeds /f /f /f'
echo $'10 newlines /n/n/n/n/n/n/n/n/n/n'
echo $'/102/141/163/150'   # Bash
                           # 8进制的等价字符.

exit 0
%%%&&&ex77.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex78.sh@@@!!!************************************************************************************
#!/bin/bash

# 间接变量引用.
# 这种方法比较像C++中的引用特性.


a=letter_of_alphabet
letter_of_alphabet=z

echo "a = $a"           # 直接引用.

echo "Now a = ${!a}"    # 间接引用.
# ${!variable}表示法比老式的"eval var1=/$$var2"表示法高级的多.

echo

t=table_cell_3
table_cell_3=24
echo "t = ${!t}"                      # t = 24
table_cell_3=387
echo "Value of t changed to ${!t}"    # 387

#  在引用数组成员或者引用表的时候, 这种方法非常有用,
#+ 还可以用来模拟多维数组.
#  如果有能够索引的选项(类似于指针的算术运算)
#+ 就更好了. 可惜.

exit 0
%%%&&&ex78.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex79.sh@@@!!!************************************************************************************
#!/bin/bash

# 纸牌:
# 处理4人打牌.

UNPICKED=0
PICKED=1

DUPE_CARD=99

LOWER_LIMIT=0
UPPER_LIMIT=51
CARDS_IN_SUIT=13
CARDS=52

declare -a Deck
declare -a Suits
declare -a Cards
#  使用一个3维数组来代替这3个一维数组来描述数据,
#+ 可以更容易实现, 而且可以增加可读性.
#  或许在Bash未来的版本上会支持多维数组.


initialize_Deck ()
{
i=$LOWER_LIMIT
until [ "$i" -gt $UPPER_LIMIT ]
do
  Deck[i]=$UNPICKED   # 将整副"牌"的每一张都设置为无人持牌的状态.
  let "i += 1"
done
echo
}

initialize_Suits ()
{
Suits[0]=C #梅花
Suits[1]=D #方块
Suits[2]=H #红心
Suits[3]=S #黑桃
}

initialize_Cards ()
{
Cards=(2 3 4 5 6 7 8 9 10 J Q K A)
# 另一种初始化数组的方法.
}

pick_a_card ()
{
card_number=$RANDOM
let "card_number %= $CARDS"
if [ "${Deck[card_number]}" -eq $UNPICKED ]
then
  Deck[card_number]=$PICKED
  return $card_number
else 
  return $DUPE_CARD
fi
}

parse_card ()
{
number=$1
let "suit_number = number / CARDS_IN_SUIT"
suit=${Suits[suit_number]}
echo -n "$suit-"
let "card_no = number % CARDS_IN_SUIT"
Card=${Cards[card_no]}
printf %-4s $Card
# 使用整洁的列形式来打印每张牌.
}

seed_random ()  # 种子随机数产生器.
{               # 如果不这么做, 会发生什么?
seed=`eval date +%s`
let "seed %= 32766"
RANDOM=$seed
#  还有其他的方法
#+ 能够产生种子随机数么?
}

deal_cards ()
{
echo

cards_picked=0
while [ "$cards_picked" -le $UPPER_LIMIT ]
do
  pick_a_card
  t=$?

  if [ "$t" -ne $DUPE_CARD ]
  then
    parse_card $t

    u=$cards_picked+1
    # 将数组索引改为从1(译者注: 数组都是从0开始索引的)开始(临时的). 为什么?
    let "u %= $CARDS_IN_SUIT"
    if [ "$u" -eq 0 ]   # 内嵌的if/then条件测试.
    then
     echo
     echo
    fi
    # 分手.

    let "cards_picked += 1"
  fi 
done 

echo

return 0
}


# 结构化编程:
# 将函数中的整个程序逻辑模块化.

#================
seed_random
initialize_Deck
initialize_Suits
initialize_Cards
deal_cards
#================

exit 0

 

# 练习1:
# 完整的注释这个脚本.

# 练习2:
# 添加一个例程(函数)按照花色打印出每手牌.
# 如果你喜欢, 可以添加任何你想要添加的代码.

# 练习3:
# 简化并理顺脚本逻辑.
%%%&&&ex79.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex7.sh@@@!!!************************************************************************************
#!/bin/bash

var1=abcd-1234-defg
echo "var1 = $var1"

t=${var1#*-*}
echo "var1 (with everything, up to and including first - stripped out) = $t"
#  t=${var1#*-}  也一样,
#+ 因为#匹配最短的字符串,
#+ 同时*匹配任意前缀, 包括空字符串.
# (感谢, Stephane Chazelas, 指出这点.)

t=${var1##*-*}
echo "If var1 contains a /"-/", returns empty string...   var1 = $t"


t=${var1%*-*}
echo "var1 (with everything from the last - on stripped out) = $t"

echo

# -------------------------------------------
path_name=/home/bozo/ideas/thoughts.for.today
# -------------------------------------------
echo "path_name = $path_name"
t=${path_name##/*/}
echo "path_name, stripped of prefixes = $t"
# 在这个特例中, 与 t=`basename $path_name`  效果相同.
#  t=${path_name%/}; t=${t##*/}   是更一般的解决方法.
#+ 但有时还是会失败.
#  如果$path_name以一个换行符结尾的话, 那么 `basename $path_name` 就不能正常工作了,
#+ 但是上边的表达式可以.
# (感谢, S.C.)

t=${path_name%/*.*}
# 与 t=`dirname $path_name` 效果相同.
echo "path_name, stripped of suffixes = $t"
# 在某些情况下将失效, 比如 "../", "/foo", # "foo/", "/".
#  删除后缀, 尤其是在basename没有后缀的情况下,
#+ 但是dirname可以, 不过这同时也使问题复杂化了.
# (感谢, S.C.)

echo

t=${path_name:11}
echo "$path_name, with first 11 chars stripped off = $t"
t=${path_name:11:5}
echo "$path_name, with first 11 chars stripped off, length 5 = $t"

echo

t=${path_name/bozo/clown}
echo "$path_name with /"bozo/" replaced  by /"clown/" = $t"
t=${path_name/today/}
echo "$path_name with /"today/" deleted = $t"
t=${path_name//o/O}
echo "$path_name with all o's capitalized = $t"
t=${path_name//o/}
echo "$path_name with all o's deleted = $t"

exit 0
%%%&&&ex7.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex8.sh@@@!!!************************************************************************************
#!/bin/bash
# 从/etc/fstab中读行.

File=/etc/fstab

{
read line1
read line2
} < $File

echo "First line in $File is:"
echo "$line1"
echo
echo "Second line in $File is:"
echo "$line2"

exit 0

# 现在, 你怎么分析每行的分割域?
# 小提示: 使用awk.
%%%&&&ex8.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ex9.sh@@@!!!************************************************************************************
#!/bin/bash

# 变量赋值和替换

a=375
hello=$a

#-------------------------------------------------------------------------
# 强烈注意, 在赋值的的时候, 等号前后一定不要有空格.
# 如果出现空格会怎么样?

#  "VARIABLE =value"
#           ^
#% 脚本将尝试运行一个"VARIABLE"的命令, 带着一个"=value"参数.

#  "VARIABLE= value"
#            ^
#% 脚本将尝试运行一个"value"的命令,
#+ 并且带着一个被赋值成""的环境变量"VARIABLE".
#-------------------------------------------------------------------------


echo hello    # 没有变量引用, 只是个hello字符串.

echo $hello
echo ${hello} # 同上.

echo "$hello"
echo "${hello}"

echo

hello="A B  C   D"
echo $hello   # A B C D
echo "$hello" # A B  C   D
# 就象你看到的echo $hello   和    echo "$hello"   将给出不同的结果.
# ===============================================================
# 引用一个变量将保留其中的空白, 当然, 如果是变量替换就不会保留了.
# ===============================================================

echo

echo '$hello'  # $hello
#    ^      ^
#  全引用的作用将会导致"$"被解释为单独的字符,
#+ 而不是变量前缀.

# 注意这两种引用所产生的不同的效果.


hello=    # 设置为空值.
echo "/$hello (null value) = $hello"
#  注意设置一个变量为null, 与unset这个变量, 并不是一回事
#+ 虽然最终的结果相同(具体见下边).

# --------------------------------------------------------------

#  可以在同一行上设置多个变量,
#+ 但是必须以空白进行分隔.
#  慎用, 这么做会降低可读性, 并且不可移植.

var1=21  var2=22  var3=$V3
echo
echo "var1=$var1   var2=$var2   var3=$var3"

# 在老版本的"sh"上可能会引起问题.

# --------------------------------------------------------------

echo; echo

numbers="one two three"
#           ^   ^
other_numbers="1 2 3"
#               ^ ^
#  如果在变量中存在空白, If there is whitespace embedded within a variable,
#+ 那么就必须加上引用.
#  other_numbers=1 2 3                  # 给出一个错误消息.
echo "numbers = $numbers"
echo "other_numbers = $other_numbers"   # other_numbers = 1 2 3
#  不过也可以采用将空白转义的方法.
mixed_bag=2/ ---/ Whatever
#           ^    ^ 在转义符后边的空格(/).

echo "$mixed_bag"         # 2 --- Whatever

echo; echo

echo "uninitialized_variable = $uninitialized_variable"
# Uninitialized变量为null(就是没有值).
uninitialized_variable=   #  声明, 但是没有初始化这个变量,
                          #+ 其实和前边设置为空值的作用是一样的.
echo "uninitialized_variable = $uninitialized_variable"
                          # 还是一个空值.

uninitialized_variable=23       # 赋值.
unset uninitialized_variable    # Unset这个变量.
echo "uninitialized_variable = $uninitialized_variable"
                                # 还是一个空值.
echo

exit 0
%%%&&&ex9.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@exercising-dd.sh@@@!!!************************************************************************************
#!/bin/bash
# exercising-dd.sh

# 由Stephane Chazelas编写.
# 本文作者做了少量修改.

input_file=$0   # 脚本自身.
output_file=log.txt
n=3
p=5

dd if=$input_file of=$output_file bs=1 skip=$((n-1)) count=$((p-n+1)) 2> /dev/null
# 从脚本中把位置n到p的字符提取出来.

# -------------------------------------------------------

echo -n "hello world" | dd cbs=1 conv=unblock 2> /dev/null
# 垂直地echo "hello world".

exit 0
%%%&&&exercising-dd.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@factr.sh@@@!!!************************************************************************************
#!/bin/bash
# factr.sh: 分解约数

MIN=2       # 如果比这个数小就不行了.
E_NOARGS=65
E_TOOSMALL=66

if [ -z $1 ]
then
  echo "Usage: $0 number"
  exit $E_NOARGS
fi

if [ "$1" -lt "$MIN" ]
then
  echo "Number to factor must be $MIN or greater."
  exit $E_TOOSMALL
fi 

# 练习: 添加类型检查(防止非整型的参数).

echo "Factors of $1:"
# ---------------------------------------------------------------------------------
echo "$1[p]s2[lip/dli%0=1dvsr]s12sid2%0=13sidvsr[dli%0=1lrli2+dsi!>.]ds.xd1<2" | dc
# ---------------------------------------------------------------------------------
# 上边这行代码是Michel Charpentier编写的&lt;charpov@cs.unh.edu&gt;.
# 在此使用经过授权(感谢).

 exit 0
%%%&&&factr.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@fc4upd.sh@@@!!!************************************************************************************
#!/bin/bash
# fc4upd.sh

# 脚本作者: Frank Wang.
# 本书作者作了少量修改.
# 授权在本书中使用.


#  使用rsync命令从镜像站点上下载Fedora 4的更新.
#  为了节省空间, 如果有多个版本存在的话,
#+ 只下载最新的包.

URL=rsync://distro.ibiblio.org/fedora-linux-core/updates/
# URL=rsync://ftp.kddilabs.jp/fedora/core/updates/
# URL=rsync://rsync.planetmirror.com/fedora-linux-core/updates/

DEST=${1:-/var/www/html/fedora/updates/}
LOG=/tmp/repo-update-$(/bin/date +%Y-%m-%d).txt
PID_FILE=/var/run/${0##*/}.pid

E_RETURN=65        # 某些意想不到的错误.


# 一般rsync选项
# -r: 递归下载
# -t: 保存时间
# -v: verbose

OPTS="-rtv --delete-excluded --delete-after --partial"

# rsync include模式
# 开头的"/"会导致绝对路径名匹配.
INCLUDE=(
    "/4/i386/kde-i18n-Chinese*"
#   ^                         ^
# 双引号是必须的, 用来防止globbing.
)


# rsync exclude模式
# 使用"#"临时注释掉一些不需要的包.
EXCLUDE=(
    /1
    /2
    /3
    /testing
    /4/SRPMS
    /4/ppc
    /4/x86_64
    /4/i386/debug
   "/4/i386/kde-i18n-*"
   "/4/i386/openoffice.org-langpack-*"
   "/4/i386/*i586.rpm"
   "/4/i386/GFS-*"
   "/4/i386/cman-*"
   "/4/i386/dlm-*"
   "/4/i386/gnbd-*"
   "/4/i386/kernel-smp*"
#  "/4/i386/kernel-xen*"
#  "/4/i386/xen-*"
)


init () {
    # 让管道命令返回可能的rsync错误, 比如, 网络延时(stalled network).
    set -o pipefail

    TMP=${TMPDIR:-/tmp}/${0##*/}.$$     # 保存精炼的下载列表.
    trap "{                                                  
        rm -f $TMP 2>/dev/null                               
    }" EXIT                             # 删除存在的临时文件.
}


check_pid () {
# 检查进程是否存在.
    if [ -s "$PID_FILE" ]; then
        echo "PID file exists. Checking ..."
        PID=$(/bin/egrep -o "^[[:digit:]]+" $PID_FILE)
        if /bin/ps --pid $PID &>/dev/null; then
            echo "Process $PID found. ${0##*/} seems to be running!"
           /usr/bin/logger -t ${0##*/} /
                 "Process $PID found. ${0##*/} seems to be running!"
            exit $E_RETURN
        fi
        echo "Process $PID not found. Start new process . . ."
    fi
}


#  根据上边的模式,
#+ 设置整个文件的更新范围, 从root或$URL开始.
set_range () {
    include=
    exclude=
    for p in "${INCLUDE[@]}"; do
        include="$include --include /"$p/""
    done

    for p in "${EXCLUDE[@]}"; do
        exclude="$exclude --exclude /"$p/""
    done
}


# 获得并提炼rsync更新列表.
get_list () {
    echo $$ > $PID_FILE || {
        echo "Can't write to pid file $PID_FILE"
        exit $E_RETURN
    }

    echo -n "Retrieving and refining update list . . ."

    # 获得列表 -- 作为单个命令来运行rsync的话需要'eval'.
    # $3和$4是文件创建的日期和时间.
    # $5是完整的包名字.
    previous=
    pre_file=
    pre_date=0
    eval /bin/nice /usr/bin/rsync /
        -r $include $exclude $URL | /
        egrep '^dr.x|^-r' | /
        awk '{print $3, $4, $5}' | /
        sort -k3 | /
        { while read line; do
            # 获得这段运行的秒数, 过滤掉不用的包.
            cur_date=$(date -d "$(echo $line | awk '{print $1, $2}')" +%s)
            #  echo $cur_date

            # 取得文件名.
            cur_file=$(echo $line | awk '{print $3}')
            #  echo $cur_file

            # 如果可能的话, 从文件名中取得rpm的包名字.
            if [[ $cur_file == *rpm ]]; then
                pkg_name=$(echo $cur_file | sed -r -e /
                    's/(^([^_-]+[_-])+)[[:digit:]]+/..*[_-].*$//1/')
            else
                pkg_name=
            fi
            # echo $pkg_name

            if [ -z "$pkg_name" ]; then   #  如果不是一个rpm文件,
                echo $cur_file >> $TMP    #+ 然后添加到下载列表里.
            elif [ "$pkg_name" != "$previous" ]; then   # 发现一个新包.
                echo $pre_file >> $TMP                  # 输出最新的文件.
                previous=$pkg_name                      # 保存当前状态.
                pre_date=$cur_date
                pre_file=$cur_file
            elif [ "$cur_date" -gt "$pre_date" ]; then  #  如果是相同的包, 但是这个包更新一些,
                pre_date=$cur_date                      #+ 那么就更新最新的.
                pre_file=$cur_file
            fi
            done
            echo $pre_file >> $TMP                      #  TMP现在包含所有
                                                        #+ 提炼过的列表.
            # echo "subshell=$BASH_SUBSHELL"

    }       # 这里的大括号是为了让最后这句"echo $pre_file >> $TMP"
            # 也能与整个循环一起放到同一个子shell ( 1 )中.

    RET=$?  # 取得管道命令的返回状态.

    [ "$RET" -ne 0 ] && {
        echo "List retrieving failed with code $RET"
        exit $E_RETURN
    }

    echo "done"; echo
}

# 真正的rsync下载部分.
get_file () {

    echo "Downloading..."
    /bin/nice /usr/bin/rsync /
        $OPTS /
        --filter "merge,+/ $TMP" /
        --exclude '*'  /
        $URL $DEST     /
        | /usr/bin/tee $LOG

    RET=$?

        #  --filter merge,+/ 对于这个目的来说, 这句是至关重要的.
        #  + 修饰语意为着包含, / 意味着绝对路径.
        #  然后$TMP中排过序的列表将会包含升序的路径名,
        #+ 并从"简化的流程"(shortcutting the circuit)中阻止下边的 --exclude '*'.

    echo "Done"

    rm -f $PID_FILE 2>/dev/null

    return $RET
}

# -------
# Main
init
check_pid
set_range
get_list
get_file
RET=$?
# -------

if [ "$RET" -eq 0 ]; then
    /usr/bin/logger -t ${0##*/} "Fedora update mirrored successfully."
else
    /usr/bin/logger -t ${0##*/} "Fedora update mirrored with failure code: $RET"
fi

exit $RET
%%%&&&fc4upd.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@fifo.sh@@@!!!************************************************************************************
#!/bin/bash
# ==> Script by James R. Van Zandt, and used here with his permission.

# ==> Comments added by author of this document.

 
  HERE=`uname -n`    # ==> hostname
  THERE=bilbo
  echo "starting remote backup to $THERE at `date +%r`"
  # ==> `date +%r` returns time in 12-hour format, i.e. "08:08:34 PM".
 
  # make sure /pipe really is a pipe and not a plain file
  rm -rf /pipe
  mkfifo /pipe       # ==> Create a "named pipe", named "/pipe".
 
  # ==> 'su xyz' runs commands as user "xyz".
  # ==> 'ssh' invokes secure shell (remote login client).
  su xyz -c "ssh $THERE /"cat >/home/xyz/backup/${HERE}-daily.tar.gz/" < /pipe"&
  cd /
  tar -czf - bin boot dev etc home info lib man root sbin share usr var >/pipe
  # ==> Uses named pipe, /pipe, to communicate between processes:
  # ==> 'tar/gzip' writes to /pipe and 'ssh' reads from /pipe.

  # ==> The end result is this backs up the main directories, from / on down.

  # ==>  What are the advantages of a "named pipe" in this situation,
  # ==>+ as opposed to an "anonymous pipe", with |?
  # ==>  Will an anonymous pipe even work here?


  exit 0
%%%&&&fifo.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@file-comparison.sh@@@!!!************************************************************************************
#!/bin/bash

ARGS=2  # 脚本需要两个参数.
E_BADARGS=65
E_UNREADABLE=66

if [ $# -ne "$ARGS" ]
then
  echo "Usage: `basename $0` file1 file2"
  exit $E_BADARGS
fi

if [[ ! -r "$1" || ! -r "$2" ]]
then
  echo "Both files to be compared must exist and be readable."
  exit $E_UNREADABLE
fi

cmp $1 $2 &> /dev/null  # /dev/null将会禁止"cmp"命令的输出.
#   cmp -s $1 $2 与上边这句的结果相同("-s"选项是禁止输出(silent)标志)
#   感谢Anders Gustavsson指出这点.
#
# 使用'diff'命令也可以, 比如,   diff $1 $2 &> /dev/null

if [ $? -eq 0 ]         # 测试"cmp"命令的退出状态.
then
  echo "File /"$1/" is identical to file /"$2/"."
else 
  echo "File /"$1/" differs from file /"$2/"."
fi

exit 0
%%%&&&file-comparison.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@file-info.sh@@@!!!************************************************************************************
#!/bin/bash
# fileinfo.sh

FILES="/usr/sbin/accept
/usr/sbin/pwck
/usr/sbin/chroot
/usr/bin/fakefile
/sbin/badblocks
/sbin/ypbind"     # 这是你所关心的文件列表.
                  # 扔进去一个假文件, /usr/bin/fakefile.

echo

for file in $FILES
do

  if [ ! -e "$file" ]       # 检查文件是否存在.
  then
    echo "$file does not exist."; echo
    continue                # 继续下一个.
   fi

  ls -l $file | awk '{ print $9 "         file size: " $5 }'  # 打印两个域.
  whatis `basename $file`   # 文件信息.
  # 注意whatis数据库需要提前建立好.
  # 要想达到这个目的, 以root身份运行/usr/bin/makewhatis.
  echo
done 

exit 0
%%%&&&file-info.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@file-integrity.sh@@@!!!************************************************************************************
#!/bin/bash
# file-integrity.sh: 检查一个给定目录下的文件
#                    是否被改动了.

E_DIR_NOMATCH=70
E_BAD_DBFILE=71

dbfile=File_record.md5
# 存储记录的文件名(数据库文件).


set_up_database ()
{
  echo ""$directory"" > "$dbfile"
  # 把目录名写到文件的第一行.
  md5sum "$directory"/* >> "$dbfile"
  # 在文件中附上md5 checksum和filename.
}

check_database ()
{
  local n=0
  local filename
  local checksum

  # ------------------------------------------- #
  #  这个文件检查其实是不必要的,
  #+ 但是能更安全一些.

  if [ ! -r "$dbfile" ]
  then
    echo "Unable to read checksum database file!"
    exit $E_BAD_DBFILE
  fi
  # ------------------------------------------- #

  while read record[n]
  do

    directory_checked="${record[0]}"
    if [ "$directory_checked" != "$directory" ]
    then
      echo "Directories do not match up!"
      # 换个目录试一下.
      exit $E_DIR_NOMATCH
    fi

    if [ "$n" -gt 0 ]   # 不是目录名.
    then
      filename[n]=$( echo ${record[$n]} | awk '{ print $2 }' )
      #  md5sum向后写记录,
      #+ 先写checksum, 然后写filename.
      checksum[n]=$( md5sum "${filename[n]}" )


      if [ "${record[n]}" = "${checksum[n]}" ]
      then
        echo "${filename[n]} unchanged."

      elif [ "`basename ${filename[n]}`" != "$dbfile" ]
             #  跳过checksum数据库文件,
             #+ 因为在每次调用脚本它都会被修改.
      #  ---
      #  这不幸的意味着当我们在$PWD中运行这个脚本时侯,
      #+ 篡改这个checksum数
      #+ 据库文件将不会被检测出来.
      #  练习: 修正这个问题.
 then
          echo "${filename[n]} : CHECKSUM ERROR!"
        # 从上次的检查之后, 文件已经被修改.
      fi

      fi

 

    let "n+=1"
  done <"$dbfile"       # 从checksum数据库文件中读. 

# =================================================== #
# main ()

if [ -z  "$1" ]
then
  directory="$PWD"      #  如果没指定参数的话,
else                    #+ 那么就使用当前的工作目录.
  directory="$1"
fi 

clear                   # 清屏.
echo " Running file integrity check on $directory"
echo

# ------------------------------------------------------------------ #
  if [ ! -r "$dbfile" ] # 是否需要建立数据库文件?
  then
    echo "Setting up database file, /""$directory"/"$dbfile"/"."; echo
    set_up_database
  fi 
# ------------------------------------------------------------------ #

check_database          # 调用主要处理函数.

echo

#  你可能想把这个脚本的输出重定向到文件中,
#+ 尤其在这个目录中有很多文件的时候.

exit 0

#  如果要对数量非常多的文件做完整性检查,
#+ 可以考虑一下"Tripwire"包,
#+ http://sourceforge.net/projects/tripwire/.

%%%&&&file-integrity.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@findstring.sh@@@!!!************************************************************************************
#!/bin/bash
# findstring.sh:
# 在一个指定目录的所有文件中查找一个特定的字符串.

directory=/usr/bin/
fstring="Free Software Foundation"  # 查看哪个文件中包含FSF.

for file in $( find $directory -type f -name '*' | sort )
do
  strings -f $file | grep "$fstring" | sed -e "s%$directory%%"
  #  在"sed"表达式中,
  #+ 我们必须替换掉正常的替换分隔符"/",
  #+ 因为"/"碰巧是我们需要过滤的字符串之一.
  #  如果不用"%"代替"/"作为分隔符,那么这个操作将失败,并给出一个错误消息.(试一试).
done 

exit 0

#  练习 (很简单):
#  ---------------
#  转换这个脚本, 用命令行参数
#+ 代替内部用的$directory和$fstring.
%%%&&&findstring.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@for-loopcmd.sh@@@!!!************************************************************************************
#!/bin/bash
#  for-loopcmd.sh: 带[list]的for循环,
#+ [list]是由命令替换所产生的.

NUMBERS="9 7 3 8 37.53"

for number in `echo $NUMBERS`  # for number in 9 7 3 8 37.53
do
  echo -n "$number "
done

echo
exit 0
%%%&&&for-loopcmd.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@for-loopc.sh@@@!!!************************************************************************************
#!/bin/bash
# 两种循环到10的方法.

echo

# 标准语法.
for a in 1 2 3 4 5 6 7 8 9 10
do
  echo -n "$a "
done 

echo; echo

# +==========================================+

# 现在, 让我们用C风格语法来做相同的事情.

LIMIT=10

for ((a=1; a <= LIMIT ; a++))  # 双圆括号, 并且"LIMIT"变量前面没有"$".
do
  echo -n "$a "
done                           # 这是一个借用'ksh93'的结构.

echo; echo

# +=========================================================================+

# 让我们使用C语言的"逗号操作符", 来同时增加两个变量的值.

for ((a=1, b=1; a <= LIMIT ; a++, b++))  # 逗号将同时进行两条操作.
do
  echo -n "$a-$b "
done

echo; echo

exit 0
%%%&&&for-loopc.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@ftpget.sh@@@!!!************************************************************************************
#! /bin/sh
# $Id: ftpget,v 1.2 91/05/07 21:15:43 moraes Exp $
# Script to perform batch anonymous ftp. Essentially converts a list of
# of command line arguments into input to ftp.
# ==> This script is nothing but a shell wrapper around "ftp" . . .
# Simple, and quick - written as a companion to ftplist
# -h specifies the remote host (default prep.ai.mit.edu)
# -d specifies the remote directory to cd to - you can provide a sequence
# of -d options - they will be cd'ed to in turn. If the paths are relative,
# make sure you get the sequence right. Be careful with relative paths -
# there are far too many symlinks nowadays. 
# (default is the ftp login directory)
# -v turns on the verbose option of ftp, and shows all responses from the
# ftp server. 
# -f remotefile[:localfile] gets the remote file into localfile
# -m pattern does an mget with the specified pattern. Remember to quote
# shell characters. 
# -c does a local cd to the specified directory
# For example,
#  ftpget -h expo.lcs.mit.edu -d contrib -f xplaces.shar:xplaces.sh /
#  -d ../pub/R3/fixes -c ~/fixes -m 'fix*'
# will get xplaces.shar from ~ftp/contrib on expo.lcs.mit.edu, and put it in
# xplaces.sh in the current working directory, and get all fixes from
# ~ftp/pub/R3/fixes and put them in the ~/fixes directory.
# Obviously, the sequence of the options is important, since the equivalent
# commands are executed by ftp in corresponding order
#
# Mark Moraes &lt;moraes@csri.toronto.edu&gt;, Feb 1, 1989
#


# ==> These comments added by author of this document.

# PATH=/local/bin:/usr/ucb:/usr/bin:/bin
# export PATH
# ==> Above 2 lines from original script probably superfluous.

E_BADARGS=65

TMPFILE=/tmp/ftp.$$
# ==> Creates temp file, using process id of script ($$)
# ==> to construct filename.

SITE=`domainname`.toronto.edu
# ==> 'domainname' similar to 'hostname'
# ==> May rewrite this to parameterize this for general use.

usage="Usage: $0 [-h remotehost] [-d remotedirectory]... [-f remfile:localfile]... /
  [-c localdirectory] [-m filepattern] [-v]"
ftpflags="-i -n"
verbflag=
set -f   # So we can use globbing in -m
set x `getopt vh:d:c:m:f: $*`
if [ $? != 0 ]; then
 echo $usage
 exit $E_BADARGS
fi
shift
trap 'rm -f ${TMPFILE} ; exit' 0 1 2 3 15
# ==> Delete tempfile in case of abnormal exit from script.
echo "user anonymous ${USER-gnu}@${SITE} > ${TMPFILE}"
# ==> Added quotes (recommended in complex echoes).
echo binary >> ${TMPFILE}
for i in $*   # ==> Parse command line args.
do
 case $i in
 -v) verbflag=-v; echo hash >> ${TMPFILE}; shift;;
 -h) remhost=$2; shift 2;;
 -d) echo cd $2 >> ${TMPFILE};
     if [ x${verbflag} != x ]; then
         echo pwd >> ${TMPFILE};
     fi;
     shift 2;;
 -c) echo lcd $2 >> ${TMPFILE}; shift 2;;
 -m) echo mget "$2" >> ${TMPFILE}; shift 2;;
 -f) f1=`expr "$2" : "/([^:]*/).*"`; f2=`expr "$2" : "[^:]*:/(.*/)"`;
     echo get ${f1} ${f2} >> ${TMPFILE}; shift 2;;
 --) shift; break;;
 esac
        # ==> 'lcd' and 'mget' are ftp commands. See "man ftp" . . .
done
if [ $# -ne 0 ]; then
 echo $usage
 exit $E_BADARGS
        # ==> Changed from "exit 2" to conform with style standard.
fi
if [ x${verbflag} != x ]; then
 ftpflags="${ftpflags} -v"
fi
if [ x${remhost} = x ]; then
 remhost=prep.ai.mit.edu
 # ==> Change to match appropriate ftp site.
fi
echo quit >> ${TMPFILE}
# ==> All commands saved in tempfile.

ftp ${ftpflags} ${remhost} < ${TMPFILE}
# ==> Now, tempfile batch processed by ftp.

rm -f ${TMPFILE}
# ==> Finally, tempfile deleted (you may wish to copy it to a logfile).


# ==> Exercises:
# ==> ---------
# ==> 1) Add error checking.
# ==> 2) Add bells & whistles.
%%%&&&ftpget.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@func-cmdlinearg.sh@@@!!!************************************************************************************
#!/bin/bash
# func-cmdlinearg.sh
#  调用这个脚本, 并且带一个命令行参数.
#+ 类似于 $0 arg1.


func ()

{
echo "$1"
}

echo "First call to function: no arg passed."
echo "See if command-line arg is seen."
func
# 不行! 命令行参数不可见.

echo "============================================================"
echo
echo "Second call to function: command-line arg passed explicitly."
func $1
# 现在可见了!

exit 0
%%%&&&func-cmdlinearg.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@gcd.sh@@@!!!************************************************************************************
#!/bin/bash
# gcd.sh: 最大公约数
#         使用Euclid的算法

#  两个整数的"最大公约数" (gcd),
#+ 就是两个整数所能够同时整除的最大的数.

#  Euclid算法采用连续除法.
#  在每一次循环中,
#+ 被除数 &lt;---  除数
#+ 除数 &lt;---  余数
#+ 直到 余数 = 0.
#+ 在最后一次循环中, gcd = 被除数.
#
#  关于Euclid算法的更精彩的讨论, 可以到
#+ Jim Loy的站点, http://www.jimloy.com/number/euclids.htm.


# ------------------------------------------------------
# 参数检查
ARGS=2
E_BADARGS=65

if [ $# -ne "$ARGS" ]
then
  echo "Usage: `basename $0` first-number second-number"
  exit $E_BADARGS
fi
# ------------------------------------------------------


gcd ()
{

  dividend=$1                    #  随意赋值.
  divisor=$2                     #+ 在这里, 哪个值给的大都没关系.
                                 #  为什么没关系?

  remainder=1                    #  如果在循环中使用了未初始化的变量,
                                 #+ 那么在第一次循环中,
                                 #+ 它将会产生一个错误消息.

  until [ "$remainder" -eq 0 ]
  do
    let "remainder = $dividend % $divisor"
    dividend=$divisor            # 现在使用两个最小的数来重复.
    divisor=$remainder
  done                           # Euclid的算法

}                                # Last $dividend is the gcd.


gcd $1 $2

echo; echo "GCD of $1 and $2 = $dividend"; echo


# Exercise :
# --------
#  检查传递进来的命令行参数来确保它们都是整数.
#+ 如果不是整数, 那就给出一个适当的错误消息并退出脚本.

exit 0
%%%&&&gcd.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@generate-script.sh@@@!!!************************************************************************************
#!/bin/bash
# generate-script.sh
# 这个脚本的诞生基于Albert Reiner的一个主意.

OUTFILE=generated.sh         # 所产生文件的名字.


# -----------------------------------------------------------
# 'Here document包含了需要产生的脚本的代码.
(
cat <<'EOF'
#!/bin/bash

echo "This is a generated shell script."
#  Note that since we are inside a subshell,
#+ we can't access variables in the "outside" script.

echo "Generated file will be named: $OUTFILE"
#  Above line will not work as normally expected
#+ because parameter expansion has been disabled.
#  Instead, the result is literal output.

a=7
b=3

let "c = $a * $b"
echo "c = $c"

exit 0
EOF
) > $OUTFILE
# -----------------------------------------------------------

#  将'limit string'引用起来将会阻止上边
#+ here document消息体中的变量扩展.
#  这会使得输出文件中的内容保持here document消息体中的原文.

if [ -f "$OUTFILE" ]
then
  chmod 755 $OUTFILE
  # 让所产生的文件具有可执行权限.
else
  echo "Problem in creating file: /"$OUTFILE/""
fi

#  这个方法也可以用来产生
#+ C程序代码, Perl程序代码, Python程序代码, makefile,
#+ 和其他的一些类似的代码.
#  (译者注: 中间一段没译的注释将会被here document打印出来)
exit 0
%%%&&&generate-script.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@getopt-simple.sh@@@!!!************************************************************************************
#!/bin/bash
# getopt-simple.sh
# 作者: Chris Morgan
# 已经经过授权, 可以使用在本书中.


getopt_simple()
{
    echo "getopt_simple()"
    echo "Parameters are '$*'"
    until [ -z "$1" ]
    do
      echo "Processing parameter of: '$1'"
      if [ ${1:0:1} = '/' ]
      then
          tmp=${1:1}               # 去掉开头的'/' . . .
          parameter=${tmp%%=*}     # 提取参数名.
          value=${tmp##*=}         # 提取参数值.
          echo "Parameter: '$parameter', value: '$value'"
          eval $parameter=$value
      fi
      shift
    done
}

# 把所有选项传给函数getopt_simple().
getopt_simple $*

echo "test is '$test'"
echo "test2 is '$test2'"

exit 0

---

sh getopt_example.sh /test=value1 /test2=value2

Parameters are '/test=value1 /test2=value2'
Processing parameter of: '/test=value1'
Parameter: 'test', value: 'value1'
Processing parameter of: '/test2=value2'
Parameter: 'test2', value: 'value2'
test is 'value1'
test2 is 'value2'
%%%&&&getopt-simple.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值