***@@@col-totaler2.sh@@@!!!

************************************************************************************
#!/bin/bash

#  这是"求文件中指定列的总和"脚本的另一个版本,
#+ 这个脚本可以计算目标文件中指定列(此列的内容必须都是数字)的所有数字的和.
#  这个脚本使用了间接引用.

ARGS=2
E_WRONGARGS=65

if [ $# -ne "$ARGS" ] # 检查命令行参数的个数是否合适.
then
   echo "Usage: `basename $0` filename column-number"
   exit $E_WRONGARGS
fi

filename=$1
column_number=$2

#===== 在这一行上边的部分, 与原始脚本是相同的 =====#

# 多行的awk脚本的调用方法为: awk ' ..... '

# awk脚本开始.
# ------------------------------------------------
awk "

{ total += /$${column_number} # 间接引用
}
END {
     print total
     }

" "$filename"
# ------------------------------------------------
# awk脚本结束.

#  间接变量引用避免了在一个内嵌awk脚本中引用shell变量的混乱行为.
#  感谢, Stephane Chazelas.

exit 0
%%%&&&col-totaler2.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@col-totaler3.sh@@@!!!

************************************************************************************
#!/bin/bash

#  这是"求列的和"脚本的另外一个版本(col-totaler.sh)
#+ 那个脚本可以把目标文件中的指定的列上的所有数字全部累加起来,求和.
#  这个版本将把一个变量通过export的形式传递到'awk'中 . . .
#+ 并且把awk脚本放到一个变量中.

ARGS=2
E_WRONGARGS=65

if [ $# -ne "$ARGS" ] # 检查命令行参数的个数.
then
   echo "Usage: `basename $0` filename column-number"
   exit $E_WRONGARGS
fi

filename=$1
column_number=$2

#===== 上边的这部分,与原始脚本完全一样 =====#

export column_number
# 将列号export出来, 这样后边的进程就可用了.

# -----------------------------------------------
awkscript='{ total += $ENVIRON["column_number"] }
END { print total }'
# 是的, 变量可以保存awk脚本.
# -----------------------------------------------

# 现在, 运行这个awk脚本.
awk "$awkscript" "$filename"

# 感谢, Stephane Chazelas.

exit 0
%%%&&&col-totaler3.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@col-totaler.sh@@@!!!

************************************************************************************
#!/bin/bash

# 给目标文件添加(由数字组成的)指定的一列.

ARGS=2
E_WRONGARGS=65

if [ $# -ne "$ARGS" ] # 检查命令行参数个数是否正确.
then
   echo "Usage: `basename $0` filename column-number"
   exit $E_WRONGARGS
fi

filename=$1
column_number=$2

#  将shell变量传递给脚本的awk部分, 需要一点小技巧.
#  一种办法是, 将awk脚本中的Bash脚本变量,
#+ 强引用起来.
#     $'$BASH_SCRIPT_VAR'
#      ^                ^
#  在下面的内嵌awd脚本中, 就会这么做.
#  请参考awk的相关文档来了解更多的细节.

# 多行awk脚本的调用格式为:  awk ' ..... '

# 开始awk脚本.
# -----------------------------
awk '

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

' "$filename"
# -----------------------------
# 结束awk脚本.

#   将shell变量传递给内嵌awk脚本可能是不安全的,
#+  所以Stephane Chazelas提出了下边这种替代方法:
#   ---------------------------------------
#   awk -v column_number="$column_number" '
#   { total += $column_number
#   }
#   END {
#       print total
#   }' "$filename"
#   ---------------------------------------

exit 0
%%%&&&col-totaler.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@commentblock.sh@@@!!!

************************************************************************************
#!/bin/bash
# commentblock.sh

: <<COMMENTBLOCK
echo "This line will not echo."
This is a comment line missing the "#" prefix.
This is another comment line missing the "#" prefix.

&*@!!++=
The above line will cause no error message,
because the Bash interpreter will ignore it.
COMMENTBLOCK

echo "Exit value of above /"COMMENTBLOCK/" is $?."   # 0
# 这里将不会显示任何错误.

#  上边的这种技术当然也可以用来注释掉
#+ 一段正在使用的代码, 如果你有某些特定调试要求的话.
#  这比在每行前边都敲入"#"来得方便的多,
#+ 而且如果你想恢复的话, 还得将添加上的"#"删除掉.

: <<DEBUGXXX
for file in *
do
 cat "$file"
done
DEBUGXXX

exit 0
%%%&&&commentblock.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@connect-stat.sh@@@!!!

************************************************************************************
#!/bin/bash

PROCNAME=pppd        # ppp守护进程
PROCFILENAME=status  # 在这里寻找信息.
NOTCONNECTED=65
INTERVAL=2           # 每2秒刷新一次.

pidno=$( ps ax | grep -v "ps ax" | grep -v grep | grep $PROCNAME | awk '{ print $1 }' )
# 找出'pppd'所对应的进程号, 即'ppp守护进程'.
# 必须过滤掉由搜索本身所产生的进程行.
#
#  然而, 就像Oleg Philon所指出的那样,
#+ 如果使用"pidof"的话, 就会非常简单.
#  pidno=$( pidof $PROCNAME )
#
#  从经验中总结出来的忠告:
#+ 当命令序列变得非常复杂的时候, 一定要找到更简洁的方法.

if [ -z "$pidno" ]   # 如果没有找到匹配的pid, 那么就说明相应的进程没运行.
then
  echo "Not connected."
  exit $NOTCONNECTED
else
  echo "Connected."; echo
fi

while [ true ]       # 死循环, 这里可以改进一下.
do

if [ ! -e "/proc/$pidno/$PROCFILENAME" ]
  # 进程运行时, 相应的"status"文件就会存在.
  then
    echo "Disconnected."
    exit $NOTCONNECTED
  fi

netstat -s | grep "packets received"  # 获得一些连接统计.
netstat -s | grep "packets delivered"

sleep $INTERVAL
  echo; echo

done

exit 0

# 如果你想停止这个脚本, 只能使用Control-C.

#    练习:
#    -----
#    改进这个脚本, 使它可以按"q"键退出.
#    提高这个脚本的用户友好性.
%%%&&&connect-stat.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@continue-n.example@@@!!!

************************************************************************************
# Albert Reiner 给出了一个关于使用"continue N"的例子:
# ---------------------------------------------------------

#  假定我有很多作业需要运行, 这些任务要处理一些数据,
#+ 这些数据保存在某个目录下的文件里, 文件是以预先给定的模式进行命名的.
#+ 有几台机器要访问这个目录,
#+ 我想把工作都分配给这几台不同的机器,
#+ 然后我一般会在每台机器里运行类似下面的代码:

while true
do
  for n in .iso.*
  do
    [ "$n" = ".iso.opts" ] && continue
    beta=${n#.iso.}
    [ -r .Iso.$beta ] && continue
    [ -r .lock.$beta ] && sleep 10 && continue
    lockfile -r0 .lock.$beta || continue
    echo -n "$beta: " `date`
    run-isotherm $beta
    date
    ls -alF .Iso.$beta
    [ -r .Iso.$beta ] && rm -f .lock.$beta
    continue 2
  done
  break
done

#  在我的应用中的某些细节是很特殊的, 尤其是sleep N,
#+ 但是更一般的模式是:

while true
do
  for job in {pattern}
  do
    {job already done or running} && continue
    {mark job as running, do job, mark job as done}
    continue 2
  done
  break        # 而所谓的`sleep 600'只不过是想避免程序太快结束, 而达不到演示效果.
done

#  脚本只有在所有任务都完成之后才会停止运行,
#+ (包括那些运行时新添加的任务).
#+ 通过使用合适的lockfiles,
#+ 可以使几台机器协作运行而不会产生重复的处理,
#+ [在我的这个例子中, 重复处理会使处理时间延长一倍时间, 因此我很想避免这个问题].
#+ 同样, 如果每次都从头开始搜索, 可以由文件名得到处理顺序.
#+ 当然, 还有一种办法, 也可以不使用`continue 2',
#+ 但这样的话, 就不得不检查相同的任务是不是已经被完成过了,
#+ (而我们应该马上来找到下一个要运行的任务)
#+ (在演示的情况中, 检查新任务前我们终止或睡眠了很长一段时间).
#+
%%%&&&continue-n.example&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@continue-nlevel.sh@@@!!!

************************************************************************************
#!/bin/bash
# "continue N" 命令, 将让N层的循环全部被continue.

for outer in I II III IV V           # 外部循环
do
  echo; echo -n "Group $outer: "

# --------------------------------------------------------------------
  for inner in 1 2 3 4 5 6 7 8 9 10  # 内部循环
  do

if [ "$inner" -eq 7 ]
    then
      continue 2  # 在第2层循环上的continue, 也就是"外部循环".
                  # 使用"continue"来替代这句,
                  # 然后看一下一个正常循环的行为.
    fi

echo -n "$inner "  # 7 8 9 10 将不会被echo.
  done 
  # --------------------------------------------------------------------
  # 译者注: 如果在此处添加echo的话, 当然也不会输出.
done

echo; echo

# 练习:
# 在脚本中放入一个有意义的"continue N".

exit 0
%%%&&&continue-nlevel.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@CopyArray.sh@@@!!!

************************************************************************************
#! /bin/bash
# CopyArray.sh
#
# 这个脚本由Michael Zick所编写.
# 已通过作者授权, 可以在本书中使用.

#  如何"通过名字传值&通过名字返回"(译者注: 这里可以理解为C中的"数组指针", 或C++中的"数组引

用")
#+ 或者"建立自己的赋值语句".

CpArray_Mac() {

# 建立赋值命令

echo -n 'eval '
    echo -n "$2"                    # 目的参数名
    echo -n '=( ${'
    echo -n "$1"                    # 原参数名
    echo -n '[@]} )'

# 上边这些语句会构成一条命令.
# 这仅仅是形式上的问题.
}

declare -f CopyArray                # 函数"指针"
CopyArray=CpArray_Mac               # 构造语句

Hype()
{

# 需要连接的数组名为$1.
# (把这个数组与字符串"Really Rocks"结合起来, 形成一个新数组.)
# 并将结果从数组$2中返回.

local -a TMP
    local -a hype=( Really Rocks )

$($CopyArray $1 TMP)
    TMP=( ${TMP[@]} ${hype[@]} )
    $($CopyArray TMP $2)
}

declare -a before=( Advanced Bash Scripting )
declare -a after

echo "Array Before = ${before[@]}"

Hype before after

echo "Array After = ${after[@]}"

# 连接的太多了?

echo "What ${after[@]:3:2}?"

declare -a modest=( ${after[@]:2:1} ${after[@]:3:2} )
#                    ----       子串提取       ----

echo "Array Modest = ${modest[@]}"

# 'before'发生了什么变化么?

echo "Array Before = ${before[@]}"

exit 0
%%%&&&CopyArray.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@copy-cd.sh@@@!!!

************************************************************************************
#!/bin/bash
# copy-cd.sh: copying a data CD

CDROM=/dev/cdrom                           # CD ROM device
OF=/home/bozo/projects/cdimage.iso         # output file
#       /xxxx/xxxxxxx/                     Change to suit your system.
BLOCKSIZE=2048
SPEED=2                                    # May use higher speed if supported.
DEVICE=cdrom
# DEVICE="0,0"    on older versions of cdrecord.

echo; echo "Insert source CD, but do *not* mount it."
echo "Press ENTER when ready. "
read ready                                 # Wait for input, $ready not used.

echo; echo "Copying the source CD to $OF."
echo "This may take a while. Please be patient."

dd if=$CDROM of=$OF bs=$BLOCKSIZE          # Raw device copy.

echo; echo "Remove data CD."
echo "Insert blank CDR."
echo "Press ENTER when ready. "
read ready                                 # Wait for input, $ready not used.

echo "Copying $OF to CDR."

cdrecord -v -isosize speed=$SPEED dev=$DEVICE $OF
# Uses Joerg Schilling's "cdrecord" package (see its docs).
# http://www.fokus.gmd.de/nthp/employees/schilling/cdrecord.html

echo; echo "Done copying $OF to CDR on device $CDROM."

echo "Do you want to erase the image file (y/n)? "  # Probably a huge file.
read answer

case "$answer" in
[yY]) rm -f $OF
      echo "$OF erased."
      ;;
*)    echo "$OF not erased.";;
esac

echo

# Exercise:
# Change the above "case" statement to also accept "yes" and "Yes" as input.

exit 0
%%%&&&copy-cd.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@crypto-quote.sh@@@!!!

************************************************************************************
#!/bin/bash
# crypto-quote.sh: 加密

#  使用单码替换(单一字母替换法)来进行加密.
#  这个脚本的结果与"Crypto Quote"游戏
#+ 的行为很相似.

key=ETAOINSHRDLUBCFGJMQPVWZYXK
# "key"不过是一个乱序的字母表.
# 修改"key"就会修改加密的结果.

# 'cat "$@"' 结构既可以从stdin获得输入, 也可以从文件中获得输入.
# 如果使用stdin, 那么要想结束输入就使用 Control-D.
# 否则就要在命令行上指定文件名.

cat "$@" | tr "a-z" "A-Z" | tr "A-Z" "$key"
#        |   转化为大写   |     加密
# 小写, 大写, 或混合大小写, 都可以正常工作.
# 但是传递进来的非字母字符将不会起任何变化.

# 用下边的语句试试这个脚本:
# "Nothing so needs reforming as other people's habits."
# --Mark Twain
#                                                       
# 输出为:
# "CFPHRCS QF CIIOQ MINFMBRCS EQ FPHIM GIFGUI'Q HETRPQ."
# --BEML PZERC
                                                        
# 解密:
# cat "$@" | tr "$key" "A-Z"
                                                        
                                                        
#  这个简单的密码可以轻易的被一个12岁的小孩
#+ 用铅笔和纸破解.

exit 0

#  练习:
#  -----
#  修改这个脚本, 让它可以用命令行参数
#+ 来决定加密或解密.
%%%&&&crypto-quote.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@csubloop.sh@@@!!!

************************************************************************************
#!/bin/bash
# csubloop.sh: 将循环输出的内容设置到变量中.

variable1=`for i in 1 2 3 4 5
do
  echo -n "$i"                 #  对于在这里的命令替换来说
done`                          #+ 这个'echo'命令是非常关键的.

echo "variable1 = $variable1"  # variable1 = 12345

i=0
variable2=`while [ "$i" -lt 10 ]
do
  echo -n "$i"                 # 再来一个, 'echo'是必需的.
  let "i += 1"                 # 递增.
done`

echo "variable2 = $variable2"  # variable2 = 0123456789

#  这就证明了在一个变量的声明中
#+ 嵌入一个循环是可行的.

exit 0
%%%&&&csubloop.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@c-vars.sh@@@!!!

************************************************************************************
#!/bin/bash
# 使用((...))结构操作一个变量, C语言风格的变量操作.

echo

(( a = 23 ))  # C语言风格的变量赋值, "="两边允许有空格.
echo "a (initial value) = $a"

(( a++ ))     # C语言风格的后置自加.
echo "a (after a++) = $a"

(( a-- ))     # C语言风格的后置自减.
echo "a (after a--) = $a"

(( ++a ))     # C语言风格的前置自加.
echo "a (after ++a) = $a"

(( --a ))     # C语言风格的前置自减.
echo "a (after --a) = $a"

echo

########################################################
#  注意: 就像在C语言中一样, 前置或后置自减操作
#+ 会产生一些不同的副作用.

n=1; let --n && echo "True" || echo "False"  # False
n=1; let n-- && echo "True" || echo "False"  # True

#  感谢, Jeroen Domburg.
########################################################

echo

(( t = a<45?7:11 ))   # C语言风格的三元操作.
echo "If a < 45, then t = 7, else t = 11."
echo "t = $t "        # Yes!

echo

# ------------
# 复活节彩蛋!
# ------------
#  Chet Ramey显然偷偷摸摸的将一些未公开的C语言风格的结构
#+ 引入到了Bash中 (事实上是从ksh中引入的, 这更接近些).
#  在Bash的文档中, Ramey将((...))称为shell算术运算,
#+ 但是它所能做的远远不止于此.
#  不好意思, Chet, 现在秘密被公开了.

# 你也可以参考一些 "for" 和 "while" 循环中使用((...))结构的例子.

# 这些只能够在Bash 2.04或更高版本的Bash上才能运行.

exit 0
%%%&&&c-vars.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@cvt.sh@@@!!!

************************************************************************************
#!/bin/bash
#  cvt.sh:
#  将一个目录下的所有MacPaint格式的图片文件都转换为"pbm"各式的图片文件.

#  使用"netpbm"包中的"macptopbm"程序进行转换,
#+ 这个程序主要是由Brian Henderson(bryanh@giraffe-data.com)来维护的.
#  Netpbm绝大多数Linux发行版的标准套件.

OPERATION=macptopbm
SUFFIX=pbm          # 新的文件名后缀.

if [ -n "$1" ]
then
  directory=$1      # 如果目录名作为参数传递给脚本...
else
  directory=$PWD    # 否则使用当前的工作目录.
fi 
 
#  假定目标目录中的所有文件都是MacPaint格式的图像文件,
#+ 并且都是以".mac"作为文件名后缀.

for file in $directory/*    # 文件名匹配(filename globbing).
do
  filename=${file%.*c}      #  去掉文件名的".mac"后缀
                            #+ ('.*c' 将会匹配
       #+ '.'和'c'之间任意字符串).
  $OPERATION $file > "$filename.$SUFFIX"
                            # 把结果重定向到新的文件中.
  rm -f $file               # 转换后删除原始文件.
  echo "$filename.$SUFFIX"  # 从stdout输出转换后文件的文件名.
done

exit 0

# 练习:
# -----
#  就像它现在的样子, 这个脚本把当前
#+ 目录下的所有文件都转换了.
#  修改这个脚本, 让它只转换以".mac"为后缀名的文件.
%%%&&&cvt.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@days-between.sh@@@!!!

************************************************************************************
#!/bin/bash
# days-between.sh:    Number of days between two dates.
# Usage: ./days-between.sh [M]M/[D]D/YYYY [M]M/[D]D/YYYY
#
# Note: Script modified to account for changes in Bash 2.05b
#+      that closed the loophole permitting large negative
#+      integer return values.

ARGS=2                # Two command line parameters expected.
E_PARAM_ERR=65        # Param error.

REFYR=1600            # Reference year.
CENTURY=100
DIY=365
ADJ_DIY=367           # Adjusted for leap year + fraction.
MIY=12
DIM=31
LEAPCYCLE=4

MAXRETVAL=255         #  Largest permissable
                      #+ positive return value from a function.

diff=                 # Declare global variable for date difference.
value=                # Declare global variable for absolute value.
day=                  # Declare globals for day, month, year.
month=
year=

Param_Error ()        # Command line parameters wrong.
{
  echo "Usage: `basename $0` [M]M/[D]D/YYYY [M]M/[D]D/YYYY"
  echo "       (date must be after 1/3/1600)"
  exit $E_PARAM_ERR
}

Parse_Date ()                 # Parse date from command line params.
{
  month=${1%%/**}
  dm=${1%/**}                 # Day and month.
  day=${dm#*/}
  let "year = `basename $1`"  # Not a filename, but works just the same.
}

