330pics-shell scripts-first

***@@@add-drive.sh@@@!!!************************************************************************************
#!/bin/bash

# 在系统上添加第二块硬盘驱动器.
# 软件配置. 假设硬件已经安装了.
# 来自于本书作者的一篇文章.
# 在"Linux Gazette"的问题#38上, http://www.linuxgazette.com.

ROOT_UID=0     # 这个脚本必须以root身份运行.
E_NOTROOT=67   # 非root用户将会产生这个错误.

if [ "$UID" -ne "$ROOT_UID" ] #当不等于时
then
  echo "Must be root to run this script."
  exit $E_NOTROOT
fi 

# 要非常谨慎小心的使用!
# 如果某步错了, 可能会彻底摧毁你当前的文件系统.


NEWDISK=/dev/hdb         # 假设/dev/hdb空白. 检查一下!
MOUNTPOINT=/mnt/newdisk  #或者选择其他的mount入口.


fdisk $NEWDISK
mke2fs -cv $NEWDISK1   # 检查坏块, 并且详细输出.
#  注意:    /dev/hdb1, *不是* /dev/hdb!
mkdir $MOUNTPOINT
chmod 777 $MOUNTPOINT  # 让所有用户都具有全部权限.


# 现在, 测试一下...
# mount -t ext2 /dev/hdb1 /mnt/newdisk
# 尝试创建一个目录.
# 如果运行起来了, umount它, 然后继续.

# 最后一步:
# 将下边这行添加到/etc/fstab.
# /dev/hdb1  /mnt/newdisk  ext2  defaults  1 1

exit 0
%%%&&&add-drive.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@agram2.sh@@@!!!************************************************************************************
#!/bin/bash
# agram2.sh
# 关于命令替换嵌套的例子.

#  使用"anagram"工具.
#+ 这是作者的"yawl"文字表软件包中的一部分.
http://ibiblio.org/pub/Linux/libs/yawl-0.3.2.tar.gz
http://personal.riverusers.com/~thegrendel/yawl-0.3.2.tar.gz

E_NOARGS=66
E_BADARG=67
MINLEN=7

if [ -z "$1" ]
then
  echo "Usage $0 LETTERSET"
  exit $E_NOARGS         # 脚本需要一个命令行参数.
