***@@@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 <tengel@fluid.com>
# 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 <space> System CPU
# User CPU of dead children <space> 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<> myfile.txt
while read line <&3
do {
echo "$line"
(( Lines++ )); # 增加这个变量的值
#+ 现在外部循环就可以访问了.
# 没有子shell, 现在就没问题了.
}
done
exec 3>&-
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所编写 <ligelowbee@yahoo.com>
# 已经争得作者的授权引用在本书中.
#一个纯粹的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 <--- seed
# 3) Print NUMBER.
# 4) If NUMBER is even, divide by 2, or
# 5)+ if odd, multiply by 3 and add 1.
# 6) NUMBER <--- 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 (>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&&&%%%>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>