check_date ()                 # Checks for invalid date(s) passed.
{
  [ "$day" -gt "$DIM" ] || [ "$month" -gt "$MIY" ] || [ "$year" -lt "$REFYR" ] &&

Param_Error
  # Exit script on bad value(s).
  # Uses "or-list / and-list".
  #
  # Exercise: Implement more rigorous date checking.
}

strip_leading_zero () #  Better to strip possible leading zero(s)
{                     #+ from day and/or month
  return ${1#0}       #+ since otherwise Bash will interpret them
}                     #+ as octal values (POSIX.2, sect 2.9.2.1).

day_index ()          # Gauss' Formula:
{                     # Days from Jan. 3, 1600 to date passed as param.

day=$1
  month=$2
  year=$3

let "month = $month - 2"
  if [ "$month" -le 0 ]
  then
    let "month += 12"
    let "year -= 1"
  fi

let "year -= $REFYR"
  let "indexyr = $year / $CENTURY"

let "Days = $DIY*$year + $year/$LEAPCYCLE - $indexyr + $indexyr/$LEAPCYCLE +

$ADJ_DIY*$month/$MIY + $day - $DIM"
  #  For an in-depth explanation of this algorithm, see
  #+ http://home.t-online.de/home/berndt.schwerdtfeger/cal.htm

echo $Days

}

calculate_difference ()            # Difference between to day indices.
{
  let "diff = $1 - $2"             # Global variable.
}

abs ()                             #  Absolute value
{                                  #  Uses global "value" variable.
  if [ "$1" -lt 0 ]                #  If negative
  then                             #+ then
    let "value = 0 - $1"           #+ change sign,
  else                             #+ else
    let "value = $1"               #+ leave it alone.
  fi
}

if [ $# -ne "$ARGS" ]              # Require two command line params.
then
  Param_Error
fi

Parse_Date $1
check_date $day $month $year       #  See if valid date.

strip_leading_zero $day            #  Remove any leading zeroes
day=$?                             #+ on day and/or month.
strip_leading_zero $month
month=$?

let "date1 = `day_index $day $month $year`"

Parse_Date $2
check_date $day $month $year

strip_leading_zero $day
day=$?
strip_leading_zero $month
month=$?

date2=$(day_index $day $month $year) # Command substitution.

calculate_difference $date1 $date2

abs $diff                            # Make sure it's positive.
diff=$value

echo $diff

exit 0
#  Compare this script with
#+ the implementation of Gauss' Formula in a C program at:
#+    http://buschencrew.hypermart.net/software/datedif
%%%&&&days-between.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@dd-keypress.sh@@@!!!

************************************************************************************
#!/bin/bash
# dd-keypress.sh: 记录按键, 不需要按回车.

keypresses=4                      # 记录按键的个数.

old_tty_setting=$(stty -g)        # 保存旧的终端设置.

echo "Press $keypresses keys."
stty -icanon -echo                # 禁用标准模式.
                                  # 禁用本地echo.
keys=$(dd bs=1 count=$keypresses 2> /dev/null)
# 如果不指定输入文件的话, 'dd'使用标准输入.

stty "$old_tty_setting"           # 恢复旧的终端设置.

echo "You pressed the /"$keys/" keys."

# 感谢Stephane Chazelas, 演示了这种方法.
exit 0
%%%&&&dd-keypress.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@dereference.sh@@@!!!

************************************************************************************
#!/bin/bash
# dereference.sh
# 对一个传递给函数的参数进行解除引用的操作.
# 此脚本由Bruce W. Clare所编写.

dereference ()
{
     y=/$"$1"   # 变量名.
     echo $y    # $Junk

x=`eval "expr /"$y/" "`
     echo $1=$x
     eval "$1=/"Some Different Text /""  # 赋新值.
}

Junk="Some Text"
echo $Junk "before"    # Some Text before

dereference Junk
echo $Junk "after"     # Some Different Text after

exit 0
%%%&&&dereference.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@de-rpm.sh@@@!!!

************************************************************************************
#!/bin/bash
# de-rpm.sh: 解包一个'rpm'归档文件

: ${1?"Usage: `basename $0` target-file"}
# 必须指定'rpm'归档文件名作为参数.

TEMPFILE=$$.cpio                         # Tempfile必须是一个"唯一"的名字.
                                         # $$是这个脚本的进程ID.
                                                                                
rpm2cpio < $1 > $TEMPFILE                # 将rpm归档文件转换为cpio归档文件.
cpio --make-directories -F $TEMPFILE -i  # 解包cpio归档文件.
rm -f $TEMPFILE                          # 删除cpio归档文件.

exit 0

#  练习:
#  添加一些代码来检查    1) "target-file"是否存在
#+                       2) 这个文件是否是一个rpm归档文件.
#  暗示:                    分析'file'命令的输出.
%%%&&&de-rpm.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@dev-tcp.sh@@@!!!

************************************************************************************
#!/bin/bash
# dev-tcp.sh: 利用/dev/tcp重定向来检查Internet连接.

# 本脚本由Troy Engel编写.
# 经过授权在本书中使用.
 
TCP_HOST=www.dns-diy.com   # 一个已知的对垃圾邮件友好的ISP.
TCP_PORT=80                # 端口80是http.
 
# 尝试连接. (有些像'ping' . . .)
echo "HEAD / HTTP/1.0" >/dev/tcp/${TCP_HOST}/${TCP_PORT}
MYEXIT=$?

: &lt;&lt;EXPLANATION
If bash was compiled with --enable-net-redirections, it has the capability of
using a special character device for both TCP and UDP redirections. These
redirections are used identically as STDIN/STDOUT/STDERR. The device entries
are 30,36 for /dev/tcp:

mknod /dev/tcp c 30 36

>From the bash reference:
/dev/tcp/host/port
    If host is a valid hostname or Internet address, and port is an integer
port number or service name, Bash attempts to open a TCP connection to the
corresponding socket.
EXPLANATION

if [ "X$MYEXIT" = "X0" ]; then
  echo "Connection successful. Exit code: $MYEXIT"
else
  echo "Connection unsuccessful. Exit code: $MYEXIT"
fi

exit $MYEXIT
%%%&&&dev-tcp.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@dialog.sh@@@!!!

************************************************************************************
#!/bin/bash
# dialog.sh: 使用'gdialog'窗口部件.
# 必须在你的系统上安装'gdialog'才能运行这个脚本.
# 版本1.1 (04/05/05最后修正)

# 这个脚本的灵感来源于下面的文章.
#     "Scripting for X Productivity," by Marco Fioretti,
#      LINUX JOURNAL, Issue 113, September 2003, pp. 86-9.
# 感谢你们, 所有的LINUX JOURNAL好人.

# 在对话框窗口中的输入错误.
E_INPUT=65
# 输入窗口的显示尺寸.
HEIGHT=50
WIDTH=60

# 输出文件名(由脚本名构造).
OUTFILE=$0.output

# 将脚本的内容显示到文本窗口中.
gdialog --title "Displaying: $0" --textbox $0 $HEIGHT $WIDTH

# 现在, 我们将输入保存到文件中.
echo -n "VARIABLE=" > $OUTFILE
gdialog --title "User Input" --inputbox "Enter variable, please:" /
$HEIGHT $WIDTH 2>> $OUTFILE

if [ "$?" -eq 0 ]
# 检查退出状态码, 是一个好习惯.
then
  echo "Executed /"dialog box/" without errors."
else
  echo "Error(s) in /"dialog box/" execution."
        # 或者, 点"Cancel"按钮, 而不是"OK".
  rm $OUTFILE
  exit $E_INPUT
fi

# 现在, 我们将重新获得并显示保存的变量.
. $OUTFILE   # 'Source'(执行)保存的文件.
echo "The variable input in the /"input box/" was: "$VARIABLE""

rm $OUTFILE  # 清除临时文件.
             # 某些应用可能需要保留这个文件.

exit $?
%%%&&&dialog.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@dict-lookup.sh@@@!!!

************************************************************************************
#!/bin/bash
# dict-lookup.sh

#  这个脚本在1913年的韦氏词典中查找定义.
#  这本公共词典可以通过不同的
#+ 站点来下载,包括
#+ Project Gutenberg (http://www.gutenberg.org/etext/247).
#                                                                 
#  在使用本脚本之前,
#+ 先要将这本字典由DOS格式转换为UNIX格式(只以LF作为行结束符).
#  将这个文件存储为纯文本形式, 并且保证是未压缩的ASCII格式.
#  将DEFAULT_DICTFILE变量以path/filename形式设置好.

E_BADARGS=65
MAXCONTEXTLINES=50                        # 显示的最大行数.
DEFAULT_DICTFILE="/usr/share/dict/webster1913-dict.txt"
                                          # 默认的路径和文件名.
                                          # 在必要的时候可以进行修改.
#  注意:
#  -----
#  这个特定的1913年版的韦氏词典
#+ 在每个入口都是以大写字母开头的
#+ (剩余的字符都是小写).
#  只有每部分的第一行是以这种形式开始的,
#+ 这也就是为什么搜索算法是下边的这个样子.

if [[ -z $(echo "$1" | sed -n '/^[A-Z]/p') ]]
#  必须指定一个要查找的单词,
#+ 并且这个单词必须以大写字母开头.
then
  echo "Usage: `basename $0` Word-to-define [dictionary-file]"
  echo
  echo "Note: Word to look up must start with capital letter,"
  echo "with the rest of the word in lowercase."
  echo "--------------------------------------------"
  echo "Examples: Abandon, Dictionary, Marking, etc."
  exit $E_BADARGS
fi

if [ -z "$2" ]                            #  也可以指定不同的词典
                                          #+ 作为这个脚本的第2个参数传递进来.
then
  dictfile=$DEFAULT_DICTFILE
else
  dictfile="$2"
fi

# ---------------------------------------------------------
Definition=$(fgrep -A $MAXCONTEXTLINES "$1 //" "$dictfile")
#                                   以 "Word /..." 这种形式定义
#                                                              
#  当然, 即使搜索一个特别大的文本文件的时候
#+ "fgrep"也是足够快的.

# 现在, 剪掉定义块.

echo "$Definition" |
sed -n '1,/^[A-Z]/p' |
#  从输出的第一行
#+ 打印到下一部分的第一行.
sed '$d' | sed '$d'
#  删除输出的最后两行
#+ (空行和下一部分的第一行).
# ---------------------------------------------------------

exit 0

# 练习:
# -----
# 1)  修改这个脚本, 让它具备能够处理任何字符形式的输入
#   + (大写, 小写, 或大小写混合), 然后将其转换为
#   + 能够处理的统一形式.
#                                                      
# 2)  将这个脚本转化为一个GUI应用,
#   + 使用一些比如像"gdialog"的东西 .  .  .
#     这样的话, 脚本将不再从命令行中
#   + 取得这些参数.
#                                                      
# 3)  修改这个脚本让它具备能够分析另外一个
#   + 公共词典的能力, 比如 U.S. Census Bureau Gazetteer.
%%%&&&dict-lookup.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@directory-info.sh@@@!!!

************************************************************************************
#! /bin/bash
# directory-info.sh
# Parses and lists directory information.

# NOTE: Change lines 273 and 353 per "README" file.

# Michael Zick is the author of this script.
# Used here with his permission.

# Controls
# If overridden by command arguments, they must be in the order:
#   Arg1: "Descriptor Directory"
#   Arg2: "Exclude Paths"
#   Arg3: "Exclude Directories"
#
# Environment Settings override Defaults.
# Command arguments override Environment Settings.

# Default location for content addressed file descriptors.
MD5UCFS=${1:-${MD5UCFS:-'/tmpfs/ucfs'}}

# Directory paths never to list or enter
declare -a /
  EXCLUDE_PATHS=${2:-${EXCLUDE_PATHS:-'(/proc /dev /devfs /tmpfs)'}}

# Directories never to list or enter
declare -a /
  EXCLUDE_DIRS=${3:-${EXCLUDE_DIRS:-'(ucfs lost+found tmp wtmp)'}}

# Files never to list or enter
declare -a /
  EXCLUDE_FILES=${3:-${EXCLUDE_FILES:-'(core "Name with Spaces")'}}

# Here document used as a comment block.
: &lt;&lt;LSfieldsDoc
# # # # # List Filesystem Directory Information # # # # #
#
# ListDirectory "FileGlob" "Field-Array-Name"
# or
# ListDirectory -of "FileGlob" "Field-Array-Filename"
# '-of' meaning 'output to filename'
# # # # #

String format description based on: ls (GNU fileutils) version 4.0.36

Produces a line (or more) formatted:
inode permissions hard-links owner group ...
32736 -rw-------    1 mszick   mszick

size    day month date hh:mm:ss year path
2756608 Sun Apr 20 08:53:06 2003 /home/mszick/core

Unless it is formatted:
inode permissions hard-links owner group ...
266705 crw-rw----    1    root  uucp

major minor day month date hh:mm:ss year path
4,  68 Sun Apr 20 09:27:33 2003 /dev/ttyS4
NOTE: that pesky comma after the major number

NOTE: the 'path' may be multiple fields:
/home/mszick/core
/proc/982/fd/0 -> /dev/null
/proc/982/fd/1 -> /home/mszick/.xsession-errors
/proc/982/fd/13 -> /tmp/tmpfZVVOCs (deleted)
/proc/982/fd/7 -> /tmp/kde-mszick/ksycoca
/proc/982/fd/8 -> socket:[11586]
/proc/982/fd/9 -> pipe:[11588]

If that isn't enough to keep your parser guessing,
either or both of the path components may be relative:
../Built-Shared -> Built-Static
../linux-2.4.20.tar.bz2 -> ../../../SRCS/linux-2.4.20.tar.bz2

The first character of the 11 (10?) character permissions field:
's' Socket
'd' Directory
'b' Block device
'c' Character device
'l' Symbolic link
NOTE: Hard links not marked - test for identical inode numbers
on identical filesystems.
All information about hard linked files are shared, except
for the names and the name's location in the directory system.
NOTE: A "Hard link" is known as a "File Alias" on some systems.
'-' An undistingushed file

Followed by three groups of letters for: User, Group, Others
Character 1: '-' Not readable; 'r' Readable
Character 2: '-' Not writable; 'w' Writable
Character 3, User and Group: Combined execute and special
'-' Not Executable, Not Special
'x' Executable, Not Special
's' Executable, Special
'S' Not Executable, Special
Character 3, Others: Combined execute and sticky (tacky?)
'-' Not Executable, Not Tacky
'x' Executable, Not Tacky
't' Executable, Tacky
'T' Not Executable, Tacky

Followed by an access indicator
Haven't tested this one, it may be the eleventh character
or it may generate another field
' ' No alternate access
'+' Alternate access
LSfieldsDoc

ListDirectory()
{
 local -a T
 local -i of=0  # Default return in variable
# OLD_IFS=$IFS  # Using BASH default ' /t/n'

case "$#" in
 3) case "$1" in
  -of) of=1 ; shift ;;
   * ) return 1 ;;
  esac ;;
 2) : ;;  # Poor man's "continue"
 *) return 1 ;;
 esac