elif [ ${#1} -lt $MINLEN ]
then
  echo "Argument must have at least $MINLEN letters."
  exit $E_BADARG
fi

 

FILTER='.......'         # 必须至少有7个字符.
#       1234567
Anagrams=( $(echo $(anagram $1 | grep $FILTER) ) )
#           |     |    嵌套的命令替换.       | |
#        (              数组分配                 )

echo
echo "${#Anagrams[*]}  7+ letter anagrams found"
echo
echo ${Anagrams[0]}      # 第一个anagram.
echo ${Anagrams[1]}      # 第二个anagram.
                         # 等等.

# echo "${Anagrams[*]}"  # 在一行上列出所有的anagram . . .

#  考虑到后边还有单独的一章, 对"数组"进行详细的讲解,
#+ 所以在这里就不深入讨论了.

# 可以参考脚本agram.sh, 这也是一个找出anagram的例子.

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


***@@@agram.sh@@@!!!************************************************************************************
#!/bin/bash
# agram.sh: 使用anagram来玩游戏.

# 寻找anagram...
LETTERSET=etaoinshrdlu
FILTER='.......'       # 最少有多少个字母?
#       1234567

anagram "$LETTERSET" | # 找出这个字符串中所有的anagram...
grep "$FILTER" |       # 至少需要7个字符,
grep '^is' |           # 以'is'开头
grep -v 's$' |         # 不是复数(指英文单词的复数)
grep -v 'ed$'          # 不是过去时(也指英文单词)
# 可以添加许多种组合条件和过滤器.

#  使用"anagram"工具,
#+ 这是作者的"yawl"文字表软件包中的一部分.
http://ibiblio.org/pub/Linux/libs/yawl-0.3.2.tar.gz
http://personal.riverusers.com/~thegrendel/yawl-0.3.2.tar.gz

exit 0                 # 代码结束.


bash$ sh agram.sh
islander
isolate
isolead
isotheral

 

#  练习:
#  -----
#  修改这个脚本, 使其能够让LETTERSET作为命令行参数.
#  将第11 - 13行的过滤器参数化(比如, 可以使用变量$FILTER),
#+ 这样我们就可以根据传递的参数来指定功能.

#  可以参考脚本agram2.sh,
#+ 与这个例子稍微有些不同.
%%%&&&agram.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


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

shopt -s expand_aliases
# 必须设置这个选项, 否则脚本不会打开别名功能.


# 首先, 来点有趣的.
alias Jesse_James='echo "/"Alias Jesse James/" was a 1959 comedy starring Bob Hope."'
Jesse_James

echo; echo; echo;

alias ll="ls -l"
# 可以使用单引号(')或双引号(")来定义一个别名.

echo "Trying aliased /"ll/":"
ll /usr/X11R6/bin/mk*   #* 别名工作了.

echo

directory=/usr/X11R6/bin/
prefix=mk*  # 看一下通配符会不会引起麻烦.
echo "Variables /"directory/" + /"prefix/" = $directory$prefix"
echo

alias lll="ls -l $directory$prefix"

echo "Trying aliased /"lll/":"
lll         # 详细列出/usr/X11R6/bin目录下所有以mk开头的文件.
# 别名能处理连接变量 -- 包括通配符 -- o.k.

 


TRUE=1

echo

if [ TRUE ]
then
  alias rr="ls -l"
  echo "Trying aliased /"rr/" within if/then statement:"
  rr /usr/X11R6/bin/mk*   #* 产生错误信息!
  # 别名不能在混合结构中使用.
  echo "However, previously expanded alias still recognized:"
  ll /usr/X11R6/bin/mk*
fi 

echo

count=0
while [ $count -lt 3 ]
do
  alias rrr="ls -l"
  echo "Trying aliased /"rrr/" within /"while/" loop:"
  rrr /usr/X11R6/bin/mk*   #* 这里, 别名也不会扩展.
                           #  alias.sh: line 57: rrr: command not found
  let count+=1
done

echo; echo

alias xyz='cat $0'   # 脚本打印自身内容.
                     # 注意是单引号(强引用).
xyz
#  虽然Bash文档建议, 它不能正常运行,
#+ 不过它看起来是可以运行的.
#
#  然而, 就像Steve Jacobson所指出的那样,
#+ 参数"$0"立即扩展成了这个别名的声明.

exit 0
%%%&&&alias.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@allprofs.sh@@@!!!************************************************************************************
#!/bin/bash
# allprofs.sh: 打印所有用户的配置文件

# 由Heiner Steven编写, 并由本书作者进行了修改.

FILE=.bashrc  #  在原始脚本中, File containing user profile,
              #+ 包含用户profile的是文件".profile".

for home in `awk -F: '{print $6}' /etc/passwd`
do
  [ -d "$home" ] || continue    # 如果没有home目录, 跳出本次循环.
  [ -r "$home" ] || continue    # 如果home目录没有读权限, 跳出本次循环.
  (cd $home; [ -e $FILE ] && less $FILE)
done

#  当脚本结束时,不必使用'cd'命令返回原来的目录.
#+ 因为'cd $home'是在子shell中发生的, 不影响父shell.

exit 0
%%%&&&allprofs.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@alt-bc.sh@@@!!!************************************************************************************
#!/bin/bash
# 使用命令替换来调用'bc'
# 并与'here document'相结合.


var1=`bc << EOF
18.33 * 19.78
EOF
`
echo $var1       # 362.56


#  使用$( ... )这种标记法也可以.
v1=23.53
v2=17.881
v3=83.501
v4=171.63

var2=$(bc << EOF
scale = 4
a = ( $v1 + $v2 )
b = ( $v3 * $v4 )
a * b + 15.35
EOF
)
echo $var2       # 593487.8452


var3=$(bc -l << EOF
scale = 9
s ( 1.7 )
EOF
)
# 返回弧度为1.7的正弦.
# "-l"选项将会调用'bc'算数库.
echo $var3       # .991664810


# 现在, 在函数中试一下...
hyp=             # 声明全局变量.
hypotenuse ()    # 计算直角三角形的斜边.
{
hyp=$(bc -l << EOF
scale = 9
sqrt ( $1 * $1 + $2 * $2 )
EOF
)
# 不幸的是, 不能从bash函数中返回浮点值.
}

hypotenuse 3.68 7.31
echo "hypotenuse = $hyp"    # 8.184039344


exit 0
%%%&&&alt-bc.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@am-i-root.sh@@@!!!************************************************************************************
#!/bin/bash
# am-i-root.sh:   我是不是root用户?

ROOT_UID=0   # Root的$UID为0.

if [ "$UID" -eq "$ROOT_UID" ]  # 只有真正的"root"才能经受得住考验?
then
  echo "You are root."
else
  echo "You are just an ordinary user (but mom loves you just the same)."
fi

exit 0


# ============================================= #
# 下边的代码不会执行, 因为脚本在上边已经退出了.

# 下边是另外一种判断root用户的方法:

ROOTUSER_NAME=root

username=`id -nu`              # 或者...   username=`whoami`
if [ "$username" = "$ROOTUSER_NAME" ]
then
  echo "Rooty, toot, toot. You are root."
else
  echo "You are just a regular fella."
fi
%%%&&&am-i-root.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@and-list2.sh@@@!!!************************************************************************************
#!/bin/bash

ARGS=1        # 期望的参数个数.
E_BADARGS=65  # 如果传递的参数个数不正确, 那么给出这个退出码.

test $# -ne $ARGS && echo "Usage: `basename $0` $ARGS argument(s)" && exit $E_BADARGS
#  如果"条件1"测试为true (表示传递给脚本的参数个数不对),
#+ 则余下的命令会被执行, 并且脚本将结束运行.

# 只有当上面的测试条件为false的时候, 这行代码才会被执行.
echo "Correct number of arguments passed to this script."

exit 0

# 为了检查退出码, 在脚本结束的时候可以使用"echo $?"来查看退出码.
%%%&&&and-list2.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@and-or.sh@@@!!!************************************************************************************
#!/bin/bash

a=24
b=47

if [ "$a" -eq 24 ] && [ "$b" -eq 47 ]
then
  echo "Test #1 succeeds."
else
  echo "Test #1 fails."
fi

# ERROR:   if [ "$a" -eq 24 && "$b" -eq 47 ]
#+         尝试运行' [ "$a" -eq 24 '
#+         因为没找到匹配的']'所以失败了.
#
#  注意:  if [[ $a -eq 24 && $b -eq 24 ]]  也能正常运行.
#  双中括号的if-test结构要比
#+ 单中括号的if-test结构更加灵活.
#    (在第17行"&&"与第6行的"&&"具有不同的含义.)
#    感谢, Stephane Chazelas, 指出这点.


if [ "$a" -eq 98 ] || [ "$b" -eq 47 ]
then
  echo "Test #2 succeeds."
else
  echo "Test #2 fails."
fi


#  -a和-o选项提供了
#+ 一种可选的混合条件测试的方法.
#  感谢Patrick Callahan指出这点.


if [ "$a" -eq 24 -a "$b" -eq 47 ]
then
  echo "Test #3 succeeds."
else
  echo "Test #3 fails."
fi


if [ "$a" -eq 98 -o "$b" -eq 47 ]
then
  echo "Test #4 succeeds."
else
  echo "Test #4 fails."
fi


a=rhino
b=crocodile
if [ "$a" = rhino ] && [ "$b" = crocodile ]
then
  echo "Test #5 succeeds."
else
  echo "Test #5 fails."
fi

exit 0
%%%&&&and-or.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@archiveweblogs.sh@@@!!!************************************************************************************
#!/bin/bash
# archiveweblogs.sh v1.0

# Troy Engel &lt;tengel@fluid.com&gt;
# Slightly modified by document author.
# Used with permission.
#
#  This script will preserve the normally rotated and
#+ thrown away weblogs from a default RedHat/Apache installation.
#  It will save the files with a date/time stamp in the filename,
#+ bzipped, to a given directory.
#
#  Run this from crontab nightly at an off hour,
#+ as bzip2 can suck up some serious CPU on huge logs:
#  0 2 * * * /opt/sbin/archiveweblogs.sh


PROBLEM=66

# Set this to your backup dir.
BKP_DIR=/opt/backups/weblogs

# Default Apache/RedHat stuff
LOG_DAYS="4 3 2 1"
LOG_DIR=/var/log/httpd
LOG_FILES="access_log error_log"

# Default RedHat program locations
LS=/bin/ls
MV=/bin/mv
ID=/usr/bin/id
CUT=/bin/cut
COL=/usr/bin/column
BZ2=/usr/bin/bzip2

# Are we root?
USER=`$ID -u`
if [ "X$USER" != "X0" ]; then
  echo "PANIC: Only root can run this script!"
  exit $PROBLEM
fi

# Backup dir exists/writable?
if [ ! -x $BKP_DIR ]; then
  echo "PANIC: $BKP_DIR doesn't exist or isn't writable!"
  exit $PROBLEM
fi

# Move, rename and bzip2 the logs
for logday in $LOG_DAYS; do
  for logfile in $LOG_FILES; do
    MYFILE="$LOG_DIR/$logfile.$logday"
    if [ -w $MYFILE ]; then
      DTS=`$LS -lgo --time-style=+%Y%m%d $MYFILE | $COL -t | $CUT -d ' ' -f7`
      $MV $MYFILE $BKP_DIR/$logfile.$DTS
      $BZ2 $BKP_DIR/$logfile.$DTS
    else
      # Only spew an error if the file exits (ergo non-writable).
      if [ -f $MYFILE ]; then
        echo "ERROR: $MYFILE not writable. Skipping."
      fi
    fi
  done
done

exit 0
%%%&&&archiveweblogs.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@arglist.sh@@@!!!************************************************************************************
#!/bin/bash
# arglist.sh
# 多使用几个参数来调用这个脚本, 比如"one two three".

E_BADARGS=65

if [ ! -n "$1" ]
then
  echo "Usage: `basename $0` argument1 argument2 etc."
  exit $E_BADARGS
fi 

echo

index=1          # 起始计数.

echo "Listing args with /"/$*/":"
for arg in "$*"  # 如果"$*"不被""引用,那么将不能正常地工作.
do
  echo "Arg #$index = $arg"
  let "index+=1"
done             # $* 将所有的参数看成一个单词.
echo "Entire arg list seen as single word."

echo

index=1          # 重置计数(译者注: 从1开始).
                 # 如果你写这句会发生什么?

echo "Listing args with /"/$@/":"
for arg in "$@"
do
  echo "Arg #$index = $arg"
  let "index+=1"
done             # $@ 把每个参数都看成是单独的单词.
echo "Arg list seen as separate words."

echo

index=1          # 重置计数(译者注: 从1开始).

echo "Listing args with /$* (unquoted):"
for arg in $*
do
  echo "Arg #$index = $arg"
  let "index+=1"
done             # 未引用的$*将会把参数看成单独的单词.
echo "Arg list seen as separate words."

exit 0
%%%&&&arglist.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@arith-ops.sh@@@!!!************************************************************************************
#!/bin/bash
# 使用10种不同的方法计数到11.

n=1; echo -n "$n "

let "n = $n + 1"   # let "n = n + 1"  也可以.
echo -n "$n "


: $((n = $n + 1))
#  ":" 是必需的, 因为如果没有":"的话,
#+ Bash将会尝试把"$((n = $n + 1))"解释为一个命令.
echo -n "$n "

(( n = n + 1 ))
#  上边这句是一种更简单方法.
#  感谢, David Lombard, 指出这点.
echo -n "$n "

n=$(($n + 1))
echo -n "$n "

: $[ n = $n + 1 ]
#  ":" 是必需的, 因为如果没有":"的话,
#+ Bash将会尝试把"$[ n = $n + 1 ]"解释为一个命令.
#  即使"n"被初始化为字符串, 这句也能够正常运行.
echo -n "$n "

n=$[ $n + 1 ]
#  即使"n"被初始化为字符串, 这句也能够正常运行.
#* 应该尽量避免使用这种类型的结构, 因为它已经被废弃了, 而且不具可移植性.
#  感谢, Stephane Chazelas.
echo -n "$n "

# 现在来一个C风格的增量操作.
# 感谢, Frank Wang, 指出这点.

let "n++"          # let "++n"  也可以.
echo -n "$n "

(( n++ ))          # (( ++n )  也可以.
echo -n "$n "

: $(( n++ ))       # : $(( ++n )) 也可以.
echo -n "$n "

: $[ n++ ]         # : $[ ++n ]] 也可以.
echo -n "$n "

echo

exit 0
%%%&&&arith-ops.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@arith-tests.sh@@@!!!************************************************************************************
#!/bin/bash
# 算术测试.

# (( ... ))结构可以用来计算并测试算术表达式的结果.
# 退出状态将会与[ ... ]结构完全相反!

(( 0 ))
echo "Exit status of /"(( 0 ))/" is $?."         # 1

(( 1 ))
echo "Exit status of /"(( 1 ))/" is $?."         # 0

(( 5 > 4 ))                                      # 真
echo "Exit status of /"(( 5 > 4 ))/" is $?."     # 0

(( 5 > 9 ))                                      # 假
echo "Exit status of /"(( 5 > 9 ))/" is $?."     # 1

(( 5 - 5 ))                                      # 0
echo "Exit status of /"(( 5 - 5 ))/" is $?."     # 1

(( 5 / 4 ))                                      # 除法也可以.
echo "Exit status of /"(( 5 / 4 ))/" is $?."     # 0

(( 1 / 2 ))                                      # 除法的计算结果 < 1.
echo "Exit status of /"(( 1 / 2 ))/" is $?."     # 截取之后的结果为 0.
                                                 # 1

(( 1 / 0 )) 2>/dev/null                          # 除数为0, 非法计算.
#           ^^^^^^^^^^^
echo "Exit status of /"(( 1 / 0 ))/" is $?."     # 1

# "2>/dev/null"起了什么作用?
# 如果这句被删除会怎样?
# 尝试删除这句, 然后在运行这个脚本.

exit 0
%%%&&&arith-tests.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@array-append.bash@@@!!!************************************************************************************
#! /bin/bash
# array-append.bash

# Copyright (c) Michael S. Zick, 2003, All rights reserved.
# License: Unrestricted reuse in any form, for any purpose.
# Version: $ID$
#
# 在格式上, 由M.C做了一些修改.


# 数组操作是Bash特有的属性.
# 传统的UNIX /bin/sh缺乏类似的功能.


#  将这个脚本的输出通过管道传递给'more',
#+ 这么做的目的是防止输出的内容超过终端能够显示的范围.


# 依次使用下标.
declare -a array1=( zero1 one1 two1 )
# 数组中存在空缺的元素([1]未定义).
declare -a array2=( [0]=zero2 [2]=two2 [3]=three2 )

echo
echo '- Confirm that the array is really subscript sparse. -'
echo "Number of elements: 4"        # 仅仅为了演示, 所以就写死了.
for (( i = 0 ; i < 4 ; i++ ))
do
    echo "Element [$i]: ${array2[$i]}"
done
# 也可以参考一个更通用的例子, basics-reviewed.bash.


declare -a dest

# 将两个数组合并(附加)到第3个数组.
echo
echo 'Conditions: Unquoted, default IFS, All-Elements-Of operator'
echo '- Undefined elements not present, subscripts not maintained. -'
# # 那些未定义的元素不会出现; 组合时会丢弃这些元素.

dest=( ${array1[@]} ${array2[@]} )
# dest=${array1[@]}${array2[@]}     # 令人奇怪的结果, 或许是个bug.

# 现在, 打印结果.
echo
echo '- - Testing Array Append - -'
cnt=${#dest[@]}

echo "Number of elements: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
    echo "Element [$i]: ${dest[$i]}"
done

# 将数组赋值给一个数组中的元素(两次).
dest[0]=${array1[@]}
dest[1]=${array2[@]}

# 打印结果.
echo
echo '- - Testing modified array - -'
cnt=${#dest[@]}

echo "Number of elements: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
    echo "Element [$i]: ${dest[$i]}"
done

# 检查第二个元素的修改状况.
echo
echo '- - Reassign and list second element - -'

declare -a subArray=${dest[1]}
cnt=${#subArray[@]}

echo "Number of elements: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
    echo "Element [$i]: ${subArray[$i]}"
done

#  如果你使用'=${ ... }'形式
#+ 将一个数组赋值到另一个数组的一个元素中,
#+ 那么这个数组的所有元素都会被转换为一个字符串,
#+ 这个字符串中的每个数组元素都以空格进行分隔(其实是IFS的第一个字符).

# 如果原来数组中的所有元素都不包含空白符 . . .
# 如果原来的数组下标都是连续的 . . .
# 那么我们就可以将原来的数组进行恢复.

# 从修改过的第二个元素中, 将原来的数组恢复出来.
echo
echo '- - Listing restored element - -'

declare -a subArray=( ${dest[1]} )
cnt=${#subArray[@]}

echo "Number of elements: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
    echo "Element [$i]: ${subArray[$i]}"
done
echo '- - Do not depend on this behavior. - -'
echo '- - This behavior is subject to change - -'
echo '- - in versions of Bash newer than version 2.05b - -'

# MSZ: 抱歉, 之前混淆了一些要点(译者注: 指的是本书以前的版本).

exit 0
%%%&&&array-append.bash&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@array-assign.bash@@@!!!************************************************************************************
#! /bin/bash
# array-assign.bash

#  数组操作是Bash所特有的,
#+ 所以才使用".bash"作为脚本扩展名.

# Copyright (c) Michael S. Zick, 2003, All rights reserved.
# License: Unrestricted reuse in any form, for any purpose.
# Version: $ID$
#
# 说明与注释由William Park所添加.

#  基于Stephane Chazelas所提供的
#+ 出现在本书中的一个例子.

# 'times'命令的输出格式:
# User CPU &lt;space&gt; System CPU
# User CPU of dead children &lt;space&gt; System CPU of dead children

#  Bash有两种方法,
#+ 可以将一个数组的所有元素都赋值给一个新的数组变量.
#  在2.04, 2.05a和2.05b版本的Bash中,
#+ 这两种方法都会丢弃数组中的"空引用"(null值)元素.
#  另一种给数组赋值的方法将会被添加到新版本的Bash中,
#+ 这种方法采用[subscript]=value形式, 来维护数组下标与元素值之间的关系.

#  可以使用内部命令来构造一个大数组,
#+ 当然, 构造一个包含上千元素数组的其他方法
#+ 也能很好的完成任务.

declare -a bigOne=( /dev/* )
echo
echo 'Conditions: Unquoted, default IFS, All-Elements-Of'
echo "Number of elements in array is ${#bigOne[@]}"

# set -vx

 

echo
echo '- - testing: =( ${array[@]} ) - -'
times
declare -a bigTwo=( ${bigOne[@]} )
#                 ^              ^
times

echo
echo '- - testing: =${array[@]} - -'
times
declare -a bigThree=${bigOne[@]}
# 这次没用括号.
times

#  正如Stephane Chazelas所指出的, 通过比较,
#+ 可以了解到第二种格式的赋值比第三或第四种形式更快.
#
#  William Park解释:
#+ 数组bigTwo是作为一个单个字符串被赋值的,
#+ 而数组bigThree, 则是一个元素一个元素进行的赋值.
#  所以, 实质上是:
#                   bigTwo=( [0]="... ... ..." )
#                   bigThree=( [0]="..." [1]="..." [2]="..." ... )


#  在本书的例子中, 我还是会继续使用第一种形式,
#+ 因为我认为这种形式更有利于将问题阐述清楚.

#  在我所使用的例子中, 在其中复用的部分,
#+ 还是使用了第二种形式, 那是因为这种形式更快.

# MSZ: 很抱歉早先的疏忽(译者: 应是指本书的老版本).


#  注意事项:
#  ---------
#  31行和43行的"declare -a"语句其实不是必需的,
#+ 因为Array=( ... )形式
#+ 只能用于数组赋值.
#  然而, 如果省略这些声明的话,
#+ 会导致脚本后边的相关操作变慢.
#  试一下, 看看发生了什么.

exit 0
%%%&&&array-assign.bash&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@array-function.sh@@@!!!************************************************************************************
#!/bin/bash
# array-function.sh: 将数组传递到函数中与...
#                   从函数中"返回"一个数组


Pass_Array ()
{
  local passed_array   # 局部变量.
  passed_array=( `echo "$1"` )
  echo "${passed_array[@]}"
  #  列出这个新数组中的所有元素,
  #+ 这个新数组是在函数内声明的, 也是在函数内赋值的.
}


original_array=( element1 element2 element3 element4 element5 )

echo
echo "original_array = ${original_array[@]}"
#                      列出原始数组的所有元素.


# 下面是关于如何将数组传递给函数的技巧.
# **********************************
argument=`echo ${original_array[@]}`
# **********************************
#  将原始数组中所有的元素都用空格进行分隔,
#+ 然后合并成一个字符串, 最后赋值给一个变量.
#
# 注意, 如果只把数组传递给函数, 那是不行的.


# 下面是让数组作为"返回值"的技巧.
# *****************************************
returned_array=( `Pass_Array "$argument"` )
# *****************************************
# 将函数中'echo'出来的输出赋值给数组变量.

echo "returned_array = ${returned_array[@]}"

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

#  现在, 再试一次,
#+ 尝试一下, 在函数外面访问(列出)数组.
Pass_Array "$argument"

# 函数自身可以列出数组, 但是...
#+ 从函数外部访问数组是被禁止的.
echo "Passed array (within function) = ${passed_array[@]}"
# NULL值, 因为这个变量是函数内部的局部变量.

echo

exit 0
%%%&&&array-function.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@array-ops.sh@@@!!!************************************************************************************
#!/bin/bash
# array-ops.sh: 更多有趣的数组用法.


array=( zero one two three four five )
# 数组元素 0   1   2    3     4    5

echo ${array[0]}       #  0
echo ${array:0}        #  0
                       #  第一个元素的参数扩展,
                       #+ 从位置0(#0)开始(即第一个字符).
echo ${array:1}        #  ero
                       #  第一个元素的参数扩展,
                       #+ 从位置1(#1)开始(即第2个字符).

echo "--------------"

echo ${#array[0]}      #  4
                       #  第一个数组元素的长度.
echo ${#array}         #  4
                       #  第一个数组元素的长度.
                       #  (另一种表示形式)

echo ${#array[1]}      #  3
                       #  第二个数组元素的长度.
                       #  Bash中的数组是从0开始索引的.

echo ${#array[*]}      #  6
                       #  数组中的元素个数.
echo ${#array[@]}      #  6
                       #  数组中的元素个数.

echo "--------------"

array2=( [0]="first element" [1]="second element" [3]="fourth element" )

echo ${array2[0]}      # 第一个元素
echo ${array2[1]}      # 第二个元素
echo ${array2[2]}      #
                       # 因为并没有被初始化, 所以此值为null.
echo ${array2[3]}      # 第四个元素


exit 0
%%%&&&array-ops.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@array-strops.sh@@@!!!************************************************************************************
#!/bin/bash
# array-strops.sh: 用于数组的字符串操作.
# 本脚本由Michael Zick所编写.
# 授权使用在本书中.

#  一般来说, 任何类似于${name ... }(这种形式)的字符串操作
#+ 都能够应用于数组中的所有字符串元素,
#+ 比如说${name[@] ... }或${name[*] ...}这两种形式.


arrayZ=( one two three four five five )

echo

# 提取尾部的子串
echo ${arrayZ[@]:0}     # one two three four five five
                        # 所有元素.

echo ${arrayZ[@]:1}     # two three four five five
                        # element[0]后边的所有元素.

echo ${arrayZ[@]:1:2}   # two three
                        # 只提取element[0]后边的两个元素.

echo "-----------------------"

#  子串删除
#  从字符串的开头删除最短的匹配,
#+ 匹配的子串也可以是正则表达式.

echo ${arrayZ[@]#f*r}   # one two three five five
                        # 匹配将应用于数组的所有元素.
                        # 匹配到了"four", 并且将它删除.

# 从字符串的开头删除最长的匹配
echo ${arrayZ[@]##t*e}  # one two four five five
                        # 匹配将应用于数组的所有元素.
                        # 匹配到了"three", 并且将它删除.

# 从字符串的结尾删除最短的匹配
echo ${arrayZ[@]%h*e}   # one two t four five five
                        # 匹配将应用于数组的所有元素.
                        # 匹配到了"hree", 并且将它删除.

# 从字符串的结尾删除最长的匹配
echo ${arrayZ[@]%%t*e}  # one two four five five
                        # 匹配将应用于数组的所有元素.
                        # 匹配到了"three", 并且将它删除.

echo "-----------------------"

# 子串替换

# 第一个匹配到的子串将会被替换
echo ${arrayZ[@]/fiv/XYZ}   # one two three four XYZe XYZe
                            # 匹配将应用于数组的所有元素.

# 所有匹配到的子串都会被替换
echo ${arrayZ[@]//iv/YY}    # one two three four fYYe fYYe
                            # 匹配将应用于数组的所有元素.

# 删除所有的匹配子串
# 如果没有指定替换字符串的话, 那就意味着'删除'
echo ${arrayZ[@]//fi/}      # one two three four ve ve
                            # 匹配将应用于数组的所有元素.

# 替换字符串前端子串
echo ${arrayZ[@]/#fi/XY}    # one two three four XYve XYve
                            # 匹配将应用于数组的所有元素.

# 替换字符串后端子串
echo ${arrayZ[@]/%ve/ZZ}    # one two three four fiZZ fiZZ
                            # 匹配将应用于数组的所有元素.

echo ${arrayZ[@]/%o/XX}     # one twXX three four five five
                            # 为什么?

echo "-----------------------"


# 在将处理后的结果发送到awk(或者其他的处理工具)之前 --
# 回忆一下:
#   $( ... )是命令替换.
#   函数作为子进程运行.
#   函数结果输出到stdout.
#   用read来读取函数的stdout.
#   使用name[@]表示法指定了一个"for-each"操作.

newstr() {
    echo -n "!!!"
}

echo ${arrayZ[@]/%e/$(newstr)}
# on!!! two thre!!! four fiv!!! fiv!!!
# Q.E.D: 替换动作实际上是一个'赋值'.

#  使用"For-Each"形式的
echo ${arrayZ[@]//*/$(newstr optional_arguments)}
#  现在, 如果Bash只将匹配到的子串作为$0
#+ 传递给将被调用的函数 . . .

echo

exit 0
%%%&&&array-strops.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@arrow-detect.sh@@@!!!************************************************************************************
#!/bin/bash
# arrow-detect.sh: 检测方向键, 和一些非打印字符的按键.
# 感谢, Sandro Magi, 告诉了我们怎么做到这点.

# --------------------------------------------
# 按键所产生的字符编码.
arrowup='/[A'
arrowdown='/[B'
arrowrt='/[C'
arrowleft='/[D'
insert='/[2'
delete='/[3'
# --------------------------------------------

SUCCESS=0
OTHER=65

echo -n "Press a key...  "
# 如果不是上边列表所列出的按键, 可能还是需要按回车. (译者注: 因为一般按键是一个字符)
read -n3 key                      # 读取3个字符.

echo -n "$key" | grep "$arrowup"  # 检查输入字符是否匹配.
if [ "$?" -eq $SUCCESS ]
then
  echo "Up-arrow key pressed."
  exit $SUCCESS
fi

echo -n "$key" | grep "$arrowdown"
if [ "$?" -eq $SUCCESS ]
then
  echo "Down-arrow key pressed."
  exit $SUCCESS
fi

echo -n "$key" | grep "$arrowrt"
if [ "$?" -eq $SUCCESS ]
then
  echo "Right-arrow key pressed."
  exit $SUCCESS
fi

echo -n "$key" | grep "$arrowleft"
if [ "$?" -eq $SUCCESS ]
then
  echo "Left-arrow key pressed."
  exit $SUCCESS
fi

echo -n "$key" | grep "$insert"
if [ "$?" -eq $SUCCESS ]
then
  echo "/"Insert/" key pressed."
  exit $SUCCESS
fi

echo -n "$key" | grep "$delete"
if [ "$?" -eq $SUCCESS ]
then
  echo "/"Delete/" key pressed."
  exit $SUCCESS
fi


echo " Some other key pressed."

exit $OTHER

#  练习:
#  -----
#  1) 使用'case'结构来代替'if'结构,
#+    这样可以简化这个脚本.
#  2) 添加 "Home", "End", "PgUp", 和 "PgDn" 这些按键的检查.
%%%&&&arrow-detect.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


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

assert ()                 #  如果条件为false,
{                         #+ 那么就打印错误信息并退出脚本.
  E_PARAM_ERR=98
  E_ASSERT_FAILED=99


  if [ -z "$2" ]          # 传递进来的参数个数不够.
  then
    return $E_PARAM_ERR   # 什么都不做就return.
  fi

  lineno=$2

  if [ ! $1 ]
  then
    echo "Assertion failed:  /"$1/""
    echo "File /"$0/", line $lineno"
    exit $E_ASSERT_FAILED
  # else
  #   返回
  #   然后继续执行脚本余下的代码.
  fi 
}   


a=5
b=4
condition="$a -lt $b"     # 产生错误信息并退出脚本.
                          #  尝试把这个"条件"放到其他的地方,
                          #+ 然后看看发生了什么.

assert "$condition" $LINENO
# 只有在"assert"成功时, 脚本余下的代码才会继续执行.


# 这里放置的是其他的一些命令.
# ...
echo "This statement echoes only if the /"assert/" does not fail."
# ...
# 这里也放置其他一些命令.

exit 0
%%%&&&assert.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@avoid-subshell.sh@@@!!!************************************************************************************
#!/bin/bash
# avoid-subshell.sh
# 由Matthew Walker所提出的建议.

Lines=0

echo

cat myfile.txt | while read line;  #  (译者注: 管道会产生子shell)
                 do {
                   echo $line
                   (( Lines++ ));  #  增加这个变量的值
                                   #+ 但是外部循环却不能访问.
                                   #  子shell问题.
                 }
                 done

echo "Number of lines read = $Lines"     # 0
                                         # 错误!

echo "------------------------"


exec 3&lt;&gt; myfile.txt
while read line &lt;&3
do {
  echo "$line"
  (( Lines++ ));                   #  增加这个变量的值
                                   #+ 现在外部循环就可以访问了.
                                   #  没有子shell, 现在就没问题了.
}
done
exec 3&gt;&-

echo "Number of lines read = $Lines"     # 8

echo

exit 0

# 下边这些行是这个脚本的结果, 脚本是不会走到这里的.

$ cat myfile.txt

Line 1.
Line 2.
Line 3.
Line 4.
Line 5.
Line 6.
Line 7.
Line 8.
%%%&&&avoid-subshell.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@background-loop.sh@@@!!!************************************************************************************
#!/bin/bash
# background-loop.sh

for i in 1 2 3 4 5 6 7 8 9 10            # 第一个循环.
do
  echo -n "$i "
done & # 在后台运行这个循环.
       # 在第2个循环之后, 将在某些时候执行.

echo   # 这个'echo'某些时候将不会显示.

for i in 11 12 13 14 15 16 17 18 19 20   # 第二个循环.
do
  echo -n "$i "
done 

echo   # 这个'echo'某些时候将不会显示.

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

# 期望的输出应该是:
# 1 2 3 4 5 6 7 8 9 10
# 11 12 13 14 15 16 17 18 19 20

# 然而实际的结果有可能是:
# 11 12 13 14 15 16 17 18 19 20
# 1 2 3 4 5 6 7 8 9 10 bozo $
# (第2个'echo'没执行, 为什么?)

# 也可能是:
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# (第1个'echo'没执行, 为什么?)

# 非常少见的执行结果, 也有可能是:
# 11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20
# 前台的循环先于后台的执行.

exit 0

#  Nasimuddin Ansari 建议加一句 sleep 1
#+ 在6行和14行的 echo -n "$i" 之后加这句.
#+ 为了真正的乐趣.
%%%&&&background-loop.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@bad-op.sh@@@!!!************************************************************************************
#!/bin/bash
# bad-op.sh: 尝试一下对整数使用字符串比较.

echo
number=1

# 下面的"while循环"有两个错误:
#+ 一个比较明显, 而另一个比较隐蔽.

while [ "$number" < 5 ]    # 错! 应该是:  while [ "$number" -lt 5 ]
do
  echo -n "$number "
  let "number += 1"
done 
#  如果你企图运行这个错误的脚本, 那么就会得到一个错误消息:
#+ bad-op.sh: line 10: 5: No such file or directory
#  在单中括号结构([ ])中, "<"必须被转义.
#+ 即便如此, 比较两个整数还是错误的.


echo "---------------------"


while [ "$number" /< 5 ]    #  1 2 3 4
do                          #
  echo -n "$number "        #  看起来*好像可以工作, 但是 . . .
  let "number += 1"         #+ 事实上是比较ASCII码,
done                        #+ 而不是整数比较.

echo; echo "---------------------"

# 这么做会产生问题. 比如:

lesser=5
greater=105

if [ "$greater" /< "$lesser" ]
then
  echo "$greater is less than $lesser"
fi                          # 105 is less than 5
#  事实上, 在字符串比较中(按照ASCII码的顺序)
#+ "105"小于"5".

echo

exit 0
%%%&&&bad-op.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@badread.sh@@@!!!************************************************************************************
#!/bin/bash
#  badread.sh:
#  尝试使用'echo'和'read'命令
#+ 非交互的给变量赋值.

a=aaa
b=bbb
c=ccc

echo "one two three" | read a b c
# 尝试重新给变量a, b, 和c赋值.

echo
echo "a = $a"  # a = aaa
echo "b = $b"  # b = bbb
echo "c = $c"  # c = ccc
# 重新赋值失败.

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

# 试试下边这种方法.

var=`echo "one two three"`
set -- $var
a=$1; b=$2; c=$3

echo "-------"
echo "a = $a"  # a = one
echo "b = $b"  # b = two
echo "c = $c"  # c = three
# 重新赋值成功.

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

#  也请注意, echo到'read'的值只会在子shell中起作用.
#  所以, 变量的值*只*会在子shell中被修改.

a=aaa          # 重新开始.
b=bbb
c=ccc

echo; echo
echo "one two three" | ( read a b c;
echo "Inside subshell: "; echo "a = $a"; echo "b = $b"; echo "c = $c" )
# a = one
# b = two
# c = three
echo "-----------------"
echo "Outside subshell: "
echo "a = $a"  # a = aaa
echo "b = $b"  # b = bbb
echo "c = $c"  # c = ccc
echo

exit 0
%%%&&&badread.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@base.sh@@@!!!************************************************************************************
#!/bin/bash
##########################################################################
# 脚本       : base.sh - 用不同的数制来打印数字 (Bourne Shell)
# 作者       : Heiner Steven (heiner.steven@odn.de)
# 日期       : 07-03-95
# 类型       : 桌面
# $Id: base.sh,v 1.2 2000/02/06 19:55:35 heiner Exp $
# ==> 上边这行是RCS ID信息.
##########################################################################
# 描述
#
# 修改纪录
# 21-03-95 stv fixed error occuring with 0xb as input (0.2)
##########################################################################

# ==> 在本书中使用这个脚本通过了原作者的授权.
# ==> 注释是本书作者添加的.

NOARGS=65
PN=`basename "$0"`          # 程序名
VER=`echo '$Revision: 1.2 $' | cut -d' ' -f2`  # ==> VER=1.2

Usage () {
    echo "$PN - print number to different bases, $VER (stv '95)
usage: $PN [number ...]

If no number is given, the numbers are read from standard input.
A number may be
    binary (base 2)  starting with 0b (i.e. 0b1100)
    octal (base 8)  starting with 0  (i.e. 014)
    hexadecimal (base 16) starting with 0x (i.e. 0xc)
    decimal   otherwise (i.e. 12)" >&2
    exit $NOARGS
}   # ==> 打印出用法信息的函数.

Msg () {
    for i   # ==> 省略[list].
    do echo "$PN: $i" >&2
    done
}

Fatal () { Msg "$@"; exit 66; }

PrintBases () {
    # 决定数字的数制
    for i      # ==> 省略[list]...
    do         # ==> 所以是对命令行参数进行操作.
 case "$i" in
     0b*)  ibase=2;; # 2进制
     0x*|[a-f]*|[A-F]*) ibase=16;; # 16进制
     0*)   ibase=8;; # 8进制
     [1-9]*)  ibase=10;; # 10进制
     *)
  Msg "illegal number $i - ignored"
  continue;;
 esac

 # 去掉前缀, 将16进制数字转换为大写(bc命令需要这么做)
 number=`echo "$i" | sed -e 's:^0[bBxX]::' | tr '[a-f]' '[A-F]'`
 # ==> 使用":" 作为sed分隔符, 而不使用"/".

 # 将数字转换为10进制
 dec=`echo "ibase=$ibase; $number" | bc`  # ==> 'bc'是个计算工具.
 case "$dec" in
     [0-9]*) ;;    # 数字没问题
     *)  continue;;   # 错误: 忽略
 esac

 # 在一行上打印所有转换后的数字.
 # ==> 'here document'提供命令列表给'bc'.
 echo `bc <<!
     obase=16; "hex="; $dec
     obase=10; "dec="; $dec
     obase=8;  "oct="; $dec
     obase=2;  "bin="; $dec
!
    ` | sed -e 's: : :g'

    done
}