# NOTE: the (ls) command is NOT quoted (")
 T=( $(ls --inode --ignore-backups --almost-all --directory /
 --full-time --color=none --time=status --sort=none /
 --format=long $1) )

case $of in
 # Assign T back to the array whose name was passed as $2
  0) eval $2=/( /"/$/{T/[@/]/}/" /) ;;
 # Write T into filename passed as $2
  1) echo "${T[@]}" > "$2" ;;
 esac
 return 0
   }

# # # # # Is that string a legal number? # # # # #
#
# IsNumber "Var"
# # # # # There has to be a better way, sigh...

IsNumber()
{
 local -i int
 if [ $# -eq 0 ]
 then
  return 1
 else
  (let int=$1)  2>/dev/null
  return $? # Exit status of the let thread
 fi
}

# # # # # Index Filesystem Directory Information # # # # #
#
# IndexList "Field-Array-Name" "Index-Array-Name"
# or
# IndexList -if Field-Array-Filename Index-Array-Name
# IndexList -of Field-Array-Name Index-Array-Filename
# IndexList -if -of Field-Array-Filename Index-Array-Filename
# # # # #

: &lt;&lt;IndexListDoc
Walk an array of directory fields produced by ListDirectory

Having suppressed the line breaks in an otherwise line oriented
report, build an index to the array element which starts each line.

Each line gets two index entries, the first element of each line
(inode) and the element that holds the pathname of the file.

The first index entry pair (Line-Number==0) are informational:
Index-Array-Name[0] : Number of "Lines" indexed
Index-Array-Name[1] : "Current Line" pointer into Index-Array-Name

The following index pairs (if any) hold element indexes into
the Field-Array-Name per:
Index-Array-Name[Line-Number * 2] : The "inode" field element.
NOTE: This distance may be either +11 or +12 elements.
Index-Array-Name[(Line-Number * 2) + 1] : The "pathname" element.
NOTE: This distance may be a variable number of elements.
Next line index pair for Line-Number+1.
IndexListDoc

IndexList()
{
 local -a LIST   # Local of listname passed
 local -a -i INDEX=( 0 0 ) # Local of index to return
 local -i Lidx Lcnt
 local -i if=0 of=0  # Default to variable names

case "$#" in   # Simplistic option testing
  0) return 1 ;;
  1) return 1 ;;
  2) : ;;   # Poor man's continue
  3) case "$1" in
   -if) if=1 ;;
   -of) of=1 ;;
    * ) return 1 ;;
     esac ; shift ;;
  4) if=1 ; of=1 ; shift ; shift ;;
  *) return 1
 esac

# Make local copy of list
 case "$if" in
  0) eval LIST=/( /"/$/{$1/[@/]/}/" /) ;;
  1) LIST=( $(cat $1) ) ;;
 esac

# Grok (grope?) the array
 Lcnt=${#LIST[@]}
 Lidx=0
 until (( Lidx >= Lcnt ))
 do
 if IsNumber ${LIST[$Lidx]}
 then
  local -i inode name
  local ft
  inode=Lidx
  local m=${LIST[$Lidx+2]} # Hard Links field
  ft=${LIST[$Lidx+1]:0:1}  # Fast-Stat
  case $ft in
  b) ((Lidx+=12)) ;;  # Block device
  c) ((Lidx+=12)) ;;  # Character device
  *) ((Lidx+=11)) ;;  # Anything else
  esac
  name=Lidx
  case $ft in
  -) ((Lidx+=1)) ;;  # The easy one
  b) ((Lidx+=1)) ;;  # Block device
  c) ((Lidx+=1)) ;;  # Character device
  d) ((Lidx+=1)) ;;  # The other easy one
  l) ((Lidx+=3)) ;;  # At LEAST two more fields
#  A little more elegance here would handle pipes,
#+ sockets, deleted files - later.
  *) until IsNumber ${LIST[$Lidx]} || ((Lidx >= Lcnt))
   do
    ((Lidx+=1))
   done
   ;;   # Not required
  esac
  INDEX[${#INDEX[*]}]=$inode
  INDEX[${#INDEX[*]}]=$name
  INDEX[0]=${INDEX[0]}+1  # One more "line" found
# echo "Line: ${INDEX[0]} Type: $ft Links: $m Inode: /
# ${LIST[$inode]} Name: ${LIST[$name]}"

else
  ((Lidx+=1))
 fi
 done
 case "$of" in
  0) eval $2=/( /"/$/{INDEX/[@/]/}/" /) ;;
  1) echo "${INDEX[@]}" > "$2" ;;
 esac
 return 0    # What could go wrong?
}

# # # # # Content Identify File # # # # #
#
# DigestFile Input-Array-Name Digest-Array-Name
# or
# DigestFile -if Input-FileName Digest-Array-Name
# # # # #

# Here document used as a comment block.
: &lt;&lt;DigestFilesDoc

The key (no pun intended) to a Unified Content File System (UCFS)
is to distinguish the files in the system based on their content.
Distinguishing files by their name is just, so, 20th Century.

The content is distinguished by computing a checksum of that content.
This version uses the md5sum program to generate a 128 bit checksum
representative of the file's contents.
There is a chance that two files having different content might
generate the same checksum using md5sum (or any checksum).  Should
that become a problem, then the use of md5sum can be replace by a
cyrptographic signature.  But until then...

The md5sum program is documented as outputting three fields (and it
does), but when read it appears as two fields (array elements).  This
is caused by the lack of whitespace between the second and third field.
So this function gropes the md5sum output and returns:
 [0] 32 character checksum in hexidecimal (UCFS filename)
 [1] Single character: ' ' text file, '*' binary file
 [2] Filesystem (20th Century Style) name
 Note: That name may be the character '-' indicating STDIN read.

DigestFilesDoc

DigestFile()
{
 local if=0  # Default, variable name
 local -a T1 T2

case "$#" in
 3) case "$1" in
  -if) if=1 ; shift ;;
   * ) return 1 ;;
  esac ;;
 2) : ;;  # Poor man's "continue"
 *) return 1 ;;
 esac

case $if in
 0) eval T1=/( /"/$/{$1/[@/]/}/" /)
    T2=( $(echo ${T1[@]} | md5sum -) )
    ;;
 1) T2=( $(md5sum $1) )
    ;;
 esac