while [ $# -gt 0 ]
# ==>  这里必须使用一个"while循环",
# ==>+ 因为所有的case都可能退出循环或者
# ==>+ 结束脚本.
# ==> (感谢, Paulo Marcel Coelho Aragao.)
do
    case "$1" in
 --)     shift; break;;
 -h)     Usage;;                 # ==> 帮助信息.
 -*)     Usage;;
         *)     break;;   # 第一个数字
    esac   # ==> 对于非法输入进行更严格检查是非常有用的.
    shift
done

if [ $# -gt 0 ]
then
    PrintBases "$@"
else     # 从stdin中读取
    while read line
    do
 PrintBases $line
    done
fi


exit 0
%%%&&&base.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


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

echo "Greetings from the Bash part of the script."
# 这里可以放置更多的Bash命令.

exit 0
# 脚本的Bash部分结束.

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

#!/usr/bin/perl
# 脚本的这部分必须使用-x选项来调用.

print "Greetings from the Perl part of the script./n";
# 这里可以放置更多的Perl命令.

# 脚本的Perl部分结束.
%%%&&&bashandperl.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


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

# bashpodder.sh:
# By Linc 10/1/2004
# Find the latest script at http://linc.homeunix.org:8080/scripts/bashpodder
# Last revision 12/14/2004 - Many Contributors!
# If you use this and have made improvements or have comments
# drop me an email at linc dot fessenden at gmail dot com
# I'd appreciate it!

# ==>  ABS Guide extra comments.

# ==>  Author of this script has kindly granted permission
# ==>+ for inclusion in ABS Guide.


# ==> ################################################################
#
# ==> What is "podcasting"?

# ==> It's broadcasting "radio shows" over the Internet.
# ==> These shows can be played on iPods and other music file players.

# ==> This script makes it possible.
# ==> See documentation at the script author's site, above.

# ==> ################################################################


# Make script crontab friendly:
cd $(dirname $0)
# ==> Change to directory where this script lives.

# datadir is the directory you want podcasts saved to:
datadir=$(date +%Y-%m-%d)
# ==> Will create a directory with the name: YYYY-MM-DD

# Check for and create datadir if necessary:
if test ! -d $datadir
        then
        mkdir $datadir
fi

# Delete any temp file:
rm -f temp.log

# Read the bp.conf file and wget any url not already in the podcast.log file:
while read podcast
        do # ==> Main action follows.
        file=$(wget -q $podcast -O - | tr '/r' '/n' | tr /' /" | sed -n 's/.*url="/([^"]*/)".*//1/p')
        for url in $file
                do
                echo $url >> temp.log
                if ! grep "$url" podcast.log > /dev/null
                        then
                        wget -q -P $datadir "$url"
                fi
                done
        done < bp.conf