case ${#T2[@]} in
 0) return 1 ;;
 1) return 1 ;;
 2) case ${T2[1]:0:1} in  # SanScrit-2.0.5
    /*) T2[${#T2[@]}]=${T2[1]:1}
        T2[1]=/*
        ;;
     *) T2[${#T2[@]}]=${T2[1]}
        T2[1]=" "
        ;;
    esac
    ;;
 3) : ;; # Assume it worked
 *) return 1 ;;
 esac

local -i len=${#T2[0]}
 if [ $len -ne 32 ] ; then return 1 ; fi
 eval $2=/( /"/$/{T2/[@/]/}/" /)
}

# # # # # Locate File # # # # #
#
# LocateFile [-l] FileName Location-Array-Name
# or
# LocateFile [-l] -of FileName Location-Array-FileName
# # # # #

# A file location is Filesystem-id and inode-number

# Here document used as a comment block.
: &lt;&lt;StatFieldsDoc
 Based on stat, version 2.2
 stat -t and stat -lt fields
 [0] name
 [1] Total size
  File - number of bytes
  Symbolic link - string length of pathname
 [2] Number of (512 byte) blocks allocated
 [3] File type and Access rights (hex)
 [4] User ID of owner
 [5] Group ID of owner
 [6] Device number
 [7] Inode number
 [8] Number of hard links
 [9] Device type (if inode device) Major
 [10] Device type (if inode device) Minor
 [11] Time of last access
  May be disabled in 'mount' with noatime
  atime of files changed by exec, read, pipe, utime, mknod (mmap?)
  atime of directories changed by addition/deletion of files
 [12] Time of last modification
  mtime of files changed by write, truncate, utime, mknod
  mtime of directories changed by addtition/deletion of files
 [13] Time of last change
  ctime reflects time of changed inode information (owner, group
  permissions, link count
-*-*- Per:
 Return code: 0
 Size of array: 14
 Contents of array
 Element 0: /home/mszick
 Element 1: 4096
 Element 2: 8
 Element 3: 41e8
 Element 4: 500
 Element 5: 500
 Element 6: 303
 Element 7: 32385
 Element 8: 22
 Element 9: 0
 Element 10: 0
 Element 11: 1051221030
 Element 12: 1051214068
 Element 13: 1051214068

For a link in the form of linkname -> realname
 stat -t  linkname returns the linkname (link) information
 stat -lt linkname returns the realname information

stat -tf and stat -ltf fields
 [0] name
 [1] ID-0?  # Maybe someday, but Linux stat structure
 [2] ID-0?  # does not have either LABEL nor UUID
    # fields, currently information must come
    # from file-system specific utilities
 These will be munged into:
 [1] UUID if possible
 [2] Volume Label if possible
 Note: 'mount -l' does return the label and could return the UUID

[3] Maximum length of filenames
 [4] Filesystem type
 [5] Total blocks in the filesystem
 [6] Free blocks
 [7] Free blocks for non-root user(s)
 [8] Block size of the filesystem
 [9] Total inodes
 [10] Free inodes

-*-*- Per:
 Return code: 0
 Size of array: 11
 Contents of array
 Element 0: /home/mszick
 Element 1: 0
 Element 2: 0
 Element 3: 255
 Element 4: ef53
 Element 5: 2581445
 Element 6: 2277180
 Element 7: 2146050
 Element 8: 4096
 Element 9: 1311552
 Element 10: 1276425

StatFieldsDoc

# LocateFile [-l] FileName Location-Array-Name
# LocateFile [-l] -of FileName Location-Array-FileName

LocateFile()
{
 local -a LOC LOC1 LOC2
 local lk="" of=0

case "$#" in
 0) return 1 ;;
 1) return 1 ;;
 2) : ;;
 *) while (( "$#" > 2 ))
    do
       case "$1" in
        -l) lk=-1 ;;
       -of) of=1 ;;
         *) return 1 ;;
       esac
    shift
           done ;;
 esac

# More Sanscrit-2.0.5
      # LOC1=( $(stat -t $lk $1) )
      # LOC2=( $(stat -tf $lk $1) )
      # Uncomment above two lines if system has "stat" command installed.
 LOC=( ${LOC1[@]:0:1} ${LOC1[@]:3:11}
       ${LOC2[@]:1:2} ${LOC2[@]:4:1} )

case "$of" in
  0) eval $2=/( /"/$/{LOC/[@/]/}/" /) ;;
  1) echo "${LOC[@]}" > "$2" ;;
 esac
 return 0
# Which yields (if you are lucky, and have "stat" installed)
# -*-*- Location Discriptor -*-*-
# Return code: 0
# Size of array: 15
# Contents of array
# Element 0: /home/mszick  20th Century name
# Element 1: 41e8   Type and Permissions
# Element 2: 500   User
# Element 3: 500   Group
# Element 4: 303   Device
# Element 5: 32385  inode
# Element 6: 22   Link count
# Element 7: 0   Device Major
# Element 8: 0   Device Minor
# Element 9: 1051224608  Last Access
# Element 10: 1051214068  Last Modify
# Element 11: 1051214068  Last Status
# Element 12: 0   UUID (to be)
# Element 13: 0   Volume Label (to be)
# Element 14: ef53  Filesystem type
}

# And then there was some test code

ListArray() # ListArray Name
{
 local -a Ta

eval Ta=/( /"/$/{$1/[@/]/}/" /)
 echo
 echo "-*-*- List of Array -*-*-"
 echo "Size of array $1: ${#Ta[*]}"
 echo "Contents of array $1:"
 for (( i=0 ; i<${#Ta[*]} ; i++ ))
 do
     echo -e "/tElement $i: ${Ta[$i]}"
 done
 return 0
}

declare -a CUR_DIR
# For small arrays
ListDirectory "${PWD}" CUR_DIR
ListArray CUR_DIR

declare -a DIR_DIG
DigestFile CUR_DIR DIR_DIG
echo "The new /"name/" (checksum) for ${CUR_DIR[9]} is ${DIR_DIG[0]}"

declare -a DIR_ENT
# BIG_DIR # For really big arrays - use a temporary file in ramdisk
# BIG-DIR # ListDirectory -of "${CUR_DIR[11]}/*" "/tmpfs/junk2"
ListDirectory "${CUR_DIR[11]}/*" DIR_ENT

declare -a DIR_IDX
# BIG-DIR # IndexList -if "/tmpfs/junk2" DIR_IDX
IndexList DIR_ENT DIR_IDX

declare -a IDX_DIG
# BIG-DIR # DIR_ENT=( $(cat /tmpfs/junk2) )
# BIG-DIR # DigestFile -if /tmpfs/junk2 IDX_DIG
DigestFile DIR_ENT IDX_DIG
# Small (should) be able to parallize IndexList & DigestFile
# Large (should) be able to parallize IndexList & DigestFile & the assignment
echo "The /"name/" (checksum) for the contents of ${PWD} is ${IDX_DIG[0]}"

declare -a FILE_LOC
LocateFile ${PWD} FILE_LOC
ListArray FILE_LOC

exit 0
%%%&&&directory-info.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@Draw-box.sh@@@!!!

************************************************************************************
#!/bin/bash
# Draw-box.sh: 使用ASCII字符画一个盒子.

# 由Stefano Palmeri编写, 本书作者做了少量修改.
# 经过授权, 可以在本书中使用.

######################################################################
###  draw_box函数注释  ###

#  "draw_box"函数可以让用户
#+ 在终端上画一个盒子.
#
#  用法: draw_box ROW COLUMN HEIGHT WIDTH [COLOR]
#  ROW和COLUMN用来定位你想要
#+ 画的盒子的左上角.
#  ROW和COLUMN必须大于0,
#+ 并且要小于当前终端的尺寸.
#  HEIGHT是盒子的行数, 并且必须 >0 .
#  HEIGHT + ROW 必须 <= 终端的高度.
#  WIDTH是盒子的列数, 必须 >0 .
#  WIDTH + COLUMN 必须 <= 终端的宽度.
#
# 例如: 如果你的终端尺寸为20x80,
#  draw_box 2 3 10 45 是合法的
#  draw_box 2 3 19 45 的HEIGHT是错的 (19+2 > 20)
#  draw_box 2 3 18 78 的WIDTH是错的 (78+3 > 80)
#
#  COLOR是盒子边框的颜色.
#  这是第5个参数, 并且是可选的.
#  0=黑 1=红 2=绿 3=棕褐 4=蓝 5=紫 6=青 7=白.
#  如果你传递给函数的参数错误,
#+ 它将会退出, 并返回65,
#+ 不会有消息打印到stderr上.
#
#  开始画盒子之前, 会清屏.
#  函数内不包含清屏命令.
#  这样就允许用户画多个盒子, 甚至可以叠加多个盒子.

###  draw_box函数注释结束  ###
######################################################################

draw_box(){

#=============#
HORZ="-"
VERT="|"
CORNER_CHAR="+"

MINARGS=4
E_BADARGS=65
#=============#

if [ $# -lt "$MINARGS" ]; then                 # 如果参数小于4, 退出.
    exit $E_BADARGS
fi

# 找出参数中非数字的字符.
# 还有其他更好的方法么(留给读者作为练习?).
if echo $@ | tr -d [:blank:] | tr -d [:digit:] | grep . &> /dev/null; then
   exit $E_BADARGS
fi

BOX_HEIGHT=`expr $3 - 1`   #  必须-1, 因为边角的"+"是
BOX_WIDTH=`expr $4 - 1`    #+ 高和宽共有的部分.
T_ROWS=`tput lines`        #  定义当前终端的
T_COLS=`tput cols`         #+ 长和宽的尺寸.
        
if [ $1 -lt 1 ] || [ $1 -gt $T_ROWS ]; then    #  开始检查参数
   exit $E_BADARGS                             #+ 是否正确.
fi
if [ $2 -lt 1 ] || [ $2 -gt $T_COLS ]; then
   exit $E_BADARGS
fi
if [ `expr $1 + $BOX_HEIGHT + 1` -gt $T_ROWS ]; then
   exit $E_BADARGS
fi
if [ `expr $2 + $BOX_WIDTH + 1` -gt $T_COLS ]; then
   exit $E_BADARGS
fi
if [ $3 -lt 1 ] || [ $4 -lt 1 ]; then
   exit $E_BADARGS
fi                                 # 参数检查结束.

plot_char(){                       # 函数内的函数.
   echo -e "/E[${1};${2}H"$3
}

echo -ne "/E[3${5}m"               # 如果定义了, 就设置盒子边框的颜色.

# 开始画盒子

count=1                                         #  使用plot_char函数
for (( r=$1; count<=$BOX_HEIGHT; r++)); do      #+ 画垂直线.
  plot_char $r $2 $VERT
  let count=count+1
done

count=1
c=`expr $2 + $BOX_WIDTH`
for (( r=$1; count<=$BOX_HEIGHT; r++)); do
  plot_char $r $c $VERT
  let count=count+1
done

count=1                                        #  使用plot_char函数
for (( c=$2; count<=$BOX_WIDTH; c++)); do      #+ 画水平线.
  plot_char $1 $c $HORZ
  let count=count+1
done

count=1
r=`expr $1 + $BOX_HEIGHT`
for (( c=$2; count<=$BOX_WIDTH; c++)); do
  plot_char $r $c $HORZ
  let count=count+1
done

plot_char $1 $2 $CORNER_CHAR                   # 画盒子的角.
plot_char $1 `expr $2 + $BOX_WIDTH` +
plot_char `expr $1 + $BOX_HEIGHT` $2 +
plot_char `expr $1 + $BOX_HEIGHT` `expr $2 + $BOX_WIDTH` +

echo -ne "/E[0m"             #  恢复原来的颜色.

P_ROWS=`expr $T_ROWS - 1`    #  在终端的底部打印提示符.

echo -e "/E[${P_ROWS};1H"
}

# 现在, 让我们开始画盒子吧.
clear                       # 清屏.
R=2      # 行
C=3      # 列
H=10     # 高
W=45     # 宽
col=1    # 颜色(红)
draw_box $R $C $H $W $col   # 画盒子.

exit 0

# 练习:
# -----
# 添加一个选项, 用来支持可以在所画的盒子中打印文本.
%%%&&&Draw-box.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@Du.sh@@@!!!

************************************************************************************
#!/bin/bash
# Du.sh: DOS到UNIX文本文件的转换.

E_WRONGARGS=65

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

NEWFILENAME=$1.unx

CR='/015'  # 回车.
           # 015是8进制的ASCII码的回车.
           # DOS中文本文件的行结束符是CR-LF.
           # UNIX中文本文件的行结束符只是LF.

tr -d $CR &lt; $1 &gt; $NEWFILENAME
# 删除回车并且写到新文件中.

echo "Original DOS text file is /"$1/"."
echo "Converted UNIX text file is /"$NEWFILENAME/"."

exit 0

# 练习:
# -----
# 修改上边的脚本完成从UNIX到DOS的转换.
%%%&&&Du.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@embedded-arrays.sh@@@!!!

************************************************************************************
#!/bin/bash
# embedded-arrays.sh
# 嵌套数组和间接引用.

# 本脚本由Dennis Leeuw编写.
# 经过授权, 在本书中使用.
# 本书作者做了少许修改.

ARRAY1=(
        VAR1_1=value11
        VAR1_2=value12
        VAR1_3=value13
)

ARRAY2=(
        VARIABLE="test"
        STRING="VAR1=value1 VAR2=value2 VAR3=value3"
        ARRAY21=${ARRAY1[*]}
)       # 将ARRAY1嵌套到这个数组中.

function print () {
        OLD_IFS="$IFS"
        IFS=$'/n'       #  这么做是为了每行
                        #+ 只打印一个数组元素.
        TEST1="ARRAY2[*]"
        local ${!TEST1} # 删除这一行, 看看会发生什么?
        #  间接引用.
 #  这使得$TEST1
 #+ 只能够在函数内被访问.

#  让我们看看还能干点什么.
        echo
        echo "/$TEST1 = $TEST1"       #  仅仅是变量名字.
        echo; echo
        echo "{/$TEST1} = ${!TEST1}"  #  变量内容.
                                      #  这就是
                                      #+ 间接引用的作用.
        echo
        echo "-------------------------------------------"; echo
        echo

# 打印变量
        echo "Variable VARIABLE: $VARIABLE"
 
        # 打印一个字符串元素
        IFS="$OLD_IFS"
        TEST2="STRING[*]"
        local ${!TEST2}      # 间接引用(同上).
        echo "String element VAR2: $VAR2 from STRING"

# 打印一个数组元素
        TEST2="ARRAY21[*]"
        local ${!TEST2}      # 间接引用(同上).
        echo "Array element VAR1_1: $VAR1_1 from ARRAY21"
}

print
echo

exit 0

#   脚本作者注,
#+ "你可以很容易的将其扩展成一个能创建hash的Bash脚本."
#   (难) 留给读者的练习: 实现它.
%%%&&&embedded-arrays.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@empty-array.sh@@@!!!

************************************************************************************
#!/bin/bash
# empty-array.sh

#  感谢Stephane Chazelas制作这个例子的原始版本,
#+ 同时感谢Michael Zick对这个例子所作的扩展.

# 空数组与包含有空元素的数组, 这两个概念不同.

array0=( first second third )
array1=( '' )   # "array1"包含一个空元素.
array2=( )      # 没有元素 . . . "array2"为空.

echo
ListArray()
{
echo
echo "Elements in array0:  ${array0[@]}"
echo "Elements in array1:  ${array1[@]}"
echo "Elements in array2:  ${array2[@]}"
echo
echo "Length of first element in array0 = ${#array0}"
echo "Length of first element in array1 = ${#array1}"
echo "Length of first element in array2 = ${#array2}"
echo
echo "Number of elements in array0 = ${#array0[*]}"  # 3
echo "Number of elements in array1 = ${#array1[*]}"  # 1  (惊奇!)
echo "Number of elements in array2 = ${#array2[*]}"  # 0
}

# ===================================================================

ListArray

# 尝试扩展这些数组.

# 添加一个元素到这个数组.
array0=( "${array0[@]}" "new1" )
array1=( "${array1[@]}" "new1" )
array2=( "${array2[@]}" "new1" )

ListArray

# 或
array0[${#array0[*]}]="new2"
array1[${#array1[*]}]="new2"
array2[${#array2[*]}]="new2"

ListArray

# 如果你按照上边的方法对数组进行扩展的话; 数组比较象'栈'
# 上边的操作就是'压栈'
# 栈'高'为:
height=${#array2[@]}
echo
echo "Stack height for array2 = $height"

# '出栈'就是:
unset array2[${#array2[@]}-1] #  数组从0开始索引,
height=${#array2[@]}            #+ 这意味着第一个数组下标为0.
echo
echo "POP"
echo "New stack height for array2 = $height"

ListArray

# 只列出数组array0的第二个和第三个元素.
from=1  # 从0开始索引.
to=2  #
array3=( ${array0[@]:1:2} )
echo
echo "Elements in array3:  ${array3[@]}"

# 处理方式就像是字符串(字符数组).
# 试试其他的"字符串"形式.

# 替换:
array4=( ${array0[@]/second/2nd} )
echo
echo "Elements in array4:  ${array4[@]}"

# 替换掉所有匹配通配符的字符串.
array5=( ${array0[@]//new?/old} )
echo
echo "Elements in array5:  ${array5[@]}"

# 当你开始觉得对此有把握的时候 . . .
array6=( ${array0[@]#*new} )
echo # 这个可能会让你感到惊奇.
echo "Elements in array6:  ${array6[@]}"

array7=( ${array0[@]#new1} )
echo # 数组array6之后就没有惊奇了.
echo "Elements in array7:  ${array7[@]}"

# 看起来非常像 . . .
array8=( ${array0[@]/new1/} )
echo
echo "Elements in array8:  ${array8[@]}"

#  所以, 让我们怎么形容呢?

#  对数组var[@]中的每个元素
#+ 进行连续的字符串操作.
#  因此: 如果结果是长度为0的字符串,
#+ Bash支持字符串向量操作,
#+ 元素会在结果赋值中消失不见.

#  一个问题, 这些字符串是强引用还是弱引用?

zap='new*'
array9=( ${array0[@]/$zap/} )
echo
echo "Elements in array9:  ${array9[@]}"

# 当你还在考虑, 你身在Kansas州何处时 . . .
array10=( ${array0[@]#$zap} )
echo
echo "Elements in array10:  ${array10[@]}"

# 比较array7和array10.
# 比较array8和array9.

# 答案: 必须是弱引用.

exit 0
%%%&&&empty-array.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@encryptedpw.sh@@@!!!

************************************************************************************
#!/bin/bash

# Example "ex72.sh" modified to use encrypted password.

#  Note that this is still rather insecure,
#+ since the decrypted password is sent in the clear.
#  Use something like "ssh" if this is a concern.

E_BADARGS=65

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

Username=bozo           # Change to suit.
pword=/home/bozo/secret/password_encrypted.file
# File containing encrypted password.

Filename=`basename $1`  # Strips pathname out of file name.

Server="XXX"
Directory="YYY"         # Change above to actual server name & directory.

Password=`cruft &lt;$pword`          # Decrypt password.
#  Uses the author's own "cruft" file encryption package,
#+ based on the classic "onetime pad" algorithm,
#+ and obtainable from:
#+ Primary-site:   ftp://ibiblio.org/pub/Linux/utils/file
#+                 cruft-0.2.tar.gz [16k]

ftp -n $Server &lt;&lt;End-Of-Session
user $Username $Password
binary
bell
cd $Directory
put $Filename
bye
End-Of-Session
# -n option to "ftp" disables auto-logon.
# Note that "bell" rings 'bell' after each file transfer.

exit 0
%%%&&&encryptedpw.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@erase.sh@@@!!!

************************************************************************************
#!/bin/bash
# erase.sh: 在读取输入时使用"stty"来设置一个擦除字符.

echo -n "What is your name? "
read name                      #  试试用退格键
                               #+ 来删除输入的字符.
                               #  有什么问题?
echo "Your name is $name."

stty erase '#'                 #  将"hashmark"(#)设置为退格字符.
echo -n "What is your name? "
read name                      #  使用#来删除最后键入的字符.
echo "Your name is $name."

# 警告: 即使在脚本退出后, 新的键值还是保持着这个设置. (译者: 可以使用stty erase '^?'进行恢

复)

exit 0
%%%&&&erase.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@escaped.sh@@@!!!

************************************************************************************
#!/bin/bash
# escaped.sh: 转义符

echo; echo

echo "/v/v/v/v"      # 逐字的打印/v/v/v/v.
# 使用-e选项的'echo'命令来打印转义符.
echo "============="
echo "VERTICAL TABS"
echo -e "/v/v/v/v"   # 打印4个垂直制表符.
echo "=============="

echo "QUOTATION MARK"
echo -e "/042"       # 打印" (引号, 8进制的ASCII 码就是42).
echo "=============="

# 如果使用$'/X'结构,那-e选项就不必要了.
echo; echo "NEWLINE AND BEEP"
echo $'/n'           # 新行.
echo $'/a'           # 警告(蜂鸣).

echo "==============="
echo "QUOTATION MARKS"
# 版本2以后Bash允许使用$'/nnn'结构.
# 注意在这里, '/nnn/'是8进制的值.
echo $'/t /042 /t'   # 被水平制表符括起来的引号(").

# 当然,也可以使用16进制的值,使用$'/xhhh' 结构.
echo $'/t /x22 /t'  # 被水平制表符括起来的引号(").
# 感谢, Greg Keraunen, 指出了这点.
# 早一点的Bash版本允许'/x022'这种形式.
echo "==============="
echo

# 分配ASCII字符到变量中.
# ----------------------------------------
quote=$'/042'        # " 被赋值到变量中.
echo "$quote This is a quoted string, $quote and this lies outside the quotes."

echo

# 变量中的连续的ASCII字符.
triple_underline=$'/137/137/137'  # 137是八进制的'_'.
echo "$triple_underline UNDERLINE $triple_underline"

echo

ABC=$'/101/102/103/010'           # 101, 102, 103是八进制码的A, B, C.
echo $ABC

echo; echo

escape=$'/033'                    # 033 是八进制码的esc.
echo "/"escape/" echoes as $escape"
#                                   没有变量被输出.

echo; echo

exit 0
%%%&&&escaped.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@eval.example@@@!!!

************************************************************************************
In the Perl script "test.pl":
        ...  
        my $WEBROOT = &lt;WEBROOT_PATH&gt;;
        ...

To force variable substitution try:
        $export WEBROOT_PATH=/usr/local/webroot
        $sed 's/&lt;WEBROOT_PATH&gt;/$WEBROOT_PATH/' &lt; test.pl &gt; out

But this just gives:
        my $WEBROOT = $WEBROOT_PATH;

However:
        $export WEBROOT_PATH=/usr/local/webroot
        $eval sed 's%/&lt;WEBROOT_PATH/&gt;%$WEBROOT_PATH%' &lt; test.pl &gt; out
#        ====

That works fine, and gives the expected substitution:
        my $WEBROOT = /usr/local/webroot;

### Paulo Marcel Coelho Aragao校正了这个原始例子.
%%%&&&eval.example&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex10.sh@@@!!!

************************************************************************************
#!/bin/bash

#  小技巧:
#  如果你不能够确定一个特定的条件该如何进行判断,
#+ 那么就使用if-test结构.

echo

echo "Testing /"0/""
if [ 0 ]      # zero
then
  echo "0 is true."
else
  echo "0 is false."
fi            # 0 为真.

echo

echo "Testing /"1/""
if [ 1 ]      # one
then
  echo "1 is true."
else
  echo "1 is false."
fi            # 1 为真.

echo

echo "Testing /"-1/""
if [ -1 ]     # 负1
then
  echo "-1 is true."
else
  echo "-1 is false."
fi            # -1 为真.

echo

echo "Testing /"NULL/""
if [ ]        # NULL (空状态)
then
  echo "NULL is true."
else
  echo "NULL is false."
fi            # NULL 为假.

echo

echo "Testing /"xyz/""
if [ xyz ]    # 字符串
then
  echo "Random string is true."
else
  echo "Random string is false."
fi            # 随便的一串字符为真.

echo

echo "Testing /"/$xyz/""
if [ $xyz ]   # 判断$xyz是否为null, 但是...
              # 这只是一个未初始化的变量.
then
  echo "Uninitialized variable is true."
else
  echo "Uninitialized variable is false."
fi            # 未定义的初始化为假.

echo

echo "Testing /"-n /$xyz/""
if [ -n "$xyz" ]            # 更加正规的条件检查.
then
  echo "Uninitialized variable is true."
else
  echo "Uninitialized variable is false."
fi            # 未初始化的变量为假.

echo

xyz=          # 初始化了, 但是赋null值.

echo "Testing /"-n /$xyz/""
if [ -n "$xyz" ]
then
  echo "Null variable is true."
else
  echo "Null variable is false."
fi            # null变量为假.

echo

# 什么时候"false"为真?

echo "Testing /"false/""
if [ "false" ]              #  看起来"false"只不过是一个字符串而已.
then
  echo "/"false/" is true." #+ 并且条件判断的结果为真.
else
  echo "/"false/" is false."
fi            # "false" 为真.

echo

echo "Testing /"/$false/""  # 再来一个, 未初始化的变量.
if [ "$false" ]
then
  echo "/"/$false/" is true."
else
  echo "/"/$false/" is false."
fi            # "$false" 为假.
              # 现在, 我们得到了预期的结果.

#  如果我们测试以下为初始化的变量"$true"会发生什么呢?

echo

exit 0
%%%&&&ex10.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex11.sh@@@!!!

************************************************************************************
#!/bin/bash

echo

if test -z "$1"
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

if /usr/bin/test -z "$1"      # 与内建的"test"命令结果相同.
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

if [ -z "$1" ]                # 与上边的代码块作用相同.
#   if [ -z "$1"                应该能够运行, 但是...
#+  Bash报错, 提示缺少关闭条件测试的右中括号.
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

if /usr/bin/[ -z "$1" ]       # 再来一个, 与上边的代码块作用相同.
# if /usr/bin/[ -z "$1"       # 能够工作, 但是还是给出一个错误消息.
#                             # 注意:
#                               在版本3.x的Bash中, 这个bug已经被修正了.
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

exit 0
%%%&&&ex11.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex12.sh@@@!!!

************************************************************************************
#!/bin/bash

filename=sys.log

cat /dev/null > $filename; echo "Creating / cleaning out file."
#  如果文件不存在的话就创建文件,
#+ 然后将这个文件清空.
#  : > filename   和   > filename 也能完成这个工作.

tail /var/log/messages > $filename 
# /var/log/messages 必须具有全局的可读权限才行.

echo "$filename contains tail end of system log."

exit 0
%%%&&&ex12.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex13.sh@@@!!!

************************************************************************************
#!/bin/bash

a=4
b=5

#  这里的"a"和"b"既可以被认为是整型也可被认为是字符串.
#  这里在算术比较与字符串比较之间是容易让人产生混淆,
#+ 因为Bash变量并不是强类型的.

#  Bash允许对于变量进行整形操作与比较操作.
#+ 但前提是变量中只能包含数字字符.
#  不管怎么样, 还是要小心.

echo

if [ "$a" -ne "$b" ]
then
  echo "$a is not equal to $b"
  echo "(arithmetic comparison)"
fi

echo

if [ "$a" != "$b" ]
then
  echo "$a is not equal to $b."
  echo "(string comparison)"
  #     "4"  != "5"
  # ASCII 52 != ASCII 53
fi

# 在这个特定的例子中, "-ne"和"!="都可以.

echo

exit 0
%%%&&&ex13.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex14.sh@@@!!!

************************************************************************************
#!/bin/bash
# zmore

#使用'more'来查看gzip文件

NOARGS=65
NOTFOUND=66
NOTGZIP=67

if [ $# -eq 0 ] # 与if [ -z "$1" ]效果相同
# (译者注: 上边这句注释有问题), $1是可以存在的, 可以为空, 如:  zmore "" arg2 arg3
then
  echo "Usage: `basename $0` filename" >&2
  # 错误消息输出到stderr.
  exit $NOARGS
  # 返回65作为脚本的退出状态的值(错误码).
fi

filename=$1

if [ ! -f "$filename" ]   # 将$filename引用起来, 这样允许其中包含空白字符.
then
  echo "File $filename not found!" >&2
  # 错误消息输出到stderr.
  exit $NOTFOUND
fi

if [ ${filename##*.} != "gz" ]
# 在变量替换中使用中括号结构.
then
  echo "File $1 is not a gzipped file!"
  exit $NOTGZIP
fi

zcat $1 | more

# 使用过滤命令'more.'
# 当然, 如果你愿意, 也可以使用'less'.

exit $?   # 脚本将把管道的退出状态作为返回值.
# 事实上, 也不一定非要加上"exit $?", 因为在任何情况下,
# 脚本都会将最后一条命令的退出状态作为返回值.
%%%&&&ex14.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex15.sh@@@!!!

************************************************************************************
#!/bin/bash
# "裸体"变量

echo

# 变量什么时候是"裸体"的, 比如前边少了$的时候?
# 当它被赋值的时候, 而不是被引用的时候.

# 赋值
a=879
echo "The value of /"a/" is $a."

# 使用'let'赋值
let a=16+5
echo "The value of /"a/" is now $a."

echo

# 在'for'循环中(事实上, 这是一种伪赋值):
echo -n "Values of /"a/" in the loop are: "
for a in 7 8 9 11
do
  echo -n "$a "
done

echo
echo

# 使用'read'命令进行赋值(这也是一种赋值的类型):
echo -n "Enter /"a/" "
read a
echo "The value of /"a/" is now $a."

echo

exit 0
%%%&&&ex15.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex16.sh@@@!!!

************************************************************************************
#!/bin/bash

a=23              # 简单的赋值
echo $a
b=$a
echo $b

# 现在让我们来点小变化(命令替换).

a=`echo Hello!`   # 把'echo'命令的结果传给变量'a'
echo $a
#  注意, 如果在一个#+的命令替换结构中包含一个(!)的话,
#+ 那么在命令行下将无法工作.
#+ 因为这触发了Bash的"历史机制."
#  但是, 在脚本中使用的话, 历史功能是被禁用的, 所以就能够正常的运行.

a=`ls -l`         # 把'ls -l'的结果赋值给'a'
echo $a           # 然而, 如果没有引号的话将会删除ls结果中多余的tab和换行符.
echo
echo "$a"         # 如果加上引号的话, 那么就会保留ls结果中的空白符.
                  # (具体请参阅"引用"的相关章节.)

exit 0
%%%&&&ex16.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex17.sh@@@!!!

************************************************************************************
#!/bin/bash

# 作为用例, 调用这个脚本至少需要10个参数, 比如:
# ./scriptname 1 2 3 4 5 6 7 8 9 10
MINPARAMS=10

echo

echo "The name of this script is /"$0/"."
# 添加./是表示当前目录
echo "The name of this script is /"`basename $0`/"."
# 去掉路径名, 剩下文件名, (参见'basename')

echo

if [ -n "$1" ]              # 测试变量被引用.
then
 echo "Parameter #1 is $1"  # 需要引用才能够转义"#"
fi

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

if [ -n "$3" ]
then
 echo "Parameter #3 is $3"
fi

# ...

if [ -n "${10}" ]  # 大于$9的参数必须用{}括起来.
then
 echo "Parameter #10 is ${10}"
fi

echo "-----------------------------------"
echo "All the command-line parameters are: "$*""

if [ $# -lt "$MINPARAMS" ]
then
  echo
  echo "This script needs at least $MINPARAMS command-line arguments!"
fi

echo

exit 0
%%%&&&ex17.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex18.sh@@@!!!

************************************************************************************
#!/bin/bash
# ex18.sh

# 是否'whois domain-name'能够找到如下3个服务之一:
#                    ripe.net, cw.net, radb.net

# 把这个脚本重命名为'wh', 然后放到/usr/local/bin目录下.

# 需要符号链接:
# ln -s /usr/local/bin/wh /usr/local/bin/wh-ripe
# ln -s /usr/local/bin/wh /usr/local/bin/wh-cw
# ln -s /usr/local/bin/wh /usr/local/bin/wh-radb

E_NOARGS=65

if [ -z "$1" ]
then
  echo "Usage: `basename $0` [domain-name]"
  exit $E_NOARGS
fi

# 检查脚本名字, 然后调用合适的服务.
case `basename $0` in    # Or:    case ${0##*/} in
    "wh"     ) whois $1@whois.ripe.net;;
    "wh-ripe") whois $1@whois.ripe.net;;
    "wh-radb") whois $1@whois.radb.net;;
    "wh-cw"  ) whois $1@whois.cw.net;;
    *        ) echo "Usage: `basename $0` [domain-name]";;