# Move dynamically created log file to permanent log file:
cat podcast.log >> temp.log
sort temp.log | uniq > podcast.log
rm temp.log
# Create an m3u playlist:
ls $datadir | grep -v m3u > $datadir/podcast.m3u


exit 0

#################################################
For a different scripting approach to Podcasting,
see Phil Salkie's article,
"Internet Radio to Podcast with Shell Tools"
in the September, 2005 issue of LINUX JOURNAL,
http://www.linuxjournal.com/article/8171
#################################################
%%%&&&bashpodder.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@basics-reviewed.bash@@@!!!************************************************************************************
#!/bin/bash
# basics-reviewed.bash

# File extension == *.bash == specific to Bash

#   Copyright (c) Michael S. Zick, 2003; All rights reserved.
#   License: Use in any form, for any purpose.
#   Revision: $ID$
#
#              Edited for layout by M.C.
#   (author of the "Advanced Bash Scripting Guide")


#  This script tested under Bash versions 2.04, 2.05a and 2.05b.
#  It may not work with earlier versions.
#  This demonstration script generates one --intentional--
#+ "command not found" error message. See line 394.

#  The current Bash maintainer, Chet Ramey, has fixed the items noted
#+ for an upcoming version of Bash.

 

        ###-------------------------------------------###
        ###  Pipe the output of this script to 'more' ###
        ###+ else it will scroll off the page.        ###
        ###                                           ###
        ###  You may also redirect its output         ###
        ###+ to a file for examination.               ### 
        ###-------------------------------------------###

 

#  Most of the following points are described at length in
#+ the text of the foregoing "Advanced Bash Scripting Guide."
#  This demonstration script is mostly just a reorganized presentation.
#      -- msz

# Variables are not typed unless otherwise specified.

#  Variables are named. Names must contain a non-digit.
#  File descriptor names (as in, for example: 2>&1)
#+ contain ONLY digits.

# Parameters and Bash array elements are numbered.
# (Parameters are very similar to Bash arrays.)

# A variable name may be undefined (null reference).
unset VarNull

# A variable name may be defined but empty (null contents).
VarEmpty=''                         # Two, adjacent, single quotes.

# A variable name my be defined and non-empty
VarSomething='Literal'

# A variable may contain:
#   * A whole number as a signed 32-bit (or larger) integer
#   * A string
# A variable may also be an array.

#  A string may contain embedded blanks and may be treated
#+ as if it where a function name with optional arguments.

#  The names of variables and the names of functions
#+ are in different namespaces.


#  A variable may be defined as a Bash array either explicitly or
#+ implicitly by the syntax of the assignment statement.
#  Explicit:
declare -a ArrayVar

 

# The echo command is a built-in.
echo $VarSomething

# The printf command is a built-in.
# Translate %s as: String-Format
printf %s $VarSomething         # No linebreak specified, none output.
echo                            # Default, only linebreak output.

 


# The Bash parser word breaks on whitespace.
# Whitespace, or the lack of it is significant.
# (This holds true in general; there are, of course, exceptions.)

 


# Translate the DOLLAR_SIGN character as: Content-Of.

# Extended-Syntax way of writing Content-Of:
echo ${VarSomething}

#  The ${ ... } Extended-Syntax allows more than just the variable
#+ name to be specified.
#  In general, $VarSomething can always be written as: ${VarSomething}.

# Call this script with arguments to see the following in action.

 

#  Outside of double-quotes, the special characters @ and *
#+ specify identical behavior.
#  May be pronounced as: All-Elements-Of.

#  Without specification of a name, they refer to the
#+ pre-defined parameter Bash-Array.

 

# Glob-Pattern references
echo $*                         # All parameters to script or function
echo ${*}                       # Same

# Bash disables filename expansion for Glob-Patterns.
# Only character matching is active.


# All-Elements-Of references
echo $@                         # Same as above
echo ${@}                       # Same as above

 


#  Within double-quotes, the behavior of Glob-Pattern references
#+ depends on the setting of IFS (Input Field Separator).
#  Within double-quotes, All-Elements-Of references behave the same.