esac

exit $?
%%%&&&ex18.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex19.sh@@@!!!

************************************************************************************
#!/bin/bash
# 使用'shift'来逐步存取所有的位置参数.

#  给脚本命个名, 比如shft,
#+ 然后给脚本传递一些位置参数, 比如:
#          ./shft a b c def 23 skidoo

until [ -z "$1" ]  # 直到所有的位置参数都被存取完...
do
  echo -n "$1 "
  shift
done

echo               # 额外的换行.

exit 0
%%%&&&ex19.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex1a.sh@@@!!!

************************************************************************************
#!/bin/bash
# 一个Bash脚本的正确的开头部分.

# Cleanup, 版本 2

# 当然要使用root身份来运行.
# 在此处插入代码,来打印错误消息,并且在不是root身份的时候退出.

LOG_DIR=/var/log
# 如果使用变量,当然比把代码写死的好.
cd $LOG_DIR

cat /dev/null > messages
cat /dev/null > wtmp

echo "Logs cleaned up."

exit # 这个命令是一种正确并且合适的退出脚本的方法.
%%%&&&ex1a.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex1.sh@@@!!!

************************************************************************************
# 清除
# 当然要使用root身份来运行这个脚本.

cd /var/log
cat /dev/null > messages
cat /dev/null > wtmp
echo "Logs cleaned up."
%%%&&&ex1.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex20.sh@@@!!!

************************************************************************************
#!/bin/bash

func1 ()
{
echo This is a function.
}

declare -f        # 列出前面定义的所有函数.

echo

declare -i var1   # var1是个整型变量.
var1=2367
echo "var1 declared as $var1"
var1=var1+1       # 整型变量的声明并不需要使用'let'命令.
echo "var1 incremented by 1 is $var1."
# 尝试修改一个已经声明为整型变量的值.
echo "Attempting to change var1 to floating point value, 2367.1."
var1=2367.1       # 产生错误信息, 并且变量并没有被修改.
echo "var1 is still $var1"

echo

declare -r var2=13.36         # 'declare'允许设置变量的属性,
                              #+ 同时给变量赋值.
echo "var2 declared as $var2" # 试图修改只读变量的值.
var2=13.37                    # 产生错误消息, 并且从脚本退出.

echo "var2 is still $var2"    # 将不会执行到这行.

exit 0                        # 脚本也不会从此处退出.
%%%&&&ex20.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex21.sh@@@!!!

************************************************************************************
#!/bin/bash

# 每次调用$RANDOM都会返回不同的随机整数.
# 一般范围为: 0 - 32767 (有符号的16-bit整数).

MAXCOUNT=10
count=1

echo
echo "$MAXCOUNT random numbers:"
echo "-----------------"
while [ "$count" -le $MAXCOUNT ]      # 产生10 ($MAXCOUNT)个随机整数.
do
  number=$RANDOM
  echo $number
  let "count += 1"  # 增加计数.
done
echo "-----------------"

# 如果你需要在特定范围内产生随机整数, 那么使用'modulo'(模)操作.(译者注: 事实上, 这不是一个

非常好的办法. 理由见man 3 rand)
# 取模操作会返回除法的余数.

RANGE=500

echo

number=$RANDOM
let "number %= $RANGE"
#           ^^
echo "Random number less than $RANGE  ---  $number"

echo

#  如果你需要产生一个大于某个下限的随机整数.
#+ 那么建立一个test循环来丢弃所有小于此下限值的整数.

FLOOR=200

number=0   #初始化
while [ "$number" -le $FLOOR ]
do
  number=$RANDOM
done
echo "Random number greater than $FLOOR ---  $number"
echo

# 让我们对上边的循环尝试一个小改动, 如下:
   #       let "number = $RANDOM + $FLOOR"
   # 这将不再需要那个while循环, 并且能够运行的更快.
   # 但是, 这可能会产生一个问题, 思考一下是什么问题?

# 结合上边两个例子, 来在指定的上下限之间来产生随机数.
number=0   #initialize
while [ "$number" -le $FLOOR ]
do
  number=$RANDOM
  let "number %= $RANGE"  # 让$number依比例落在$RANGE的范围内.
done
echo "Random number between $FLOOR and $RANGE ---  $number"
echo

# 产生二元值, 就是, "true" 或 "false" 两个值.
BINARY=2
T=1
number=$RANDOM

let "number %= $BINARY"
#  注意 let "number >>= 14"    将会给出一个更好的随机分配. #(译者注: 正如man页中提到的, 更

高位的随机分布更加平均)
#+ (右移14位将把所有的位全部清空, 除了第15位, 因为有符号, 第16位是符号位). #取模操作使用低

位来产生随机数会相对不平均)
if [ "$number" -eq $T ]
then
  echo "TRUE"
else
  echo "FALSE"
fi

echo

# 抛骰子.
SPOTS=6   # 模6给出的范围是0 - 5.
          # 加1会得到期望的范围1 - 6.
          # 感谢, Paulo Marcel Coelho Aragao, 对此进行的简化.
die1=0
die2=0
# 是否让SPOTS=7会比加1更好呢? 解释行或者不行的原因?

# 每次抛骰子, 都会给出均等的机会.

let "die1 = $RANDOM % $SPOTS +1" # 抛第一次.
    let "die2 = $RANDOM % $SPOTS +1" # 抛第二次.
    #  上边的算术操作中, 哪个具有更高的优先级呢 --
    #+ 模(%) 还是加法操作(+)?

let "throw = $die1 + $die2"
echo "Throw of the dice = $throw"
echo

exit 0
%%%&&&ex21.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex22a.sh@@@!!!

************************************************************************************
#!/bin/bash
# 还是行星.

# 用行星距太阳的距离来分配行星的名字.

for planet in "Mercury 36" "Venus 67" "Earth 93"  "Mars 142" "Jupiter 483"
do
  set -- $planet  # 解析变量"planet"并且设置位置参数.
  # "--" 将防止$planet为空, 或者是以一个破折号开头.

# 可能需要保存原始的位置参数, 因为它们被覆盖了.
  # 一种方法就是使用数组.
  #        original_params=("$@")

echo "$1  $2,000,000 miles from the sun"
  #-------two  tabs---把后边的0和2连接起来
done

# (感谢, S.C., 对此问题进行的澄清.)

exit 0
%%%&&&ex22a.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex22.sh@@@!!!

************************************************************************************
#!/bin/bash
# 列出所有的行星名称. (译者注: 现在的太阳系行星已经有了变化^_^)

for planet in Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto
do
  echo $planet  # 每个行星都被单独打印在一行上.
done

echo

for planet in "Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto"
# 所有的行星名称都打印在同一行上.
# 整个'list'都被双引号封成了一个变量.
do
  echo $planet
done

exit 0
%%%&&&ex22.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex23.sh@@@!!!

************************************************************************************
#!/bin/bash

#  使用两种方式来调用这个脚本, 一种带参数, 另一种不带参数,
#+ 并观察在这两种情况下, 此脚本的行为.

for a
do
 echo -n "$a "
done

#  省略'in list'部分, 因此循环将会操作'$@'
#+ (包括空白的命令行参数列表).

echo

exit 0
%%%&&&ex23.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex24.sh@@@!!!

************************************************************************************
#!/bin/bash
# Faxing (前提是'fax'必须已经安装好).

EXPECTED_ARGS=2
E_BADARGS=65

if [ $# -ne $EXPECTED_ARGS ]
# 检查命令行参数的个数是否正确.
then
   echo "Usage: `basename $0` phone# text-file"
   exit $E_BADARGS
fi

if [ ! -f "$2" ]
then
  echo "File $2 is not a text file"
  exit $E_BADARGS
fi

fax make $2              # 从纯文本文件中创建传真格式的文件.

for file in $(ls $2.0*)  # 连接转换过的文件.
                         # 在变量列表中使用通配符.
do
  fil="$fil $file"
done

efax -d /dev/ttyS3 -o1 -t "T$1" $fil   # 干活的地方.

# S.C. 指出, 通过下边的命令可以省去for循环.
#    efax -d /dev/ttyS3 -o1 -t "T$1" $2.0*
# 但这并不十分具有讲解意义[嘿嘿].

exit 0
%%%&&&ex24.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex25.sh@@@!!!

************************************************************************************
#!/bin/bash

var0=0
LIMIT=10

while [ "$var0" -lt "$LIMIT" ]
do
  echo -n "$var0 "        # -n 将会阻止产生新行.
  #             ^           空格, 数字之间的分隔.

var0=`expr $var0 + 1`   # var0=$(($var0+1))  也可以.
                          # var0=$((var0 + 1)) 也可以.
                          # let "var0 += 1"    也可以.
done                      # 使用其他的方法也行.

echo

exit 0
%%%&&&ex25.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex26a.sh@@@!!!

************************************************************************************
#!/bin/bash

var1=unset
previous=$var1

while echo "previous-variable = $previous"
      echo
      previous=$var1
      [ "$var1" != end ] # 纪录之前的$var1.
      # 这个"while"中有4个条件, 但是只有最后一个能够控制循环.
      # *最后*的退出状态就是由这最后一个条件来决定.
do
echo "Input variable #1 (end to exit) "
  read var1
  echo "variable #1 = $var1"
done

# 尝试理解这个脚本的运行过程.
# 这里还是有点小技巧的.

exit 0
%%%&&&ex26a.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex26.sh@@@!!!

************************************************************************************
#!/bin/bash

echo
                               # 等价于:
while [ "$var1" != "end" ]     # while test "$var1" != "end"
do
  echo "Input variable #1 (end to exit) "
  read var1                    # 为什么不使用'read $var1'?
  echo "variable #1 = $var1"   # 因为包含"#", 所以需要""
  # 如果输入为'end', 那么就在这里echo.
  # 不在这里判断结束, 在循环顶判断.
  echo
done

exit 0
%%%&&&ex26.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex27.sh@@@!!!

************************************************************************************
#!/bin/bash

END_CONDITION=end

until [ "$var1" = "$END_CONDITION" ]
# 在循环的顶部进行条件判断.
do
  echo "Input variable #1 "
  echo "($END_CONDITION to exit)"
  read var1
  echo "variable #1 = $var1"
  echo
done

exit 0
%%%&&&ex27.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex28.sh@@@!!!

************************************************************************************
#!/bin/bash

LIMIT=19  # 上限

echo
echo "Printing Numbers 1 through 20 (but not 3 and 11)."

a=0

while [ $a -le "$LIMIT" ]
do
 a=$(($a+1))

if [ "$a" -eq 3 ] || [ "$a" -eq 11 ]  # 除了3和11.
 then
   continue      # 跳过本次循环剩余的语句.
 fi

echo -n "$a "   # 在$a等于3和11的时候,这句将不会执行.
done

# 练习:
# 为什么循环会打印出20?

echo; echo

echo Printing Numbers 1 through 20, but something happens after 2.

##################################################################

# 同样的循环, 但是用'break'来代替'continue'.

a=0

while [ "$a" -le "$LIMIT" ]
do
 a=$(($a+1))

if [ "$a" -gt 2 ]
 then
   break  # 将会跳出整个循环.
 fi

echo -n "$a "
done

echo; echo; echo

exit 0
%%%&&&ex28.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex29.sh@@@!!!

************************************************************************************
#!/bin/bash
# 测试字符串范围.

echo; echo "Hit a key, then hit return."
read Keypress

case "$Keypress" in
  [[:lower:]]   ) echo "Lowercase letter";;
  [[:upper:]]   ) echo "Uppercase letter";;
  [0-9]         ) echo "Digit";;
  *             ) echo "Punctuation, whitespace, or other";;
esac      #  允许字符串的范围出现在[中括号]中,
          #+ 或者出现在POSIX风格的[[双中括号中.

#  在这个例子的第一个版本中,
#+ 测试大写和小写字符串的工作使用的是
#+ [a-z] 和 [A-Z].
#  这种用法在某些特定场合的或某些Linux发行版中不能够正常工作.
#  POSIX 的风格更具可移植性.
#  感谢Frank Wang指出了这点.

#  练习:
#  -----
#  就像这个脚本所表现出来的, 它只允许单次的按键, 然后就结束了.
#  修改这个脚本, 让它能够接受重复输入,
#+ 报告每次按键, 并且只有在"X"被键入时才结束.
#  暗示: 将这些代码都用"while"循环圈起来.

exit 0
%%%&&&ex29.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex2.sh@@@!!!

************************************************************************************
#!/bin/bash
# 清除, 版本 3

#  警告:
#  -----
#  这个脚本有好多特征,
#+ 这些特征是在后边章节进行解释的.
#  大概是进行到本书的一半的时候,
#+ 你就会觉得它没有什么神秘的了.

LOG_DIR=/var/log
ROOT_UID=0     # $UID为0的时候,用户才具有root用户的权限
LINES=50       # 默认的保存行数
E_XCD=66       # 不能修改目录?
E_NOTROOT=67   # 非root用户将以error退出

# 当然要使用root用户来运行.
if [ "$UID" -ne "$ROOT_UID" ]
then
  echo "Must be root to run this script."
  exit $E_NOTROOT
fi

if [ -n "$1" ]
# 测试是否有命令行参数(非空).
then
  lines=$1
else 
  lines=$LINES # 默认,如果不在命令行中指定.
fi

#  Stephane Chazelas 建议使用下边
#+ 的更好方法来检测命令行参数.
#+ 但对于这章来说还是有点超前.
#
#    E_WRONGARGS=65  # 非数值参数(错误的参数格式)
#
#    case "$1" in
#    ""      ) lines=50;;
#    *[!0-9]*) echo "Usage: `basename $0` file-to-cleanup"; exit $E_WRONGARGS;;
#    *       ) lines=$1;;
#    esac
#
#* 直到"Loops"的章节才会对上边的内容进行详细的描述.

cd $LOG_DIR

if [ `pwd` != "$LOG_DIR" ]  # 或者 if[ "$PWD" != "$LOG_DIR" ]
                            # 不在 /var/log中?
then
  echo "Can't change to $LOG_DIR."
  exit $E_XCD
fi  # 在处理log file之前,再确认一遍当前目录是否正确.

# 更有效率的做法是:
#
# cd /var/log || {
#   echo "Cannot change to necessary directory." >&2
#   exit $E_XCD;
# }

tail -$lines messages > mesg.temp # 保存log file消息的最后部分.
mv mesg.temp messages             # 变为新的log目录.

# cat /dev/null > messages
#* 不再需要了,使用上边的方法更安全.

cat /dev/null > wtmp  #  ': > wtmp' 和 '> wtmp'具有相同的作用
echo "Logs cleaned up."

exit 0
#  退出之前返回0,
#+ 返回0表示成功.
%%%&&&ex2.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex30a.sh@@@!!!

************************************************************************************
#!/bin/bash
# ex30a.sh: 脚本ex30.sh的"彩色"版本.
#            没被加工处理过的地址数据库

clear                                   # 清屏.

echo -n "          "
echo -e '/E[37;44m'"/033[1mContact List/033[0m"
                                        # 在蓝色背景下的白色.
echo; echo
echo -e "/033[1mChoose one of the following persons:/033[0m"
                                        # 粗体
tput sgr0
echo "(Enter only the first letter of name.)"
echo
echo -en '/E[47;34m'"/033[1mE/033[0m"   # 蓝色
tput sgr0                               # 将颜色重置为"常规".
echo "vans, Roland"                     # "[E]vans, Roland"
echo -en '/E[47;35m'"/033[1mJ/033[0m"   # 红紫色
tput sgr0
echo "ones, Mildred"
echo -en '/E[47;32m'"/033[1mS/033[0m"   # 绿色
tput sgr0
echo "mith, Julie"
echo -en '/E[47;31m'"/033[1mZ/033[0m"   # 红色
tput sgr0
echo "ane, Morris"
echo

read person

case "$person" in
# 注意, 变量被引用起来了.

"E" | "e" )
  # 大小写的输入都能接受.
  echo
  echo "Roland Evans"
  echo "4321 Floppy Dr."
  echo "Hardscrabble, CO 80753"
  echo "(303) 734-9874"
  echo "(303) 734-9892 fax"
  echo "revans@zzy.net"
  echo "Business partner & old friend"
  ;;

"J" | "j" )
  echo
  echo "Mildred Jones"
  echo "249 E. 7th St., Apt. 19"
  echo "New York, NY 10009"
  echo "(212) 533-2814"
  echo "(212) 533-9972 fax"
  echo "milliej@loisaida.com"
  echo "Girlfriend"
  echo "Birthday: Feb. 11"
  ;;

# 稍后为Smith & Zane添加信息.

* )
   # 默认选项.   
   # 空输入(直接按回车)也会在这被匹配.
   echo
   echo "Not yet in database."
  ;;

esac

tput sgr0                               # 将颜色重置为"常规".

echo

exit 0
%%%&&&ex30a.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex30.sh@@@!!!

************************************************************************************
#!/bin/bash

# 未经处理的地址资料

clear # 清屏.

echo "          Contact List"
echo "          ------- ----"
echo "Choose one of the following persons:"
echo
echo "[E]vans, Roland"
echo "[J]ones, Mildred"
echo "[S]mith, Julie"
echo "[Z]ane, Morris"
echo

read person

case "$person" in
# 注意, 变量是被""引用的.