#  Specifying only the name of a variable holding a string refers
#+ to all elements (characters) of a string.


#  To specify an element (character) of a string,
#+ the Extended-Syntax reference notation (see below) MAY be used.

 


#  Specifying only the name of a Bash array references
#+ the subscript zero element,
#+ NOT the FIRST DEFINED nor the FIRST WITH CONTENTS element.

#  Additional qualification is needed to reference other elements,
#+ which means that the reference MUST be written in Extended-Syntax.
#  The general form is: ${name[subscript]}.

#  The string forms may also be used: ${name:subscript}
#+ for Bash-Arrays when referencing the subscript zero element.


# Bash-Arrays are implemented internally as linked lists,
#+ not as a fixed area of storage as in some programming languages.


#   Characteristics of Bash arrays (Bash-Arrays):
#   --------------------------------------------

#   If not otherwise specified, Bash-Array subscripts begin with
#+  subscript number zero. Literally: [0]
#   This is called zero-based indexing.
###
#   If not otherwise specified, Bash-Arrays are subscript packed
#+  (sequential subscripts without subscript gaps).
###
#   Negative subscripts are not allowed.
###
#   Elements of a Bash-Array need not all be of the same type.
###
#   Elements of a Bash-Array may be undefined (null reference).
#       That is, a Bash-Array my be "subscript sparse."
###
#   Elements of a Bash-Array may be defined and empty (null contents).
###
#   Elements of a Bash-Array may contain:
#     * A whole number as a signed 32-bit (or larger) integer
#     * A string
#     * A string formated so that it appears to be a function name
#     + with optional arguments
###
#   Defined elements of a Bash-Array may be undefined (unset).
#       That is, a subscript packed Bash-Array may be changed
#   +   into a subscript sparse Bash-Array.
###
#   Elements may be added to a Bash-Array by defining an element
#+  not previously defined.
###
# For these reasons, I have been calling them "Bash-Arrays".
# I'll return to the generic term "array" from now on.
#     -- msz

 


#  Demo time -- initialize the previously declared ArrayVar as a
#+ sparse array.
#  (The 'unset ... ' is just documentation here.)

unset ArrayVar[0]                   # Just for the record
ArrayVar[1]=one                     # Unquoted literal
ArrayVar[2]=''                      # Defined, and empty
unset ArrayVar[3]                   # Just for the record
ArrayVar[4]='four'                  # Quoted literal

 

# Translate the %q format as: Quoted-Respecting-IFS-Rules.
echo
echo '- - Outside of double-quotes - -'
###
printf %q ${ArrayVar[*]}            # Glob-Pattern All-Elements-Of
echo
echo 'echo command:'${ArrayVar[*]}
###
printf %q ${ArrayVar[@]}            # All-Elements-Of
echo
echo 'echo command:'${ArrayVar[@]}

# The use of double-quotes may be translated as: Enable-Substitution.

# There are five cases recognized for the IFS setting.

echo
echo '- - Within double-quotes - Default IFS of space-tab-newline - -'
IFS=$'/x20'$'/x09'$'/x0A'           #  These three bytes,
                                    #+ in exactly this order.


printf %q "${ArrayVar[*]}"          # Glob-Pattern All-Elements-Of
echo
echo 'echo command:'"${ArrayVar[*]}"
###
printf %q "${ArrayVar[@]}"          # All-Elements-Of
echo
echo 'echo command:'"${ArrayVar[@]}"


echo
echo '- - Within double-quotes - First character of IFS is ^ - -'
# Any printing, non-whitespace character should do the same.
IFS='^'$IFS                         # ^ + space tab newline
###
printf %q "${ArrayVar[*]}"          # Glob-Pattern All-Elements-Of
echo
echo 'echo command:'"${ArrayVar[*]}"
###
printf %q "${ArrayVar[@]}"          # All-Elements-Of
echo
echo 'echo command:'"${ArrayVar[@]}"


echo
echo '- - Within double-quotes - Without whitespace in IFS - -'
IFS='^:%!'
###
printf %q "${ArrayVar[*]}"          # Glob-Pattern All-Elements-Of
echo
echo 'echo command:'"${ArrayVar[*]}"
###
printf %q "${ArrayVar[@]}"          # All-Elements-Of
echo
echo 'echo command:'"${ArrayVar[@]}"


echo
echo '- - Within double-quotes - IFS set and empty - -'
IFS=''
###
printf %q "${ArrayVar[*]}"          # Glob-Pattern All-Elements-Of
echo
echo 'echo command:'"${ArrayVar[*]}"
###
printf %q "${ArrayVar[@]}"          # All-Elements-Of
echo
echo 'echo command:'"${ArrayVar[@]}"


echo
echo '- - Within double-quotes - IFS undefined - -'
unset IFS
###
printf %q "${ArrayVar[*]}"          # Glob-Pattern All-Elements-Of
echo
echo 'echo command:'"${ArrayVar[*]}"
###
printf %q "${ArrayVar[@]}"          # All-Elements-Of
echo
echo 'echo command:'"${ArrayVar[@]}"


# Put IFS back to the default.
# Default is exactly these three bytes.
IFS=$'/x20'$'/x09'$'/x0A'           # In exactly this order.

# Interpretation of the above outputs:
#   A Glob-Pattern is I/O; the setting of IFS matters.
###
#   An All-Elements-Of does not consider IFS settings.
###
#   Note the different output using the echo command and the
#+  quoted format operator of the printf command.


#  Recall:
#   Parameters are similar to arrays and have the similar behaviors.
###
#  The above examples demonstrate the possible variations.
#  To retain the shape of a sparse array, additional script
#+ programming is required.
###
#  The source code of Bash has a routine to output the
#+ [subscript]=value   array assignment format.
#  As of version 2.05b, that routine is not used,
#+ but that might change in future releases.

 