"E" | "e" )
  # 接受大写或者小写输入.
  echo
  echo "Roland Evans"
  echo "4321 Floppy Dr."
  echo "Hardscrabble, CO 80753"
  echo "(303) 734-9874"
  echo "(303) 734-9892 fax"
  echo "revans@zzy.net"
  echo "Business partner & old friend"
  ;;
# 注意, 每个选项后边都要以双分号;;结尾.

"J" | "j" )
  echo
  echo "Mildred Jones"
  echo "249 E. 7th St., Apt. 19"
  echo "New York, NY 10009"
  echo "(212) 533-2814"
  echo "(212) 533-9972 fax"
  echo "milliej@loisaida.com"
  echo "Ex-girlfriend"
  echo "Birthday: Feb. 11"
  ;;

# 后边的 Smith 和 Zane 的信息在这里就省略了.

* )
   # 默认选项.
   # 空输入(敲回车RETURN), 也适用于这里.
   echo
   echo "Not yet in database."
  ;;

esac

echo

#  练习:
#  -----
#  修改这个脚本, 让它能够接受多个输入,
#+ 并且能够显示多个地址.

exit 0
%%%&&&ex30.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex31.sh@@@!!!

************************************************************************************
#!/bin/bash

PS3='Choose your favorite vegetable: ' # 设置提示符字串.

echo

select vegetable in "beans" "carrots" "potatoes" "onions" "rutabagas"
do
  echo
  echo "Your favorite veggie is $vegetable."
  echo "Yuck!"
  echo
  break  # 如果这里没有 'break' 会发生什么?
done

exit 0
%%%&&&ex31.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex32.sh@@@!!!

************************************************************************************
#!/bin/bash

PS3='Choose your favorite vegetable: '

echo

choice_of()
{
select vegetable
# [in list]被忽略, 所以'select'使用传递给函数的参数.
do
  echo
  echo "Your favorite veggie is $vegetable."
  echo "Yuck!"
  echo
  break
done
}

choice_of beans rice carrots radishes tomatoes spinach
#         $1    $2   $3      $4       $5       $6
#         传递给choice_of()的参数

exit 0
%%%&&&ex32.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex33a.sh@@@!!!

************************************************************************************
#!/bin/bash
# 使用getopt.

# 尝试使用下边的不同的方法来调用这脚本:
#   sh ex33a.sh -a
#   sh ex33a.sh -abc
#   sh ex33a.sh -a -b -c
#   sh ex33a.sh -d
#   sh ex33a.sh -dXYZ
#   sh ex33a.sh -d XYZ
#   sh ex33a.sh -abcd
#   sh ex33a.sh -abcdZ
#   sh ex33a.sh -z
#   sh ex33a.sh a
# 解释上面每一次调用的结果.

E_OPTERR=65

if [ "$#" -eq 0 ]
then   # 脚本需要至少一个命令行参数.
  echo "Usage $0 -[options a,b,c]"
  exit $E_OPTERR
fi

set -- `getopt "abcd:" "$@"`
# 为命令行参数设置位置参数.
# 如果使用"$*"来代替"$@"的话, 会发生什么?

while [ ! -z "$1" ]
do
  case "$1" in
    -a) echo "Option /"a/"";;
    -b) echo "Option /"b/"";;
    -c) echo "Option /"c/"";;
    -d) echo "Option /"d/" $2";;
     *) break;;
  esac

shift
done

#  通常来说在脚本中使用内建的'getopts'命令,
#+ 会比使用'getopt'好一些.
#  参考"ex33.sh".

exit 0
%%%&&&ex33a.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex33.sh@@@!!!

************************************************************************************
#!/bin/bash
# 练习 getopts 和 OPTIND
# 在Bill Gradwohl的建议下, 这个脚本于 10/09/03 被修改.

# 在这里我们将学习如何使用 'getopts' 来处理脚本的命令行参数.
# 参数被作为"选项"(标志)来解析, 并且对选项分配参数.

# 试一下, 使用如下方法来调用这个脚本
# 'scriptname -mn'
# 'scriptname -oq qOption' (qOption 可以是任意的哪怕有些诡异字符的字符串.)
# 'scriptname -qXXX -r'
#
# 'scriptname -qr'    - 意外的结果, "r" 将被看成是选项 "q" 的参数.
# 'scriptname -q -r'  - 意外的结果, 同上.
# 'scriptname -mnop -mnop'  - 意外的结果
# (OPTIND在选项刚传递进来的地方是不可靠的).
# (译者注: 也就是说OPTIND只是一个参数指针, 指向下一个参数的位置.
#  比如: -mnop 在mno处理的位置OPTION都为1, 而到p的处理就变成2,
#   -m -n -o 在m的时候OPTION为2, 而n为3, o为4,
#   也就是说它总指向下一个位置).
#
#  如果选项需要一个参数的话("flag:"), 那么它将获取
#+ 命令行上紧挨在它后边的任何字符.

NO_ARGS=0
E_OPTERROR=65

if [ $# -eq "$NO_ARGS" ]  # 不带命令行参数就调用脚本?
then
  echo "Usage: `basename $0` options (-mnopqrs)"
  exit $E_OPTERROR        # 如果没有参数传递进来, 那么就退出脚本, 并且解释此脚本的用法.
fi 
# 用法: scriptname -options
# 注意: 必须使用破折号 (-)

while getopts ":mnopq:rs" Option
do
  case $Option in
    m     ) echo "Scenario #1: option -m-   [OPTIND=${OPTIND}]";;
    n | o ) echo "Scenario #2: option -$Option-   [OPTIND=${OPTIND}]";;
    p     ) echo "Scenario #3: option -p-   [OPTIND=${OPTIND}]";;
    q     ) echo "Scenario #4: option -q-/
 with argument /"$OPTARG/"   [OPTIND=${OPTIND}]";;
    #  注意, 选项'q'必须分配一个参数,
    #+ 否则, 默认将失败.
    r | s ) echo "Scenario #5: option -$Option-";;
    *     ) echo "Unimplemented option chosen.";;   # 默认情况的处理
  esac
done

shift $(($OPTIND - 1))
#  (译者注: shift命令是可以带参数的, 参数就是移动的个数)
#  将参数指针减1, 这样它将指向下一个参数.
#  $1 现在引用的是命令行上的第一个非选项参数,
#+ 如果有一个这样的参数存在的话.

exit 0

#   就像 Bill Gradwohl 所描述的,
#  "getopts机制允许指定一个参数,
#+ 但是scriptname -mnop -mnop就是一种比较特殊的情况,
#+ 因为在使用OPTIND的时候, 没有可靠的方法来区分到底传递进来了什么东西."
%%%&&&ex33.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex34.sh@@@!!!

************************************************************************************
#!/bin/bash

# script "set-test"

# 使用3个命令行参数来调用这个脚本,
# 比如, "./set-test one two three".

echo
echo "Positional parameters before  set /`uname -a/` :"
echo "Command-line argument #1 = $1"
echo "Command-line argument #2 = $2"
echo "Command-line argument #3 = $3"

set `uname -a` # 把`uname -a`的命令输出设置
               # 为新的位置参数.

echo $_        # unknown(译者注: 这要看你的uname -a输出了,这句打印出的就是输出的最后一个单

词.)
# 在脚本中设置标志.

echo "Positional parameters after  set /`uname -a/` :"
# $1, $2, $3, 等等. 这些位置参数将被重新初始化为`uname -a`的结果
echo "Field #1 of 'uname -a' = $1"
echo "Field #2 of 'uname -a' = $2"
echo "Field #3 of 'uname -a' = $3"
echo ---
echo $_        # ---
echo

exit 0
%%%&&&ex34.sh&&&%%%

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>

***@@@ex35.sh@@@!!!

************************************************************************************
#!/bin/bash

a=/home/bozo/daily-journal.txt

echo "Basename of /home/bozo/daily-journal.txt = `basename $a`"
echo "Dirname of /home/bozo/daily-journal.txt = `dirname $a`"
echo
echo "My own home is `basename ~/`."         # `basename ~` 也可以.
echo "The home of my home is `dirname ~/`."  # `dirname ~`  也可以.

exit 0

330pics-shell scripts-second相关推荐

  1. 鸟哥的Linux私房菜-第10/11/12/13章(vim程序编辑器、学习bash、正则表达式与文件格式化处理、学习Shell Scripts)...

    第10章 vim程序编辑器 可以将vim看做vi的进阶版本,vim可以用颜色或底线等方式来显示出一些特殊的信息. 为何要学习vim?因为: a. 所有的 Unix Like 系统都会内建 vi 文书编 ...

  2. [读书笔记]鸟哥的LINUX私房菜 十三章 shell scripts

    shell scripts语法 #!/bin/bash 规定使用bash语法解释scripts #Program: 表明程序开始 主程序部分 exit 0 表明程序结束 #用sh执行脚本会新开一个子进 ...

  3. 初识Shell Scripts编程--最最简单的shell例子

    前段时间因为要往一个文件写入一系列随机数给大家用,自己用C语言写了一个,把源文件发给大家,然后大家还要编译,运行,有时候里面的代码需要修改重新编译运行,很是麻烦,其实这些都可以用简单的Shell Sc ...

  4. Top DBA Shell Scripts for Monitoring the Database

    Top DBA Shell Scripts for Monitoring the Database 本文来自 DBAZine 创建于: 2010-3-24 上午6:38 作者 Anirban Dutt ...

  5. Linux的shell scripts

    一.什么是脚本(scripts) 安装一定逻辑关系记录的命令文件,在此文件有可执行权限的情况下可以用文件名称发起脚本内记录命令的执行,shell脚本是一种解释性语言,文件内记录的动作需要解释器shel ...

  6. 27.13. flock - manage locks from shell scripts

    ### flock 当多个进程可能会对同样的数据执行操作时,这些进程需要保证其它进程没有在操作,以免损坏数据.通常,这样的进程会使用一个"锁文件",也就是建立一个文件来告诉别的进程 ...

  7. Note For Linux By Jes(7)-学习 shell scripts

    简单的shellscript 练习: script 的运行方式差异(source, sh script, ./script) 利用直接运行的方式来运行script 利用source 来运行脚本:在父程 ...

  8. Linux的shell scripts的shell脚本练习

    1.查找当前交互式的用户 2.红色字体红色背景显示从1到10计时 3.查询IP是否连通 4.带交互式,带提示的查询IP是否连通 5.显示主机有几块网卡,每块网卡的IP是多少 6.10秒带红色字体红色背 ...

  9. shell scripts 之 代码量统计

    代码统计1 文件only中的内容为多个文件的文件名,code如下: xargs说明: xargs 读入stdin的值, 并默认以空白或者回车作为分隔符,将分割的值作为参数传给后面紧接着的的命令行操作. ...

  10. shell设置系统环境变量的问题

    业务场景: 我在一个bash脚本中修改了PATH变量的内容,并将其保存到/etc/profile文件中,同时执行了 source /etc/profile 但是当脚本退出时,我发现PATH变量还是没有 ...

最新文章

  1. 转巧用notepad++ 批量转换ansi 和 utf8
  2. 什么是802.11G协议
  3. java模拟器apk闪退_急,求帮助,eclipse生成apk安装以后闪退
  4. mongodb数据库扩展名_MongoDB权威指南
  5. ASP.NET的视图(Razor)循环产生html代码
  6. java9 多版本兼容jar_Java 9 多版本兼容 jar 包
  7. Apache Hudi 是Uber 大数据存储系统
  8. web开发兼容性测试工具
  9. 9.使用 curses 函数库来管理基于文本的屏幕
  10. 阿里云眼中的“云网络3.0”:构建应用、云、边一体网络
  11. cdrx8如何批量导出jpg_Coreldraw 8插件下载|Coreldraw x8高版本文件(保存为coreldraw 8.0版)最新插件_ - 极光下载站...
  12. nodejs+express留言板功能实现
  13. 关于 idea 快捷键 alt + f7 无法使用的一些尝试
  14. 指针式仪表自动识别和读数
  15. 输入任意一个字符串,判断这个字符串是否是回文,回文示例: 上海自来水来自海上
  16. SpringBoot解决跨域请求的OPTIONS问题
  17. 【英语六级】【仔细阅读】(4)
  18. 路由器使用Caddy搭建Webdav服务
  19. CADD课程学习(13)-- 研究蛋白小分子动态相互作用-I(GROMACS)
  20. 联想air15和联想小新15有什么区别

热门文章

  1. Android 路由框架ARouter最佳实践
  2. pg2.OperationalError: could not connect to server: Connection timed out (0x0000274C/10060)
  3. LaTeX 嵌入MATLAB 绘图的字体
  4. 帅某---考研---空间直线绕坐标轴旋转、二次曲面方程
  5. 2021-09-21
  6. AD域用户加入域中的各个组解释
  7. uniapp设置整个页面背景颜色渐变,设置单个页面背景颜色
  8. “91系列”资源网凉了,500余网站被封杀!
  9. 头条php,基于PHP的免费新闻头条接口查询
  10. 记一次漫长的蓝屏处理过程