# The length of a string, measured in non-null elements (characters):
echo
echo '- - Non-quoted references - -'
echo 'Non-Null character count: '${#VarSomething}' characters.'

# test='Lit'$'/x00''eral'           # $'/x00' is a null character.
# echo ${#test}                     # See that?

 

#  The length of an array, measured in defined elements,
#+ including null content elements.
echo
echo 'Defined content count: '${#ArrayVar[@]}' elements.'
# That is NOT the maximum subscript (4).
# That is NOT the range of the subscripts (1 . . 4 inclusive).
# It IS the length of the linked list.
###
#  Both the maximum subscript and the range of the subscripts may
#+ be found with additional script programming.

# The length of a string, measured in non-null elements (characters):
echo
echo '- - Quoted, Glob-Pattern references - -'
echo 'Non-Null character count: '"${#VarSomething}"' characters.'

#  The length of an array, measured in defined elements,
#+ including null-content elements.
echo
echo 'Defined element count: '"${#ArrayVar[*]}"' elements.'

#  Interpretation: Substitution does not effect the ${# ... } operation.
#  Suggestion:
#  Always use the All-Elements-Of character
#+ if that is what is intended (independence from IFS).

 

#  Define a simple function.
#  I include an underscore in the name
#+ to make it distinctive in the examples below.
###
#  Bash separates variable names and function names
#+ in different namespaces.
#  The Mark-One eyeball isn't that advanced.
###
_simple() {
    echo -n 'SimpleFunc'$@          #  Newlines are swallowed in
}                                   #+ result returned in any case.


# The ( ... ) notation invokes a command or function.
# The $( ... ) notation is pronounced: Result-Of.


# Invoke the function _simple
echo
echo '- - Output of function _simple - -'
_simple                             # Try passing arguments.
echo
# or
(_simple)                           # Try passing arguments.
echo

echo '- Is there a variable of that name? -'
echo $_simple not defined           # No variable by that name.

# Invoke the result of function _simple (Error msg intended)

###
$(_simple)                          # Gives an error message:
#                          line 394: SimpleFunc: command not found
#                          ---------------------------------------

echo
###

#  The first word of the result of function _simple
#+ is neither a valid Bash command nor the name of a defined function.
###
# This demonstrates that the output of _simple is subject to evaluation.
###
# Interpretation:
#   A function can be used to generate in-line Bash commands.


# A simple function where the first word of result IS a bash command:
###
_print() {
    echo -n 'printf %q '$@
}

echo '- - Outputs of function _print - -'
_print parm1 parm2                  # An Output NOT A Command.
echo

$(_print parm1 parm2)               #  Executes: printf %q parm1 parm2
                                    #  See above IFS examples for the
                                    #+ various possibilities.
echo

$(_print $VarSomething)             # The predictable result.
echo

 

# Function variables
# ------------------

echo
echo '- - Function variables - -'
# A variable may represent a signed integer, a string or an array.
# A string may be used like a function name with optional arguments.

# set -vx                           #  Enable if desired
declare -f funcVar                  #+ in namespace of functions

funcVar=_print                      # Contains name of function.
$funcVar parm1                      # Same as _print at this point.
echo

funcVar=$(_print )                  # Contains result of function.
$funcVar                            # No input, No output.
$funcVar $VarSomething              # The predictable result.
echo

funcVar=$(_print $VarSomething)     #  $VarSomething replaced HERE.
$funcVar                            #  The expansion is part of the
echo                                #+ variable contents.

funcVar="$(_print $VarSomething)"   #  $VarSomething replaced HERE.
$funcVar                            #  The expansion is part of the
echo                                #+ variable contents.

#  The difference between the unquoted and the double-quoted versions
#+ above can be seen in the "protect_literal.sh" example.
#  The first case above is processed as two, unquoted, Bash-Words.
#  The second case above is processed as one, quoted, Bash-Word.

 


# Delayed replacement
# -------------------

echo
echo '- - Delayed replacement - -'
funcVar="$(_print '$VarSomething')" # No replacement, single Bash-Word.
eval $funcVar                       # $VarSomething replaced HERE.
echo

VarSomething='NewThing'
eval $funcVar                       # $VarSomething replaced HERE.
echo

# Restore the original setting trashed above.
VarSomething=Literal

#  There are a pair of functions demonstrated in the
#+ "protect_literal.sh" and "unprotect_literal.sh" examples.
#  These are general purpose functions for delayed replacement literals
#+ containing variables.

 

 

# REVIEW:
# ------

#  A string can be considered a Classic-Array of elements (characters).
#  A string operation applies to all elements (characters) of the string
#+ (in concept, anyway).
###
#  The notation: ${array_name[@]} represents all elements of the
#+ Bash-Array: array_name.
###
#  The Extended-Syntax string operations can be applied to all
#+ elements of an array.
###
#  This may be thought of as a For-Each operation on a vector of strings.
###
#  Parameters are similar to an array.
#  The initialization of a parameter array for a script
#+ and a parameter array for a function only differ
#+ in the initialization of ${0}, which never changes its setting.
###
#  Subscript zero of the script's parameter array contains
#+ the name of the script.
###
#  Subscript zero of a function's parameter array DOES NOT contain
#+ the name of the function.
#  The name of the current function is accessed by the $FUNCNAME variable.
###
#  A quick, review list follows (quick, not short).

echo
echo '- - Test (but not change) - -'
echo '- null reference -'
echo -n ${VarNull-'NotSet'}' '          # NotSet
echo ${VarNull}                         # NewLine only
echo -n ${VarNull:-'NotSet'}' '         # NotSet
echo ${VarNull}                         # Newline only

echo '- null contents -'
echo -n ${VarEmpty-'Empty'}' '          # Only the space
echo ${VarEmpty}                        # Newline only
echo -n ${VarEmpty:-'Empty'}' '         # Empty
echo ${VarEmpty}                        # Newline only

echo '- contents -'
echo ${VarSomething-'Content'}          # Literal
echo ${VarSomething:-'Content'}         # Literal

echo '- Sparse Array -'
echo ${ArrayVar[@]-'not set'}

# ASCII-Art time
# State     Y==yes, N==no
#           -       :-
# Unset     Y       Y       ${# ... } == 0
# Empty     N       Y       ${# ... } == 0
# Contents  N       N       ${# ... } > 0

#  Either the first and/or the second part of the tests
#+ may be a command or a function invocation string.
echo
echo '- - Test 1 for undefined - -'
declare -i t
_decT() {
    t=$t-1
}

# Null reference, set: t == -1
t=${#VarNull}                           # Results in zero.
${VarNull- _decT }                      # Function executes, t now -1.
echo $t

# Null contents, set: t == 0
t=${#VarEmpty}                          # Results in zero.
${VarEmpty- _decT }                     # _decT function NOT executed.
echo $t

# Contents, set: t == number of non-null characters
VarSomething='_simple'                  # Set to valid function name.
t=${#VarSomething}                      # non-zero length
${VarSomething- _decT }                 # Function _simple executed.
echo $t                                 # Note the Append-To action.

# Exercise: clean up that example.
unset t
unset _decT
VarSomething=Literal

echo
echo '- - Test and Change - -'
echo '- Assignment if null reference -'
echo -n ${VarNull='NotSet'}' '          # NotSet NotSet
echo ${VarNull}
unset VarNull

echo '- Assignment if null reference -'
echo -n ${VarNull:='NotSet'}' '         # NotSet NotSet
echo ${VarNull}
unset VarNull

echo '- No assignment if null contents -'
echo -n ${VarEmpty='Empty'}' '          # Space only
echo ${VarEmpty}
VarEmpty=''

echo '- Assignment if null contents -'
echo -n ${VarEmpty:='Empty'}' '         # Empty Empty
echo ${VarEmpty}
VarEmpty=''

echo '- No change if already has contents -'
echo ${VarSomething='Content'}          # Literal
echo ${VarSomething:='Content'}         # Literal


# "Subscript sparse" Bash-Arrays
###
#  Bash-Arrays are subscript packed, beginning with
#+ subscript zero unless otherwise specified.
###
#  The initialization of ArrayVar was one way
#+ to "otherwise specify".  Here is the other way:
###
echo
declare -a ArraySparse
ArraySparse=( [1]=one [2]='' [4]='four' )
# [0]=null reference, [2]=null content, [3]=null reference

echo '- - Array-Sparse List - -'
# Within double-quotes, default IFS, Glob-Pattern

IFS=$'/x20'$'/x09'$'/x0A'
printf %q "${ArraySparse[*]}"
echo

#  Note that the output does not distinguish between "null content"
#+ and "null reference".
#  Both print as escaped whitespace.
###
#  Note also that the output does NOT contain escaped whitespace
#+ for the "null reference(s)" prior to the first defined element.
###
# This behavior of 2.04, 2.05a and 2.05b has been reported
#+ and may change in a future version of Bash.

#  To output a sparse array and maintain the [subscript]=value
#+ relationship without change requires a bit of programming.
#  One possible code fragment:
###
# local l=${#ArraySparse[@]}        # Count of defined elements
# local f=0                         # Count of found subscripts
# local i=0                         # Subscript to test
(                                   # Anonymous in-line function
    for (( l=${#ArraySparse[@]}, f = 0, i = 0 ; f < l ; i++ ))
    do
        # 'if defined then...'
        ${ArraySparse[$i]+ eval echo '/ ['$i']='${ArraySparse[$i]} ; (( f++ )) }
    done
)

# The reader coming upon the above code fragment cold
#+ might want to review "command lists" and "multiple commands on a line"
#+ in the text of the foregoing "Advanced Bash Scripting Guide."
###
#  Note:
#  The "read -a array_name" version of the "read" command
#+ begins filling array_name at subscript zero.
#  ArraySparse does not define a value at subscript zero.
###
#  The user needing to read/write a sparse array to either
#+ external storage or a communications socket must invent
#+ a read/write code pair suitable for their purpose.
###
# Exercise: clean it up.

unset ArraySparse

echo
echo '- - Conditional alternate (But not change)- -'
echo '- No alternate if null reference -'
echo -n ${VarNull+'NotSet'}' '
echo ${VarNull}
unset VarNull

echo '- No alternate if null reference -'
echo -n ${VarNull:+'NotSet'}' '
echo ${VarNull}
unset VarNull

echo '- Alternate if null contents -'
echo -n ${VarEmpty+'Empty'}' '              # Empty
echo ${VarEmpty}
VarEmpty=''

echo '- No alternate if null contents -'
echo -n ${VarEmpty:+'Empty'}' '             # Space only
echo ${VarEmpty}
VarEmpty=''

echo '- Alternate if already has contents -'

# Alternate literal
echo -n ${VarSomething+'Content'}' '        # Content Literal
echo ${VarSomething}

# Invoke function
echo -n ${VarSomething:+ $(_simple) }' '    # SimpleFunc Literal
echo ${VarSomething}
echo

echo '- - Sparse Array - -'
echo ${ArrayVar[@]+'Empty'}                 # An array of 'Empty'(ies)
echo

echo '- - Test 2 for undefined - -'

declare -i t
_incT() {
    t=$t+1
}

#  Note:
#  This is the same test used in the sparse array
#+ listing code fragment.

# Null reference, set: t == -1
t=${#VarNull}-1                     # Results in minus-one.
${VarNull+ _incT }                  # Does not execute.
echo $t' Null reference'

# Null contents, set: t == 0
t=${#VarEmpty}-1                    # Results in minus-one.
${VarEmpty+ _incT }                 # Executes.
echo $t'  Null content'

# Contents, set: t == (number of non-null characters)
t=${#VarSomething}-1                # non-null length minus-one
${VarSomething+ _incT }             # Executes.
echo $t'  Contents'

# Exercise: clean up that example.
unset t
unset _incT

# ${name?err_msg} ${name:?err_msg}
#  These follow the same rules but always exit afterwards
#+ if an action is specified following the question mark.
#  The action following the question mark may be a literal
#+ or a function result.
###
#  ${name?} ${name:?} are test-only, the return can be tested.

 


# Element operations
# ------------------

echo
echo '- - Trailing sub-element selection - -'

#  Strings, Arrays and Positional parameters

#  Call this script with multiple arguments
#+ to see the parameter selections.

echo '- All -'
echo ${VarSomething:0}              # all non-null characters
echo ${ArrayVar[@]:0}               # all elements with content
echo ${@:0}                         # all parameters with content;
                                    # ignoring parameter[0]

echo
echo '- All after -'
echo ${VarSomething:1}              # all non-null after character[0]
echo ${ArrayVar[@]:1}               # all after element[0] with content
echo ${@:2}                         # all after param[1] with content

echo
echo '- Range after -'
echo ${VarSomething:4:3}            # ral
                                    # Three characters after
                                    # character[3]

echo '- Sparse array gotch -'
echo ${ArrayVar[@]:1:2}     #  four - The only element with content.
                            #  Two elements after (if that many exist).
                            #  the FIRST WITH CONTENTS
                            #+ (the FIRST WITH  CONTENTS is being
                            #+ considered as if it
                            #+ were subscript zero).
#  Executed as if Bash considers ONLY array elements with CONTENT
#  printf %q "${ArrayVar[@]:0:3}"    # Try this one

#  In versions 2.04, 2.05a and 2.05b,
#+ Bash does not handle sparse arrays as expected using this notation.
#
#  The current Bash maintainer, Chet Ramey, has corrected this
#+ for an upcoming version of Bash.


echo '- Non-sparse array -'
echo ${@:2:2}               # Two parameters following parameter[1]

# New victims for string vector examples:
stringZ=abcABC123ABCabc
arrayZ=( abcabc ABCABC 123123 ABCABC abcabc )
sparseZ=( [1]='abcabc' [3]='ABCABC' [4]='' [5]='123123' )

echo
echo ' - - Victim string - -'$stringZ'- - '
echo ' - - Victim array - -'${arrayZ[@]}'- - '
echo ' - - Sparse array - -'${sparseZ[@]}'- - '
echo ' - [0]==null ref, [2]==null ref, [4]==null content - '
echo ' - [1]=abcabc [3]=ABCABC [5]=123123 - '
echo ' - non-null-reference count: '${#sparseZ[@]}' elements'

echo
echo '- - Prefix sub-element removal - -'
echo '- - Glob-Pattern match must include the first character. - -'
echo '- - Glob-Pattern may be a literal or a function result. - -'
echo


# Function returning a simple, Literal, Glob-Pattern
_abc() {
    echo -n 'abc'
}

echo '- Shortest prefix -'
echo ${stringZ#123}                 # Unchanged (not a prefix).
echo ${stringZ#$(_abc)}             # ABC123ABCabc
echo ${arrayZ[@]#abc}               # Applied to each element.

# Fixed by Chet Ramey for an upcoming version of Bash.
# echo ${sparseZ[@]#abc}            # Version-2.05b core dumps.

# The -it would be nice- First-Subscript-Of
# echo ${#sparseZ[@]#*}             # This is NOT valid Bash.

echo
echo '- Longest prefix -'
echo ${stringZ##1*3}                # Unchanged (not a prefix)
echo ${stringZ##a*C}                # abc
echo ${arrayZ[@]##a*c}              # ABCABC 123123 ABCABC

# Fixed by Chet Ramey for an upcoming version of Bash
# echo ${sparseZ[@]##a*c}           # Version-2.05b core dumps.

echo
echo '- - Suffix sub-element removal - -'
echo '- - Glob-Pattern match must include the last character. - -'
echo '- - Glob-Pattern may be a literal or a function result. - -'
echo
echo '- Shortest suffix -'
echo ${stringZ%1*3}                 # Unchanged (not a suffix).
echo ${stringZ%$(_abc)}             # abcABC123ABC
echo ${arrayZ[@]%abc}               # Applied to each element.

# Fixed by Chet Ramey for an upcoming version of Bash.
# echo ${sparseZ[@]%abc}            # Version-2.05b core dumps.

# The -it would be nice- Last-Subscript-Of
# echo ${#sparseZ[@]%*}             # This is NOT valid Bash.

echo
echo '- Longest suffix -'
echo ${stringZ%%1*3}                # Unchanged (not a suffix)
echo ${stringZ%%b*c}                # a
echo ${arrayZ[@]%%b*c}              # a ABCABC 123123 ABCABC a

# Fixed by Chet Ramey for an upcoming version of Bash.
# echo ${sparseZ[@]%%b*c}           # Version-2.05b core dumps.

echo
echo '- - Sub-element replacement - -'
echo '- - Sub-element at any location in string. - -'
echo '- - First specification is a Glob-Pattern - -'
echo '- - Glob-Pattern may be a literal or Glob-Pattern function result. - -'
echo '- - Second specification may be a literal or function result. - -'
echo '- - Second specification may be unspecified. Pronounce that'
echo '    as: Replace-With-Nothing (Delete) - -'
echo

 

# Function returning a simple, Literal, Glob-Pattern
_123() {
    echo -n '123'
}

echo '- Replace first occurrence -'
echo ${stringZ/$(_123)/999}         # Changed (123 is a component).
echo ${stringZ/ABC/xyz}             # xyzABC123ABCabc
echo ${arrayZ[@]/ABC/xyz}           # Applied to each element.
echo ${sparseZ[@]/ABC/xyz}          # Works as expected.

echo
echo '- Delete first occurrence -'
echo ${stringZ/$(_123)/}
echo ${stringZ/ABC/}
echo ${arrayZ[@]/ABC/}
echo ${sparseZ[@]/ABC/}

#  The replacement need not be a literal,
#+ since the result of a function invocation is allowed.
#  This is general to all forms of replacement.
echo
echo '- Replace first occurrence with Result-Of -'
echo ${stringZ/$(_123)/$(_simple)}  # Works as expected.
echo ${arrayZ[@]/ca/$(_simple)}     # Applied to each element.
echo ${sparseZ[@]/ca/$(_simple)}    # Works as expected.

echo
echo '- Replace all occurrences -'
echo ${stringZ//[b2]/X}             # X-out b's and 2's
echo ${stringZ//abc/xyz}            # xyzABC123ABCxyz
echo ${arrayZ[@]//abc/xyz}          # Applied to each element.
echo ${sparseZ[@]//abc/xyz}         # Works as expected.

echo
echo '- Delete all occurrences -'
echo ${stringZ//[b2]/}
echo ${stringZ//abc/}
echo ${arrayZ[@]//abc/}
echo ${sparseZ[@]//abc/}

echo
echo '- - Prefix sub-element replacement - -'
echo '- - Match must include the first character. - -'
echo

echo '- Replace prefix occurrences -'
echo ${stringZ/#[b2]/X}             # Unchanged (neither is a prefix).
echo ${stringZ/#$(_abc)/XYZ}        # XYZABC123ABCabc
echo ${arrayZ[@]/#abc/XYZ}          # Applied to each element.
echo ${sparseZ[@]/#abc/XYZ}         # Works as expected.

echo
echo '- Delete prefix occurrences -'
echo ${stringZ/#[b2]/}
echo ${stringZ/#$(_abc)/}
echo ${arrayZ[@]/#abc/}
echo ${sparseZ[@]/#abc/}

echo
echo '- - Suffix sub-element replacement - -'
echo '- - Match must include the last character. - -'
echo

echo '- Replace suffix occurrences -'
echo ${stringZ/%[b2]/X}             # Unchanged (neither is a suffix).
echo ${stringZ/%$(_abc)/XYZ}        # abcABC123ABCXYZ
echo ${arrayZ[@]/%abc/XYZ}          # Applied to each element.
echo ${sparseZ[@]/%abc/XYZ}         # Works as expected.

echo
echo '- Delete suffix occurrences -'
echo ${stringZ/%[b2]/}
echo ${stringZ/%$(_abc)/}
echo ${arrayZ[@]/%abc/}
echo ${sparseZ[@]/%abc/}

echo
echo '- - Special cases of null Glob-Pattern - -'
echo

echo '- Prefix all -'
# null substring pattern means 'prefix'
echo ${stringZ/#/NEW}               # NEWabcABC123ABCabc
echo ${arrayZ[@]/#/NEW}             # Applied to each element.
echo ${sparseZ[@]/#/NEW}            # Applied to null-content also.
                                    # That seems reasonable.

echo
echo '- Suffix all -'
# null substring pattern means 'suffix'
echo ${stringZ/%/NEW}               # abcABC123ABCabcNEW
echo ${arrayZ[@]/%/NEW}             # Applied to each element.
echo ${sparseZ[@]/%/NEW}            # Applied to null-content also.
                                    # That seems reasonable.

echo
echo '- - Special case For-Each Glob-Pattern - -'
echo '- - - - This is a nice-to-have dream - - - -'
echo

_GenFunc() {
    echo -n ${0}                    # Illustration only.
    # Actually, that would be an arbitrary computation.
}

# All occurrences, matching the AnyThing pattern.
# Currently //*/ does not match null-content nor null-reference.
# /#/ and /%/ does match null-content but not null-reference.
echo ${sparseZ[@]//*/$(_GenFunc)}


#  A possible syntax would be to make
#+ the parameter notation used within this construct mean:
#   ${1} - The full element
#   ${2} - The prefix, if any, to the matched sub-element
#   ${3} - The matched sub-element
#   ${4} - The suffix, if any, to the matched sub-element
#
# echo ${sparseZ[@]//*/$(_GenFunc ${3})}   # Same as ${1} here.
# Perhaps it will be implemented in a future version of Bash.


exit 0
%%%&&&basics-reviewed.bash&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@behead.sh@@@!!!************************************************************************************
#! /bin/sh
# Strips off the header from a mail/News message i.e. till the first
# empty line
# Mark Moraes, University of Toronto

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

if [ $# -eq 0 ]; then
# ==> If no command line args present, then works on file redirected to stdin.
 sed -e '1,/^$/d' -e '/^[  ]*$/d'
 # --> Delete empty lines and all lines until
 # --> first one beginning with white space.
else
# ==> If command line args present, then work on files named.
 for i do
  sed -e '1,/^$/d' -e '/^[  ]*$/d' $i
  # --> Ditto, as above.
 done
fi

# ==> Exercise: Add error checking and other options.
# ==>
# ==> Note that the small sed script repeats, except for the arg passed.
# ==> Does it make sense to embed it in a function? Why or why not?
%%%&&&behead.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@bin-grep.sh@@@!!!************************************************************************************
#!/bin/bash
# bin-grep.sh: 在一个二进制文件中定位匹配字串.

# 对于二进制文件的"grep"替换.
# 与"grep -a"的效果相似

E_BADARGS=65
E_NOFILE=66

if [ $# -ne 2 ]
then
  echo "Usage: `basename $0` search_string filename"
  exit $E_BADARGS
fi

if [ ! -f "$2" ]
then
  echo "File /"$2/" does not exist."
  exit $E_NOFILE
fi 


IFS=$'/012'       # 由Paulo Marcel Coelho Aragao提出的建议.
                  # 也就是:  IFS="/n"
for word in $( strings "$2" | grep "$1" )
# "strings" 命令列出二进制文件中的所有字符串.
# 输出到管道交给"grep",然后由grep命令来过滤字符串.
do
  echo $word
done

# S.C. 指出, 行23 - 29 可以被下边的这行来代替,
#    strings "$2" | grep "$1" | tr -s "$IFS" '[/n*]'


# 试试用"./bin-grep.sh mem /bin/ls"来运行这个脚本.

exit 0
%%%&&&bin-grep.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@blank-rename.sh@@@!!!************************************************************************************
#! /bin/bash
# blank-rename.sh
#
# Substitutes underscores for blanks in all the filenames in a directory.

ONE=1                     # For getting singular/plural right (see below).
number=0                  # Keeps track of how many files actually renamed.
FOUND=0                   # Successful return value.

for filename in *         #Traverse all files in directory.
do
     echo "$filename" | grep -q " "         #  Check whether filename
     if [ $? -eq $FOUND ]                   #+ contains space(s).
     then
       fname=$filename                      # Strip off path.
       n=`echo $fname | sed -e "s/ /_/g"`   # Substitute underscore for blank.
       mv "$fname" "$n"                     # Do the actual renaming.
       let "number += 1"
     fi
done  

if [ "$number" -eq "$ONE" ]                 # For correct grammar.
then
 echo "$number file renamed."
else
 echo "$number files renamed."
fi

exit 0
%%%&&&blank-rename.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@blot-out.sh@@@!!!************************************************************************************
#!/bin/bash
# blot-out.sh: 删除一个文件"所有"的记录.

#  这个脚本会使用随机字节交替的覆盖目标文件,
#+ 并且在最终删除这个文件之前清零.
#  这么做之后, 即使你通过传统手段来检查磁盘扇区
#+ 也不能把文件原始数据重新恢复.

PASSES=7         #  破坏文件的次数.
                 #  提高这个数字会减慢脚本运行的速度,
                 #+ 尤其是对尺寸比较大的目标文件进行操作的时候.
BLOCKSIZE=1      #  带有/dev/urandom的I/O需要单位块尺寸,
                 #+ 否则你可能会获得奇怪的结果.
E_BADARGS=70     #  不同的错误退出码.
E_NOT_FOUND=71
E_CHANGED_MIND=72

if [ -z "$1" ]   # 没指定文件名.
then
  echo "Usage: `basename $0` filename"
  exit $E_BADARGS
fi

file=$1

if [ ! -e "$file" ]
then
  echo "File /"$file/" not found."
  exit $E_NOT_FOUND
fi 

echo; echo -n "Are you absolutely sure you want to blot out /"$file/" (y/n)? "
read answer
case "$answer" in
[nN]) echo "Changed your mind, huh?"
      exit $E_CHANGED_MIND
      ;;
*)    echo "Blotting out file /"$file/".";;
esac


flength=$(ls -l "$file" | awk '{print $5}')  # 5是文件长度.
pass_count=1

chmod u+w "$file"   # 允许覆盖/删除这个文件.

echo

while [ "$pass_count" -le "$PASSES" ]
do
  echo "Pass #$pass_count"
  sync         # 刷新buffers.
  dd if=/dev/urandom of=$file bs=$BLOCKSIZE count=$flength
               # 使用随机字节进行填充.
  sync         # 再次刷新buffer.
  dd if=/dev/zero of=$file bs=$BLOCKSIZE count=$flength
               # 用0填充.
  sync         # 再次刷新buffer.
  let "pass_count += 1"
  echo
done 


rm -f $file    # 最后, 删除这个已经被破坏得不成样子的文件.
sync           # 最后一次刷新buffer.

echo "File /"$file/" blotted out and deleted."; echo


exit 0

#  这是一种真正安全的删除文件的办法,
#+ 但是效率比较低, 运行比较慢.
#  GNU文件工具包中的"shred"命令,
#+ 也可以完成相同的工作, 不过更有效率.
                                                           
#  使用普通的方法是不可能重新恢复这个文件了.
#  然而 . . .
#+ 这个简单的例子是不能够抵抗
#+ 那些经验丰富并且正规的分析.
                                                           
#  这个脚本可能不会很好的运行在日志文件系统上(JFS).
#  练习 (很难): 像它做的那样修正这个问题.

 

#  Tom Vier的文件删除包可以更加彻底的删除文件,
#+ 比这个例子厉害的多.
#     http://www.ibiblio.org/pub/Linux/utils/file/wipe-2.0.0.tar.bz2

#  如果想对安全删除文件这一论题进行深入的分析,
#+ 可以参见Peter Gutmann的网页,
#+     "Secure Deletion of Data From Magnetic and Solid-State Memory".
#       http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html
%%%&&&blot-out.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@break-levels.sh@@@!!!************************************************************************************
#!/bin/bash
# break-levels.sh: 退出循环.

# "break N" 退出N层循环.

for outerloop in 1 2 3 4 5
do
  echo -n "Group $outerloop:   "

  # --------------------------------------------------------
  for innerloop in 1 2 3 4 5
  do
    echo -n "$innerloop "

    if [ "$innerloop" -eq 3 ]
    then
      break  # 试试   break 2   来看看发生什么事.
             # (内部循环和外部循环都被"Break"了. )
    fi
  done
  # --------------------------------------------------------

  echo
done 

echo

exit 0
%%%&&&break-levels.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@broken-link.sh@@@!!!************************************************************************************
#!/bin/bash
# broken-link.sh
# 由Lee bigelow所编写 &lt;ligelowbee@yahoo.com&gt;
# 已经争得作者的授权引用在本书中.

#一个纯粹的shell脚本用来找出那些断掉的符号链接文件并且输出它们所指向的文件.
#以便于它们可以把输出提供给xargs来进行处理 :)
#比如. broken-link.sh /somedir /someotherdir|xargs rm
#
#下边的方法, 不管怎么说, 都是一种更好的办法:
#
#find "somedir" -type l -print0|/
#xargs -r0 file|/
#grep "broken symbolic"|
#sed -e 's/^/|: *broken symbolic.*$/"/g'
#
#但这不是一个纯粹的bash脚本, 最起码现在不是.
#注意: 谨防在/proc文件系统和任何死循环链接中使用!
##############################################################


#如果没有参数被传递到脚本中, 那么就使用
#当前目录. 否则就是用传递进来的参数作为目录
#来搜索.
####################
[ $# -eq 0 ] && directorys=`pwd` || directorys=$@

#编写函数linkchk用来检查传递进来的目录或文件是否是链接,
#并判断这些文件或目录是否存在. 然后打印它们所指向的文件.
#如果传递进来的元素包含子目录,
#那么把子目录也放到linkcheck函数中处理, 这样就达到了递归的目的.
##########
linkchk () {
    for element in $1/*; do
    [ -h "$element" -a ! -e "$element" ] && echo /"$element/"
    [ -d "$element" ] && linkchk $element
    # 当然, '-h'用来测试符号链接, '-d'用来测试目录.
    done
}

#把每个传递到脚本的参数都送到linkchk函数中进行处理,
#检查是否有可用目录. 如果没有, 那么就打印错误消息和
#使用信息.
################
for directory in $directorys; do
    if [ -d $directory ]
 then linkchk $directory
 else
     echo "$directory is not a directory"
     echo "Usage: $0 dir1 dir2 ..."
    fi
done

exit 0
%%%&&&broken-link.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@bubble.sh@@@!!!************************************************************************************
#!/bin/bash
# bubble.sh: 一种排序方式, 冒泡排序.

# 回忆一下冒泡排序的算法. 我们在这里要实现它...

#  依靠连续的比较数组元素进行排序,
#+ 比较两个相邻元素, 如果顺序不对, 就交换这两个元素的位置.
#  当第一轮比较结束之后, 最"重"的元素就会被移动到最底部.
#  当第二轮比较结束之后, 第二"重"的元素就会被移动到次底部的位置.
#  依此类推.
#  这意味着每轮比较不需要比较之前已经"沉淀"好的数据.
#  因此你会注意到后边的数据在打印的时候会快一些.


exchange()
{
  # 交换数组中的两个元素.
  local temp=${Countries[$1]} #  临时保存
                              #+ 要交换的那个元素.
  Countries[$1]=${Countries[$2]}
  Countries[$2]=$temp
 
  return

declare -a Countries  #  声明数组,
                      #+ 此处是可选的, 因为数组在下面被初始化.

#  我们是否可以使用转义符(/)
#+ 来将数组元素的值放在不同的行上?
#  可以.

Countries=(Netherlands Ukraine Zaire Turkey Russia Yemen Syria /
Brazil Argentina Nicaragua Japan Mexico Venezuela Greece England /
Israel Peru Canada Oman Denmark Wales France Kenya /
Xanadu Qatar Liechtenstein Hungary)

# "Xanadu"虚拟出来的世外桃源.
#+


clear                      # 开始之前的清屏动作.

echo "0: ${Countries[*]}"  # 从索引0开始列出整个数组.

number_of_elements=${#Countries[@]}
let "comparisons = $number_of_elements - 1"

count=1 # 传递数字.

while [ "$comparisons" -gt 0 ]          # 开始外部循环
do

  index=0  # 在每轮循环开始之前, 重置索引.

  while [ "$index" -lt "$comparisons" ] # 开始内部循环
  do
    if [ ${Countries[$index]} /> ${Countries[`expr $index + 1`]} ]
    #  如果原来的排序次序不对...
    #  回想一下, 在单括号中,
    #+ />是ASCII码的比较操作符.

    #  if [[ ${Countries[$index]} > ${Countries[`expr $index + 1`]} ]]
    #+ 这样也行.
    then
      exchange $index `expr $index + 1`  # 交换.
    fi 
    let "index += 1"  # 或者,   index+=1   在Bash 3.1之后的版本才能这么用.
  done # 内部循环结束

# ----------------------------------------------------------------------
# Paulo Marcel Coelho Aragao建议我们可以使用更简单的for循环.
#
# for (( last = $number_of_elements - 1 ; last > 1 ; last-- ))
# do
#     for (( i = 0 ; i < last ; i++ ))
#     do
#         [[ "${Countries[$i]}" > "${Countries[$((i+1))]}" ]] /
#             && exchange $i $((i+1))
#     done
# done
# ----------------------------------------------------------------------
 

let "comparisons -= 1" #  因为最"重"的元素到了底部,
                       #+ 所以每轮我们可以少做一次比较.

echo
echo "$count: ${Countries[@]}"  # 每轮结束后, 都打印一次数组.
echo
let "count += 1"                # 增加传递计数.

done                            # 外部循环结束
                                # 至此, 全部完成.

exit 0
%%%&&&bubble.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@cannon.sh@@@!!!************************************************************************************
#!/bin/bash
# cannon.sh: Approximating PI by firing cannonballs.

# 这事实上是一个"Monte Carlo"蒙特卡洛模拟的非常简单的实例:
#+ 蒙特卡洛模拟是一种由现实事件抽象出来的数学模型,
#+ 由于要使用随机抽样统计来估算数学函数, 所以使用伪随机数来模拟真正的随机数.

#  想象有一个完美的正方形土地, 边长为10000个单位.
#  在这块土地的中间有一个完美的圆形湖,
#+ 这个湖的直径是10000个单位.
#  这块土地的绝大多数面积都是水, 当然只有4个角上有一些土地.
#  (可以把这个湖想象成为这个正方形的内接圆.)
#
#  我们将使用老式的大炮和铁炮弹
#+ 向这块正方形的土地上开炮.
#  所有的炮弹都会击中这块正方形土地的某个地方.
#+ 或者是打到湖上, 或者是打到4个角的土地上.
#  因为这个湖占据了这个区域大部分地方,
#+ 所以大部分的炮弹都会"扑通"一声落到水里.
#  而只有很少的炮弹会"砰"的一声落到4个
#+ 角的土地上.
#
#  如果我们发出的炮弹足够随机的落到这块正方形区域中的话,
#+ 那么落到水里的炮弹与打出炮弹总数的比率,
#+ 大概非常接近于PI/4.
#
#  原因是所有的炮弹事实上都
#+ 打在了这个土地的右上角,
#+ 也就是, 笛卡尔坐标系的第一象限.
#  (之前的解释只是一个简化.)
#
#  理论上来说, 如果打出的炮弹越多, 就越接近这个数字.
#  然而, 对于shell 脚本来说一定会做些让步的,
#+ 因为它肯定不能和那些内建就支持浮点运算的编译语言相比.
#  当然就会降低精度.


DIMENSION=10000  # 这块土地的边长.
                 # 这也是所产生随机整数的上限.
                                                                        
MAXSHOTS=1000    # 开炮次数.
                 # 10000或更多次的话, 效果应该更好, 但有点太浪费时间了.
PMULTIPLIER=4.0  # 接近于PI的比例因子.

get_random ()
{
SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }')
RANDOM=$SEED                                  #  来自于"seeding-random.sh"
                                              #+ 的例子脚本.
let "rnum = $RANDOM % $DIMENSION"             #  范围小于10000.
echo $rnum
}

distance=        # 声明全局变量.
hypotenuse ()    # 从"alt-bc.sh"例子来的,
{                # 计算直角三角形的斜边的函数.
distance=$(bc -l << EOF
scale = 0
sqrt ( $1 * $1 + $2 * $2 )
EOF
)
#  设置 "scale" 为 0 , 好让结果四舍五入为整数值,
#+ 这也是这个脚本中必须折中的一个地方.
#  不幸的是, 这将降低模拟的精度.
}


# main() {

# 初始化变量.
shots=0
splashes=0
thuds=0
Pi=0

while [ "$shots" -lt  "$MAXSHOTS" ]           # 主循环.
do                                                                     
                                                                       
  xCoord=$(get_random)                        # 取得随机的 X 与 Y 坐标.
  yCoord=$(get_random)                                                 
  hypotenuse $xCoord $yCoord                  #  直角三角形斜边 =
                                              #+ distance.
  ((shots++))                                                          
                                                                       
  printf "#%4d   " $shots                                              
  printf "Xc = %4d  " $xCoord                                          
  printf "Yc = %4d  " $yCoord                                          
  printf "Distance = %5d  " $distance         #  到湖中心的
                                              #+ 距离 --
                                              #  起始坐标点 --
                                              #+  (0,0).

  if [ "$distance" -le "$DIMENSION" ]
  then
    echo -n "SPLASH!  "
    ((splashes++))
  else
    echo -n "THUD!    "
    ((thuds++))
  fi

  Pi=$(echo "scale=9; $PMULTIPLIER*$splashes/$shots" | bc)
  # 将比例乘以4.0.
  echo -n "PI ~ $Pi"
  echo

done

echo
echo "After $shots shots, PI looks like approximately $Pi."
# 如果不太准的话, 那么就提高一下运行的次数. . .
# 可能是由于运行错误和随机数随机程度不高造成的.
echo

# }

exit 0

#  要想知道一个shell脚本到底适不适合对计算应用进行模拟的话?
#+ (一种需要对复杂度和精度都有要求的计算应用).
#
#  一般至少需要两个判断条件.
#  1) 作为一种概念的验证: 来显示它可以做到.
#  2) 在使用真正的编译语言来实现一个算法之前,
#+    使用脚本来测试和验证这个算法.
%%%&&&cannon.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@case-cmd.sh@@@!!!************************************************************************************
#!/bin/bash
# case-cmd.sh: 使用命令替换来产生"case"变量.

case $( arch ) in   # "arch" 返回机器体系的类型.
                    # 等价于 'uname -m' ...
i386 ) echo "80386-based machine";;
i486 ) echo "80486-based machine";;
i586 ) echo "Pentium-based machine";;
i686 ) echo "Pentium2+-based machine";;
*    ) echo "Other type of machine";;
esac

exit 0
%%%&&&case-cmd.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

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

#  The notorious "hailstone" or Collatz series.
#  -------------------------------------------
#  1) Get the integer "seed" from the command line.
#  2) NUMBER &lt;--- seed
#  3) Print NUMBER.
#  4)  If NUMBER is even, divide by 2, or
#  5)+ if odd, multiply by 3 and add 1.
#  6) NUMBER &lt;--- result
#  7) Loop back to step 3 (for specified number of iterations).
#
#  The theory is that every sequence,
#+ no matter how large the initial value,
#+ eventually settles down to repeating "4,2,1..." cycles,
#+ even after fluctuating through a wide range of values.
#
#  This is an instance of an "iterate",
#+ an operation that feeds its output back into the input.
#  Sometimes the result is a "chaotic" series.


MAX_ITERATIONS=200
# For large seed numbers (&gt;32000), increase MAX_ITERATIONS.

h=${1:-$$}                      #  Seed
                                #  Use $PID as seed,
                                #+ if not specified as command-line arg.

echo
echo "C($h) --- $MAX_ITERATIONS Iterations"
echo

for ((i=1; i<=MAX_ITERATIONS; i++))
do

echo -n "$h "
#          ^^^^^
#           tab

  let "remainder = h % 2"
  if [ "$remainder" -eq 0 ]   # Even?
  then
    let "h /= 2"              # Divide by 2.
  else
    let "h = h*3 + 1"         # Multiply by 3 and add 1.
  fi


COLUMNS=10                    # Output 10 values per line.
let "line_break = i % $COLUMNS"
if [ "$line_break" -eq 0 ]
then
  echo
fi 

done

echo

#  For more information on this mathematical function,
#+ see "Computers, Pattern, Chaos, and Beauty", by Pickover, p. 185 ff.,
#+ as listed in the bibliography.

exit 0
%%%&&&collatz.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@colm.sh@@@!!!************************************************************************************
#!/bin/bash
# 这是"column" man页中的一个例子, 作者对这个例子做了很小的修改.


(printf "PERMISSIONS LINKS OWNER GROUP SIZE MONTH DAY HH:MM PROG-NAME/n" /
; ls -l | sed 1d) | column -t

#  管道中的"sed 1d"删除输出的第一行,
#+ 第一行将是"total        N",
#+ 其中"N"是"ls -l"找到的文件总数.
                                                  
# "column"中的-t选项用来转化为易于打印的表形式.

exit 0
%%%&&&colm.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


***@@@color-echo.sh@@@!!!************************************************************************************
#!/bin/bash
# color-echo.sh: 使用颜色来显示文本消息.

# 可以按照你自己的目的来修改这个脚本.
# 这比将颜色数值写死更容易.

black='/E[30;47m'
red='/E[31;47m'
green='/E[32;47m'
yellow='/E[33;47m'
blue='/E[34;47m'
magenta='/E[35;47m'
cyan='/E[36;47m'
white='/E[37;47m'


alias Reset="tput sgr0"      #  不用清屏,
                             #+ 将文本属性重置为正常情况.


cecho ()                     # Color-echo.
                             # 参数$1 = 要显示的信息
                             # 参数$2 = 颜色
{
local default_msg="No message passed."
                             # 其实并不一定非的是局部变量.

message=${1:-$default_msg}   # 默认为default_msg.
color=${2:-$black}           # 如果没有指定, 默认为黑色.

  echo -e "$color"
  echo "$message"
  Reset                      # 重置文本属性.

  return


# 现在, 让我们试一下.
# ----------------------------------------------------
cecho "Feeling blue..." $blue
cecho "Magenta looks more like purple." $magenta
cecho "Green with envy." $green
cecho "Seeing red?" $red
cecho "Cyan, more familiarly known as aqua." $cyan
cecho "No color passed (defaults to black)."
       # 缺少$color参数.
cecho "/"Empty/" color passed (defaults to black)." ""
       # 空的$color参数.
cecho
       # 缺少$message和$color参数.
cecho "" ""
       # 空的$message和$color参数.
# ----------------------------------------------------

echo

exit 0

# 练习:
# -----
# 1) 为'cecho ()'函数添加"粗体"属性.
# 2) 为彩色背景添加选项.
%%%&&&color-echo.sh&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值